How to create bootable ISO from command line? I noticed MSMG creates bootable ISO without any 3rd party software (like WinIso or UltraIso). Also UUP Dump and ESD Decrypter seems to utilize the same technique. How can I create bootable ISO image from given folder? For instance I only convert WIM to ESD and 'd like to have nice iso file Many thanks in advance!
MakeISO.bat Code: edit: code moved few posts below to avoid duplicates; also added stand-alone MakeISO.reg to do it from folder context menu
He asked for something to create iso from commandline, preferably without external tools. The tiny script I provided answer that exactly. There's no learning curve.
Does any of the mentioned example tools use the method you published? Just to say, when someone gave me your method when i started to learn, i would never have found out how to use it for future projects. ps, not picking a fight
It's a batch script, you use it just like an exe. It's even easier to use than cdimage with those horrible parameters. Can even copy-paste the function in powershell without creating a script on disk, then immediately call it the same way: MakeISO "path-to-folder" "file.iso" "optional-label" Maybe the hybrid aspect was what confused you? My personal usage: I copy a slightly changed MakeISO.bat next to the target-folder, then rename the script as target-folder.bat and it makes me target-folder.iso next to it. So my work partition is filled with 10_1507, 10_1511, .. 10_21H2, 11_Rel, 11_22579, .. 11_22610 folders having the extracted iso contents, and for each of them I have a corresponding name.bat and resulting name.iso MakeISO.bat Code: @echo off for %%a in (%2) do call :MakeISO %* & exit /b if exist "%~dp0%~n0\*.*" call :MakeISO "%~dp0%~n0" "%~dp0%~n0.iso" "%~n0" & exit /b echo;[USAGE] echo;MakeISO "directory" "file.iso" "optional-label" echo;or echo;Place next to "target-folder", rename script as "target-folder.bat", and click to create "target-folder.iso" pause exit /b 1 #:MakeISO:# [PARAMS] "directory" "file.iso" "optional-label" set ^ #=;$f0=[io.file]::ReadAllText($env:0); $0=($f0-split '#\:MakeISO\:' ,3)[1]; $1=$env:1-replace'([`@$])','`$1'; iex($0+$1) set ^ #=& set "0=%~f0"& set 1=;MakeISO %*& powershell -nop -c "%#%"& exit /b %errorcode% function MakeISO ($dir,$iso,$label='DVD_ROM') {if (!(test-path -Path $dir -pathtype Container)) {"[ERR] $dir"; return 1}; $code=@" using System; using System.IO; using System.Runtime.Interop`Services; using System.Runtime.Interop`Services.ComTypes; public class dir2iso {public int AveYo=2021; [Dll`Import("shlwapi",CharSet=CharSet.Unicode,PreserveSig=false)] internal static extern void SHCreateStreamOnFileEx(string f,uint m,uint d,bool b,IStream r,out IStream s); public static int Create(string file, ref object obj, int bs, int tb) { IStream dir=(IStream)obj, iso; try {SHCreateStreamOnFileEx(file,0x1001,0x80,true,null,out iso);} catch(Exception e) {Console.WriteLine(e.Message); return 1;} int d=tb>1024 ? 1024 : 1, pad=tb%d, block=bs*d, total=(tb-pad)/d, c=total>100 ? total/100 : total, i=1, MB=(bs/1024)*tb/1024; Console.Write("... {0}MB ",MB); if (pad > 0) dir.CopyTo(iso, pad * block, Int`Ptr.Zero, Int`Ptr.Zero); while (total-- > 0) {dir.CopyTo(iso, block, Int`Ptr.Zero, Int`Ptr.Zero); if (total % c == 0) {Console.Write("\r{0,2}%",i++);}} iso.Commit(0); Console.WriteLine("\r{0,2}% {1}MB ",100,MB); return 0;} } // ` used to silence ps eventlog "@; "MakeISO $iso"; & { $cs = new-object CodeDom.Compiler.CompilerParameters; $cs.GenerateInMemory = 1 $compile = (new-object Microsoft.CSharp.CSharpCodeProvider).CompileAssemblyFromSource($cs, $code) $BOOT = @(); $bootable = 0; $mbr_efi = @(0,0xEF); $images = @('boot\etfsboot.com','efi\microsoft\boot\efisys.bin') #:: _noprompt 0,1|% { $bootimage = join-path $dir -child $images[$_]; if (test-path -Path $bootimage -pathtype Leaf) { $bin = new-object -ComObject ADODB.Stream; $bin.Open(); $bin.Type = 1; $bin.LoadFromFile($bootimage) $opt = new-object -ComObject IMAPI2FS.BootOptions;$opt.AssignBootImage($bin.psobject.BaseObject); $opt.PlatformId = $mbr_efi[$_] $opt.Emulation = 0; $bootable = 1; $opt.Manufacturer = 'Microsoft'; $BOOT += $opt.psobject.BaseObject } } $fsi = new-object -ComObject IMAPI2FS.MsftFileSystemImage; $fsi.FileSystemsToCreate = 4; $fsi.FreeMediaBlocks = 0 if ($bootable) {$fsi.BootImageOptionsArray = $BOOT}; $TREE = $fsi.Root; $TREE.AddTree($dir,$false); $fsi.VolumeName = $label $obj = $fsi.CreateResultImage(); $ret = [dir2iso]::Create($iso,[ref]$obj.ImageStream,$obj.BlockSize,$obj.TotalBlocks) } [GC]::Collect(); return $ret # export directory as (bootable) udf iso - lean and mean snippet by AveYo, 2022.05.04 } #:MakeISO:# And I now have turned it into a stand-alone reg file, to use MakeISO directly from Folder context menu! MakeISO.reg Code: Windows Registry Editor Version 5.00 ; Directory context menu for MakeISO - lean and mean snippet by AveYo, 2022.05.04 [-HKEY_CLASSES_ROOT\MakeISO] [-HKEY_CLASSES_ROOT\Directory\shell\extract] ; To remove entries, copy paste above into undo_MakeISO.reg file, then import it ; MakeISO on Folder [HKEY_CLASSES_ROOT\Directory\shell\extract] "MuiVerb"="MakeISO" "Icon"="imageres.dll,-5205" "MultiSelectModel"="Single" "AppliesTo"="NOT (System.Kind:~Library) AND System.Kind:~Folder" [HKEY_CLASSES_ROOT\Directory\shell\extract\command] @="C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe -nop -c iex((10..31|%%{(gp 'Registry::HKCR\\MakeISO' $_ -ea 0).$_})-join[char]10); # --%% \"%L\"" ; MakeISO function [HKEY_CLASSES_ROOT\MakeISO] "10"="function MakeISO ($dir,$iso,$label='DVD_ROM') {if (!(test-path -Path $dir -pathtype Container)) {\"[ERR] $dir\"; return 1}; $code=@\"" "11"=" using System; using System.IO; using System.Runtime.Interop`Services; using System.Runtime.Interop`Services.ComTypes;" "12"=" public class dir2iso {public int AveYo=2021; [Dll`Import(\"shlwapi\",CharSet=CharSet.Unicode,PreserveSig=false)]" "13"=" internal static extern void SHCreateStreamOnFileEx(string f,uint m,uint d,bool b,IStream r,out IStream s);" "14"=" public static int Create(string file, ref object obj, int bs, int tb) { IStream dir=(IStream)obj, iso;" "15"=" try {SHCreateStreamOnFileEx(file,0x1001,0x80,true,null,out iso);} catch(Exception e) {Console.WriteLine(e.Message); return 1;}" "16"=" int d=tb>1024 ? 1024 : 1, pad=tb%d, block=bs*d, total=(tb-pad)/d, c=total>100 ? total/100 : total, i=1, MB=(bs/1024)*tb/1024;" "17"=" Console.Write(\"... {0}MB \",MB); if (pad > 0) dir.CopyTo(iso, pad * block, Int`Ptr.Zero, Int`Ptr.Zero);" "18"=" while (total-- > 0) {dir.CopyTo(iso, block, Int`Ptr.Zero, Int`Ptr.Zero); if (total % c == 0) {Console.Write(\"\\r{0,2}%\",i++);}}" "19"=" iso.Commit(0); Console.WriteLine(\"\\r{0,2}% {1}MB \",100,MB); return 0;} } // ` used to silence ps eventlog" "20"="\"@; \"MakeISO $dir\"; & { $cs = new-object CodeDom.Compiler.CompilerParameters; $cs.GenerateInMemory = 1" "21"=" $compile = (new-object Microsoft.CSharp.CSharpCodeProvider).CompileAssemblyFromSource($cs, $code)" "22"=" $BOOT = @(); $bootable = 0; $mbr_efi = @(0,0xEF); $images = @('boot\\etfsboot.com','efi\\microsoft\\boot\\efisys.bin') #:: _noprompt" "23"=" 0,1|% { $bootimage = join-path $dir -child $images[$_]; if (test-path -Path $bootimage -pathtype Leaf) {" "24"=" $bin = new-object -ComObject ADODB.Stream; $bin.Open(); $bin.Type = 1; $bin.LoadFromFile($bootimage)" "25"=" $opt = new-object -ComObject IMAPI2FS.BootOptions;$opt.AssignBootImage($bin.psobject.BaseObject); $opt.PlatformId = $mbr_efi[$_]" "26"=" $opt.Emulation = 0; $bootable = 1; $opt.Manufacturer = 'Microsoft'; $BOOT += $opt.psobject.BaseObject } }" "27"=" $fsi = new-object -ComObject IMAPI2FS.MsftFileSystemImage; $fsi.FileSystemsToCreate = 4; $fsi.FreeMediaBlocks = 0" "28"=" if ($bootable) {$fsi.BootImageOptionsArray = $BOOT}; $TREE = $fsi.Root; $TREE.AddTree($dir,$false); $fsi.VolumeName = $label" "29"=" $obj = $fsi.CreateResultImage(); $ret = [dir2iso]::Create($iso,[ref]$obj.ImageStream,$obj.BlockSize,$obj.TotalBlocks) }" "30"=" [GC]::Collect(); return $ret # export directory as (bootable) udf iso - lean and mean snippet by AveYo, 2022.05.04" "31"="}; $A=([environment]::commandline-split'-[-]%+ ?',2)[1].Trim(' \"'); $L=split-path -leaf $A; MakeISO $A \"$L.iso\" $L"
I see it uses the API. Does the API have support for supplying the time parameters? It's the only essential thing missing.
What time parameters specifically? And why essential when any file compare worth 2c supports ignoring timestamps? API can set time per items (with some limitation in UDF mode).
The one that set (unify) timestamp for all files in the iso i guess it's a feature to oscdimg itself, not the API Code: -t04/05/2022,07:28:58
Referring to the ISO time inside. Set by cdimage.exe with the -t parameter. Using the script sets it to current time ATM. Specific issue with the context menu version (registry file): The ISO label cannot be specified when using the context menu. From your other scripts, I know that you have the power of asking for the label at runtime.
Should be doable, either with a 2nd pass over the created stream or by adding files and folders individually instead of using AddTree to add them in bulk. There's no native interface to preset it. And UDF has some quirks with CreationTime, but the script does keep LastModifiedTime not current time!
cdimage and oscdimg are essentially the same tool. The former is a Microsoft internal tool that has leaked in multiple versions (but not in the latest 2.56). The latter is the official version from the ADK. Apart from a few messages and embedded names, there are not many differences. "CDIMAGE" vs. "OSCDIMG" "For Microsoft internal use only." vs. "Licensed only for producing Microsoft authorized content." The batch files from @BAU do not use either tool, but the IMAPI2 functionality built into every even halfway modern Windows.