Classes/ISOHelper.cs

// custom class declaration (C#); contains the Create method for writing an ISO file to disk.
public class ISOHelper
{
    // Writes an ISO file to disk from an IMAPI2 image stream.

    // IMAPI2 builds the ISO in memory via MsftFileSystemImage.CreateResultImage(), which returns
    // the image data as a COM IStream together with the block size and total block count.

    // This method reads that stream block by block and writes the blocks to the output file.

    // IStream.Read (COM interface method) reports the number of bytes read by writing to a raw
    // memory address. Marshal.AllocHGlobal allocates that memory and returns an IntPtr to it,
    // and Marshal.ReadInt32 reads the value back. this avoids the need for unsafe code and pointers.

    // method parameters:
    // 'path' is the output file path for the ISO image to create.
    // 'isoImageSource' is the input COM IStream containing the ISO image data to write to disk.
    // 'blockSize' is the size of one block in bytes; the method reads and writes one block at a time until the
    // entire image is written.
    // 'blockCount' is the total number of blocks in the image; used to determine how many blocks to read/write in
    // total.
    // 'bufferSizeMultiplier' controls how many blocks are read per IStream.Read call. 1 reads one block at a time
    // (default); higher values read multiple blocks per call, reducing COM interop overhead.
    public static void Create(string path, object isoImageSource, int blockSize, int blockCount, int bufferSizeMultiplier)
    {
        // calculate the read chunk size: how many bytes to read per IStream.Read call.
        int chunkSize = blockSize * bufferSizeMultiplier;
        // allocate 4 bytes of unmanaged memory (size of int) for IStream.Read to write the byte count into.
        // IStream.Read expects a raw memory address; it cannot write to a regular C# variable.
        // Marshal.AllocCoTaskMem returns an IntPtr pointing to that memory.
        System.IntPtr bytesReadPtr = System.Runtime.InteropServices.Marshal.AllocCoTaskMem(sizeof(System.Int32));

        // declare 'buffer' array of type byte.
        // IStream.Read requires a caller-supplied byte array to write data into; it cannot write directly to a
        // file, so data is read from the stream into 'buffer' first, then written from 'buffer' to the file.
        // sized to hold 'bufferSizeMultiplier' blocks per read call.
        // the array is allocated once here and reused on every loop iteration to avoid unnecessary memory
        // allocation.
        byte[] buffer = new byte[chunkSize];

        // declare 'isoImageSourceStream' variable of type 'IStream' (the COM interface that exposes the Read method).
        // 'isoImageSource' is an input parameter received as type 'object' in the method declaration above, as that is
        // how COM objects are passed from PowerShell into C# code.
        // expression 'isoImageSource as IStream' casts 'isoImageSource' from type 'object' to type 'IStream' so C# can
        // call IStream.Read() on it.
        System.Runtime.InteropServices.ComTypes.IStream isoImageSourceStream = isoImageSource as System.Runtime.InteropServices.ComTypes.IStream;

        // try/finally ensures the unmanaged memory allocated above is always freed, even if an error occurs.
        try
        {
            // declare 'fileStream' variable of type FileStream.
            // 'File.OpenWrite(path)' opens the destination ISO file at 'path' for writing.
            // the 'using' keyword ensures 'fileStream' is automatically closed when the block exits.
            using (System.IO.FileStream fileStream = System.IO.File.OpenWrite(path))
            {
                // pre-allocate the full file size on disk to prevent fragmentation and avoid
                // repeated file extension during writing. cast to long to avoid int overflow.
                fileStream.SetLength((long)blockSize * blockCount);

                // read from 'isoImageSourceStream' until the stream returns 0 bytes (end of data).
                int bytesRead;
                do
                {
                    // read up to 'chunkSize' bytes from the image stream.
                    // 'bytesReadPtr' is the memory address where it writes back the actual number of bytes read.
                    isoImageSourceStream.Read(buffer, chunkSize, bytesReadPtr);
                    bytesRead = System.Runtime.InteropServices.Marshal.ReadInt32(bytesReadPtr);

                    // write the bytes actually read to the destination ISO file.
                    if (bytesRead > 0) fileStream.Write(buffer, 0, bytesRead);
                } while (bytesRead > 0);
            }
        }
        finally
        {
            // free the unmanaged memory allocated by Marshal.AllocCoTaskMem above.
            System.Runtime.InteropServices.Marshal.FreeCoTaskMem(bytesReadPtr);
        }
    }
}