This is the last step in turning your download of a Cumulative Update (CU) into an ISO file. In this part, we want to be able to copy a certain folder on your drive to a new ISO file.
I had no idea how to start with this .. and what do you do when you have no clue? You goo.. sorry .. You BING it (I admit, I googled … ;-))! And here are some resources that really put me in the right direction:
- http://blogs.msdn.com/b/opticalstorage/archive/2010/08/13/writing-optical-discs-using-imapi-2-in-powershell.aspx. This one explained on how to move a folder to an imagestream. I used the first 3 steps of this one, which was actially the point where I should be able to write an ImageStream to disk. The latter appeared not that simple.. .
- https://gist.github.com/marnix/3944688: After some searching, I found exactly what I was looking for: a PowerShell function “WriteIStreamToFile”. Appartenly, this is not really possible in PowerShell, because we can’t use “System.Runtime.InteropServices.ComTypes.Istream. There is more information in the link. You’ll see it’s quite funky: writing C# code in powershell, adding a type which we will be able to use then in PowerShell. I just use this function as-is!
So, the main function is called “New-ISOFileFromFolder” and this is how it looks:
Function New-ISOFileFromFolder{ param( [Parameter(Mandatory=$true)] [String]$FilePath, [Parameter(Mandatory=$true)] [String]$Name, [Parameter(Mandatory=$true)] [String]$ResultFullFileName ) $fsi = New-Object -ComObject IMAPI2FS.MsftFileSystemImage $dftd = New-Object -ComObject IMAPI2.MsftDiscFormat2Data $Recorder = New-Object -ComObject IMAPI2.MsftDiscRecorder2 $fsi.FileSystemsToCreate = 7 $fsi.VolumeName = $Name $fsi.FreeMediaBlocks = 500000 #default 332800 $fsi.Root.AddTreeWithNamedStreams($FilePath,$false) $resultimage = $fsi.CreateResultImage() $resultStream = $resultimage.ImageStream Write-IStreamToFile $resultStream $ResultFullFileName }
You see that I use the resources from above: adding a folder to an image, and having an ImageStream as result. This imagestream is going to be converted to a file with the exact downloaded function from above “Write-IStreamToFile“, which looks like this:
function Write-IStreamToFile([__ComObject] $istream, [string] $fileName) { # NOTE: We cannot use [System.Runtime.InteropServices.ComTypes.IStream], # since PowerShell apparently cannot convert an IStream COM object to this # Powershell type. (See http://stackoverflow.com/a/9037299/223837 for # details.) # It turns out that .NET/CLR _can_ do this conversion. # # That is the reason why method FileUtil.WriteIStreamToFile(), below, # takes an object, and casts it to an IStream, instead of directly # taking an IStream inputStream argument. $cp = New-Object CodeDom.Compiler.CompilerParameters $cp.CompilerOptions = "/unsafe" $cp.WarningLevel = 4 $cp.TreatWarningsAsErrors = $true Add-Type -CompilerParameters $cp -TypeDefinition @" using System; using System.IO; using System.Runtime.InteropServices.ComTypes; namespace My { public static class FileUtil { public static void WriteIStreamToFile(object i, string fileName) { IStream inputStream = i as IStream; FileStream outputFileStream = File.OpenWrite(fileName); int bytesRead = 0; int offset = 0; byte[] data; do { data = Read(inputStream, 2048, out bytesRead); outputFileStream.Write(data, 0, bytesRead); offset += bytesRead; } while (bytesRead == 2048); outputFileStream.Flush(); outputFileStream.Close(); } unsafe static private byte[] Read(IStream stream, int toRead, out int read) { byte[] buffer = new byte[toRead]; int bytesRead = 0; int* ptr = &bytesRead; stream.Read(buffer, toRead, (IntPtr)ptr); read = bytesRead; return buffer; } } } "@ [My.FileUtil]::WriteIStreamToFile($istream, $fileName) }
And there you go, last step is finished! And we have all components we need to convert our exe, our exact download of partnersource of a Cumulative Update, and turn it into an ISO file.