Achieved Goal:
I am considering using PowerShell to monitor the input video from a USB camera device in real-time.
Problem:
Problem 1: When using the await class to wait for the return value (__ComObject) of the .Net async function, the first call (InitializeAsync) passes, but an unknown error occurs on the second call (StartPreviewAsync).
Problem 2: If I proceed without waiting for the above StartPreviewAsync, a thread error occurs.
Error Messages:
Error 1:
Exception occurred while calling "Wait" with "0" arguments: "One or more errors occurred."
Error 2:
Exception occurred while calling ".ctor" with "0" arguments: "The application called an interface that was marshaled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))"
Additional Information:
Changed the execution of $ui.Content = [CaptureElement]::new() to be executed within the UI dispatcher invoke, but it had no effect.
# using namespace
using namespace System.Windows
using namespace Windows.Foundation
using namespace Windows.Media.Capture
using namespace Windows.UI.Xaml.Controls
# using dotnet
using assembly PresentationFramework
using assembly System.Runtime.WindowsRuntime
# import Runtime type [TypeName, AssemblyName, ContentType]
[Windows.Foundation.IAsyncAction, Windows.Foundation, ContentType=WindowsRuntime]
[Windows.Media.Capture.MediaCapture, Windows.Media.Capture, ContentType=WindowsRuntime]
[Windows.UI.Xaml.Controls.CaptureElement, Windows.UI.Xaml.Controls, ContentType=WindowsRuntime]
# init MediaCapture
$capture = [Mediacapture]::new()
[await]::Action($capture.InitializeAsync(), [object])
# ui
$ui = [Window]::new()
$ui.Dispatcher.Invoke([Action]{
$ui.Content = [CaptureElement]::new()
$ui.Content.Source = $capture
[await]::Action($capture.StartPreviewAsync(), [object])
})
$ui.ShowDialog()
# awaiter class
class await{
static [object]Action([__ComObject]$async, [type]$type){
$asTask = [WindowsRuntimeSystemExtensions].GetMethod("AsTask", [IAsyncAction])
$netTask = $asTask.Invoke($null, @($async))
$netTask.Wait()
return $netTask.Result
}
}
Task.ConfigureAwaitfunction withe the boolean valuetrueas the argument at the end. This tells the task to continue on the original thread. I've never done this in powershell but I am guess you can chain all of your xxxAsync() calls that way. e.g.[await]::Action($capture.InitializeAsync().ConfigureAwait(true), [object])class await{ static [object]Action([__ComObject]$async, [type]$type){ $asTaskGeneric = [WindowsRuntimeSystemExtensions].GetMethod("AsTask", [IAsyncAction]) $netTask = $asTaskGeneric.Invoke($null, @($async)) $netTask.ConfigureAwait($true) $netTask.Wait() return $netTask.Result } }ConfigureAwaitdoes not exist in class__ComObject. It exists in classTask(specifically, classSystem.Threading.Tasks.Task). But does$asTaskGeneric.Invokereturn aTaskobject?asTask.Invoketakes a __ComObject as an IAsyncAction and can convert that __ComObject into a Task.[WindowsRuntimeSystemExtensions]::AsTask(arg)has many overrides, but none that take a __ComObject. Therefore, by calling a function with an IAsyncAction argument and passing the __ComObject as an IAsyncAction, it works correctly.WindowsRuntimeSystemExtensionsis a .Net class that converts the return value of a WinRT asynchronous function, which is a __ComObject, into an appropriate type that can be handled in .Net (restoring it to its original return type).