Reputation: 3804
I'm attempting to write a script to track WIM apply progress--so far, I've been utilizing this WimgAPI wrapper example: https://managedwimgapi.codeplex.com/wikipage?title=Using%20the%20message%20callback%20functionality%20for%20recieving%20progress%20and%20other%20information&referringTitle=Documentation
Which I have converted into Powershell:
Add-Type -Path "Microsoft.Wim.dll"
# Open a handle to the .wim file
$wimHandle = [Microsoft.Wim.WimgApi]::CreateFile("G:\sources\boot.wim", `
[Microsoft.Wim.WimFileAccess]::Read,[Microsoft.Wim.WimCreationDisposition]::OpenExisting, `
[Microsoft.Wim.WimCreateFileOptions]::None, [Microsoft.Wim.WimCompressionType]::None)
# Always set temporary path
[Microsoft.Wim.WimgApi]::SetTemporaryPath($wimHandle, $env:temp) | Out-Null
# Build & register a callback method for actions which are performed by WIMGAPI for this .wim file
$callback = [Microsoft.Wim.WimMessageCallback]{
param (
[Microsoft.Wim.WimMessageType] $messageType,
[System.Object] $message,
[System.Object] $userData
)
if($messageType -eq [Microsoft.Wim.WimMessageType]::Progress)
{
$progressMessage = ($message -as [Microsoft.Wim.WimMessageProgress])
Write-Host "Percent Complete: $($progressMessage.PercentComplete)"
}
return [Microsoft.Wim.WimMessageResult]::Success
}
[Microsoft.Wim.WimgApi]::RegisterMessageCallback($wimHandle, $callback) | Out-Null
try
{
# Get a handle to the first image in the .wim file
$imageHandle = [Microsoft.Wim.WimgAPI]::LoadImage($wimHandle, 1)
# Apply the contents to C:\Apply
[Microsoft.Wim.WimgApi]::ApplyImage($imageHandle, "C:\Apply", [Microsoft.Wim.WimApplyImageOptions]::None)
Read-Host
}
catch
{
Write-Host $_.Exception
}
finally
{
[Microsoft.Wim.WimgApi]::UnregisterMessageCallback($wimHandle, $callback) | Out-Null
}
The code executes successfully, and the WIM is applied, but unfortunately I'm only seeing the callback execute twice, like so:
Percent Complete: 0
Percent Complete: 100
Though when executing the C# application, I see every percentage value from zero to 100.
I'm thinking it may have something to do with the fact that ApplyImage is a blocking call and therefore possibly blocking the console from executing the callback, though I can't think of any way to get around this.
Any thoughts would be greatly appreciated. Thanks!
Upvotes: 1
Views: 481
Reputation: 20571
I have written a CmdLet in C# that will apply a *.wim
image to location and have pushed it up to GitHub and created a Nuget package.
https://github.com/dennisroche/Microsoft.Wim.Powershell
I initially started writing the CmdLet as I was successful a quick C# demo that was writing the progress % to the console and thought that a CmdLet might be the solution (I had never written a CmdLet before).
In writing the CmdLet, I found out that PowerShell was throwing an InvalidOperationException
when attempting to Write (e.g. Write-Host
, Write-Progress
). Looking further on MSDN and I found the following:
This method can be called only from within the implementation of an input processing method (
BeginProcessing
,ProcessRecord
, andEndProcessing
) and only from that thread. If the call is made from outside these implementations or from another thread, anInvalidOperationException
exception is thrown.
This explains why it wasn't working when called directly in Powershell - the callback was on a different thread and it had to be invoked back on the Pipeline Execution Thread
. Though for some reason, the first and last progress events were on the Pipeline Execution Thread
, which is why we were seeing the output that we did.
I found an interesting solution to invoking the update progress messages on the Pipeline Execution Thread
, using the AsyncOperationManager
, the WindowsFormsSynchronizationContext
, and Application.DoEvents()
. If you have found this answer searching the above error message, then take a look at the code on GitHub.
Upvotes: 1