Accessing Windows Imaging Application Platform Interface (WIMGAPI) in PowerShell

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.

WIM Methods

WIM Methods

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.
Now, let us see how to do each of these steps in PowerShell.

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.

$WIMUtils::WIMSetTemporaryPath($WimFile,"C:\dell\vm\")

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:

$WIMUtils::WIMApplyImage($image,"C:\dell\vm\",0x00000002)

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.

$WIMUtils::WIMCloseHandle($image)
$WIMUtils::WIMCloseHandle($wimFile)

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!