Windows 8 introduced a new API for modern applications called the Windows Runtime API or WinRT API for short. One of the hallmarks of this API is that it eschews synchronous methods for any method that could take longer than 50 milliseconds to complete. The reason is that many developers still do most of their logic on the UI thread. Microsoft wants to make sure that end users have a good experience using modern apps. These apps should not appear to hang – even momentarily. The experience should be, as Microsoft has said on many occasions, “fast and fluid”. By ensuring that long running calls can’t be called synchronously, the chances improve that end users will have a better experience with modern apps.
In order to make this work well for developers, C# and VB have been modified to handle these async methods in a “simple to program” way via new async and await keywords. Unfortunately, for us PowerShell scripters, we haven’t gotten such keywords added to PowerShell. And considering PowerShell’s main focus, Windows administration, we may never see language features to make it easier to call these async WinRT APIs.
Fortunately, where there is a will there is a way. We can create a C# wrapper class to make it possible for PowerShell script to call these async methods in a synchronous manner. If you think about it, PowerShell doesn’t usually present GUI that needs to remain responsive. In many cases, our scripts run unattended where 50 millisecond or longer calls is really no big deal.
Here is an example of a WinRT API you might like to use from PowerShell:
C:\PS> [Windows.Storage.StorageFile,Windows.Storage,ContentType=WindowsRuntime] > $null
C:\PS> $path = "$home\Pictures\foo.png" C:\PS> $asyncOp = [Windows.Storage.StorageFile]::GetFileFromPathAsync($path)
C:\PS> $asyncOp System.__ComObject
First up, loading WinRT types in PowerShell takes a bit of a funky syntax as shown in the first line above. You first specify the WinRT type – Windows.Storage.StorageFile that you want to load, next I believe you specify the name of the winmd file – Windows.Storage (located in $env:WinDir\System32\WinMetadata). The third component – ContentType – specifies that the winmetadata refers to a WindowsRuntime type. Documentation on this format is somewhere between sparse and non-existent as far as I can tell.
Once we have the WinRT type loaded, we try to call one of its static type members – “GetFileFromPathAsync()”. This method returns an object that implements IAsyncOperation<StorageFile>. To PowerShell it looks like a System.__ComObject which means you don’t get any type info on the object and as far as I can tell, it isn’t usable. The interface should implement a Completed property and a GetResults() method, neither of which are recognized as a member of the PowerShell object $asyncOp.
Whenever we run into these sort of problems with PowerShell, the solution is typically to create a “PowerShell friendly” C# wrapper around the functionality. In this case, I have created a C# wrapper around objects that implement IAsyncOperation. The code is pretty simple. You can view it here on GitHub.
With this compiled into a .NET assembly called PoshWinRT.dll, I can then use the API like so:
C:\PS> [Windows.Storage.StorageFile,Windows.Storage,ContentType=WindowsRuntime] > $null C:\PS> $path = "$home\Pictures\foo.png" C:\PS> $asyncOp = [Windows.Storage.StorageFile]::GetFileFromPathAsync($path) C:\PS> Add-Type -Path ~\PoshWinRT.dll C:\PS> $typeName = 'PoshWinRT.AsyncOperationWrapper[Windows.Storage.StorageFile]' C:\PS> $wrapper = new-object $typeName -Arg $asyncOp C:\PS> $file = $wrapper.AwaitResult() C:\PS> $file ContentType : image/png FileType : .png Attributes : Archive DateCreated : 8/9/2013 10:34:22 AM -06:00 Name : foo.png Path : C:\Users\Keith\Pictures\foo.png DisplayName : foo DisplayType : PNG File FolderRelativeId : 6AE4A0202B76C225\foo.png Properties : Windows.Storage.FileProperties.StorageItemContentProperties Provider : Windows.Storage.StorageProvider IsAvailable : True
Note that we have to Add-Type our helper DLL PoshWinRT.dll. Then after we get back the $asyncOp object from the GetFileFromPathAsync() call, we create an instance of the PoshWinRT.AsyncOperationWrapper class. This class is a generic class so we need to pass in the type parameter – Windows.Storage.StorageFile in this case. This, by the way, means that you can use this wrapper class with other WinRT async APIs that return types other than StorageFile. Just pass the appropriate type in as the type parameter when constructing the AsyncOperationWrapper class. The last thing we do when creating the wrapper is to pass in the object that we got back from the async API call. The one that is of type IAsyncOperation<T>. The wrapper will wrap that object.
Once we have the wrapper class created, we simply call its AwaitResult() method. That will return whatever type you specified when creating the wrapper class. In this case, it will return a Windows.Storage.StorageFile object as you can see when we dump the result in $file to screen.
There are a number of challenges using WinRT from PowerShell. Fortunately we can overcome async APIs that return IAsyncOperation with a C# wrapper class like the one introduced in this post. One of the next challenges to overcome is how to subscribe to WinRT events which is not supported by PowerShell V3 out-of-the-box.