If you have used the Deployment Imaging Servicing Management (DISM) cmdlets in Windows 8 (Server or Client), you would have quickly realized that the functionality is limited only to certain basic tasks around image servicing and tasks such as capturing a WIM image and applying WIM image are still with dism.exe. So, we still need to use dism.exe when we need to perform some of the advanced tasks as I mentioned here. Now, it may not a very suitable solution when you want to do everything using PowerShell. Of course, you can wrap dism.exe in PowerShell but in my opinion, that is not the best way! I prefer everything PowerShell as long as possible.
So, I started exploring Windows Imaging Application Platform Interface (WIMGAPI) to see how easy or tough it is to use the same in PowerShell. WIMGAPI uses native code and we need to use PInvoke technique to access the interfaces in PowerShell. For those who are new to P/Invoke, PowerShell team member Lee Holmes has a great walk-through article here: http://www.leeholmes.com/blog/2009/01/19/powershell-pinvoke-walkthrough/
Now, coming back to the WIMGAPI part of this post, WIMGAPI.dll has the interfaces used by imagex.exe and dism.exe for capturing and applying WIM images, getting information about WIM images, and etc.
This post is about showing a quick method to demonstrate how to access and I have not completed my work on exploring the API yet. Especially, the aspects such as using callback methods to plot the image capture or apply progress in a PowerShell progress bar, etc.
So, let us start with how to access some of the methods in WIMGAPI.dll in PowerShell using the P/Invoke method:
$wimgapiSignature = @' [DllImport("wimgapi.dll", EntryPoint = "WIMCreateFile", SetLastError = true)] public static extern IntPtr WIMCreateFile( [MarshalAs (UnmanagedType .LPWStr )] string lpszWimPath, UInt32 dwDesiredAccess, Int32 dwCreationDisposition, Int32 dwFlagsAndAttributes, Int32 dwCompressionType, out Int32 lpdwCreationResult ); [DllImport("wimgapi.dll", EntryPoint = "WIMApplyImage", SetLastError = true)] public static extern int WIMApplyImage( IntPtr hImage , [MarshalAs (UnmanagedType .LPWStr )] string lpszPath, Int32 dwApplyFlags ); [DllImport("wimgapi.dll", EntryPoint = "WIMGetImageCount", SetLastError = true)] public static extern Int32 WIMGetImageCount( IntPtr hwim ); [DllImport("wimgapi.dll", EntryPoint = "WIMLoadImage", SetLastError = true)] public static extern IntPtr WIMLoadImage( IntPtr hwim, Int32 dwImageIndex ); [DllImport("wimgapi.dll", EntryPoint = "WIMSetTemporaryPath", SetLastError = true)] public static extern int WIMSetTemporaryPath( IntPtr hwim, [MarshalAs (UnmanagedType .LPWStr )] string pszPath ); [DllImport("wimgapi.dll", EntryPoint = "WIMCloseHandle", SetLastError = true)] public static extern int WIMCloseHandle( IntPtr hwim ); '@
Here, I have only included methods I tested so far. This is lot of trial and error. If you ask me, I really have no scientific method to generate this. For a developer, it may be easy to work on something like this but for a non-dev, it is a nightmare.
Now, we can Add-Type cmdlet to compile this so that we can access the same using PowerShell.
$wimUtils = Add-Type -MemberDefinition $wimgapiSignature -Name WIMUtils -Namespace WIMApplyImage -PassThru
Once we have this, we can start using the methods we have put in the signature. You can use the reference operator to get access to the methods as shown here.
The steps required to apply a WIM image are like this:
- Open the WIM image for reading. This is done using the WIMCreateFile method.
- Set the temporary path for storing the image files using the WIMSetTemporaryPath method.
- Load the right image index as we need using the WIMLoadImage method.
- Finally, we need to apply the image using WIMApplyImage method.
Open the WIM image for reading
$wimFile = $WIMUtils::WIMCreateFile("D:\sources\install.wim",0x40000000,0x00000003,0x00000002,0x00000000,[ref]$null
Refer to the WMICreateFile documentation for more information on the parameter values I used here.
Set Temporary Path for WIM image files
Using this method, we set the path to which we want to apply the WIM image.
Load the right image from the WIM file
So, here, we need to know the image index of the image we are trying to apply. The WIMGetImageInformation can help us do that. However, I don’t have that implemented yet. So, I am just using image index 1 for this post.
$image = $WIMUtils::WIMLoadImage($wimFile,1)
Apply the image to chosen destination
We can now apply the image to the destination folder or mounted VHD or whatever you choose to. This is done using:
This is it. Depending on the size of the image, this may take a while to complete. Once applying image is complete, we need to close the open file handles using the WIMCloseHandle method.
I did some research on this topic and found an old project – Install-WindowsImage – on MSDN. This is mostly around applying WIM image only but has some nifty techniques on using WIMGAPI. There are a few gaps in this code as well. I am trying to work on this to create a simple module to help do the advanced DISM tasks using PowerShell. Keep watching this space!