How to create bootable ISO from command line?

Discussion in 'Windows 10' started by exslim, May 3, 2022.

  1. exslim

    exslim MDL Novice

    Mar 10, 2022
    6
    5
    0
    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!
     
  2. Enthousiast

    Enthousiast MDL Tester

    Oct 30, 2009
    40,945
    73,235
    450
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  3. AveYo

    AveYo MDL Expert

    Feb 10, 2009
    1,693
    4,936
    60
    #3 AveYo, May 3, 2022
    Last edited: May 4, 2022
    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 :p
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  4. Enthousiast

    Enthousiast MDL Tester

    Oct 30, 2009
    40,945
    73,235
    450
    How will he now learn to use the commandline to achieve his goal?
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  5. exslim

    exslim MDL Novice

    Mar 10, 2022
    6
    5
    0
    Pardon my English. I highly appreciate both helps.
     
  6. AveYo

    AveYo MDL Expert

    Feb 10, 2009
    1,693
    4,936
    60
    :confused:
    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.
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  7. Enthousiast

    Enthousiast MDL Tester

    Oct 30, 2009
    40,945
    73,235
    450
    #7 Enthousiast, May 3, 2022
    Last edited: May 3, 2022
    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:)
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  8. AveYo

    AveYo MDL Expert

    Feb 10, 2009
    1,693
    4,936
    60
    #8 AveYo, May 4, 2022
    Last edited: May 4, 2022
    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"
    
    
     

    Attached Files:

    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  9. Carlos Detweiller

    Carlos Detweiller Emperor of Ice-Cream

    Dec 21, 2012
    5,329
    5,692
    180
    I see it uses the API. Does the API have support for supplying the time parameters? It's the only essential thing missing.
     
  10. AveYo

    AveYo MDL Expert

    Feb 10, 2009
    1,693
    4,936
    60
    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).
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  11. abbodi1406

    abbodi1406 MDL KB0000001

    Feb 19, 2011
    13,596
    67,836
    340
    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
     
  12. Carlos Detweiller

    Carlos Detweiller Emperor of Ice-Cream

    Dec 21, 2012
    5,329
    5,692
    180
    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.
     
  13. AveYo

    AveYo MDL Expert

    Feb 10, 2009
    1,693
    4,936
    60
    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!
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  14. Carlos Detweiller

    Carlos Detweiller Emperor of Ice-Cream

    Dec 21, 2012
    5,329
    5,692
    180
    Example what I'm referring to (timestamp):

    timestamp.jpg
     
  15. Magmarock

    Magmarock MDL Member

    Oct 12, 2016
    117
    17
    10
    commandline not sure if I love it or hate it.
     
  16. AveYo

    AveYo MDL Expert

    Feb 10, 2009
    1,693
    4,936
    60
    Is that a bump? Because I haven't had time yet to implement MDL Spinning Tortoise 's request.
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  17. Enthousiast

    Enthousiast MDL Tester

    Oct 30, 2009
    40,945
    73,235
    450
    Just read his other recent posts;):D
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  18. AveYo

    AveYo MDL Expert

    Feb 10, 2009
    1,693
    4,936
    60
    :cool:
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  19. Magmarock

    Magmarock MDL Member

    Oct 12, 2016
    117
    17
    10
    So why do people prefer cdimage.exe over oscdimg.exe? Is it because you don't need to install ADK?
     
  20. Carlos Detweiller

    Carlos Detweiller Emperor of Ice-Cream

    Dec 21, 2012
    5,329
    5,692
    180
    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.