Friday, February 9, 2018

Using WinRT's IAsyncOperation in PowerShell

WinRT types can be used from PowerShell if explicitly named first. Many WinRT API methods are asynchronous, returning genericized IAsyncOperation objects that come into PowerShell as System.__ComObject. Trying to use any methods on such objects fails. Some people have written compiled assemblies in C# that convert async operations to standard .NET tasks and then await them, but this can be accomplished in pure PowerShell with some reflection:
Add-Type -AssemblyName System.Runtime.WindowsRuntime
$asTaskGeneric = ([System.WindowsRuntimeSystemExtensions].GetMethods() | ? { $_.Name -eq 'AsTask' -and $_.GetParameters().Count -eq 1 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1' })[0]
Function Await($WinRtTask, $ResultType) {
 $asTask = $asTaskGeneric.MakeGenericMethod($ResultType)
 $netTask = $asTask.Invoke($null, @($WinRtTask))
 $netTask.Wait(-1) | Out-Null
 $netTask.Result
}

The Await function takes the IAsyncObject returned by a WinRT function and the type of the result. It returns the task's result. Example:
Await ([Windows.Devices.Radios.Radio]::RequestAccessAsync()) ([Windows.Devices.Radios.RadioAccessStatus])

The parentheses around the arguments are important, otherwise PowerShell tries to be helpful and interpret them as string literals.

Originally developed for my Super User answer.

1 comment:

  1. Awesome! I was struggling for a long time with this issue, trying to set the lock screen image from PowerShell. This was the final piece of the puzzle. Thanks!

    ReplyDelete