如何在 Powershell 中处理 WinRT IInputStream?

How can I handle WinRT IInputStream in Powershell?

我喜欢查看连接的蓝牙耳机的电池状态。使用脚本,我希望能够在我的 Win10 桌面上使用 Rainmeter.

之类的东西打印它

我正在使用 WinRT 命名空间成功找到并连接到我的蓝牙耳机,但要打开一个流并读取内容(例如电池状态),我必须处理一个接口。

到目前为止我得到的工作:

#Add Type WinRT so that we can make use of Universal Windows Platform (UWP)
Add-Type -AssemblyName System.Runtime.WindowsRuntime

#To deal with asynchronous functions/actions in WinRT we can use the following functions: Await and AwaitAction
#With thanks to Ben N. on https://superuser.com/questions/1341997/using-a-uwp-api-namespace-in-powershell
$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
}
Function AwaitAction($WinRtAction) {
    $asTask = ([System.WindowsRuntimeSystemExtensions].GetMethods() | ? { $_.Name -eq 'AsTask' -and $_.GetParameters().Count -eq 1 -and !$_.IsGenericMethod })[0]
    $netTask = $asTask.Invoke($null, @($WinRtAction))
    $netTask.Wait(-1) | Out-Null
}

#Get the relevant DeviceId from $btDeviceAll(not shown here for brevity)
$SonyWH1000XM3id = 'Bluetooth#Bluetoothac:##:##:##:##:##-##:##:##:##:##:##'

#Get relevant btDevice by referencing the DeviceId.
$btDeviceClass = [Windows.Devices.Bluetooth.BluetoothDevice,Windows.Devices.Bluetooth,ContentType = WindowsRuntime]
$btDevice = Await ($btDeviceClass::FromIdAsync($SonyWH1000XM3id)) ([Windows.Devices.Bluetooth.BluetoothDevice])

#Choose the relevant $btRfcommServices.services[] from $btRfcommServices(not shown here for brevity, my sony headset has HFP handsfree policy available)
$btHFPServiceUUID = '0000111e-0000-1000-8000-00805f9b34fb'
$btHFPConnectionHostName = '##:##:##:##:##:##'
$btHFPConnectionServiceName = 'Bluetooth#Bluetoothac:##:##:##:##:##-##:##:##:##:##:###RFCOMM:00000000:{00001108-0000-1000-8000-00805f9b34fb}'

#Connect to the device with a StreamSocket
[Windows.Networking.HostName,Windows.Networking,ContentType = WindowsRuntime]
[Windows.Networking.Sockets.StreamSocket,Windows.Networking.Sockets,ContentType = WindowsRuntime]
$btHostName = [Windows.Networking.HostName]($btHFPConnectionHostName)
$btSocket = New-Object Windows.Networking.Sockets.StreamSocket
AwaitAction ($btSocket.ConnectAsync($btHostName,$btHFPConnectionServiceName))

现在,要获取实际数据,我需要使用 $btSocket.InputStream Property 通过:

  1. 方法:IInputStream.ReadAsync(IBuffer, UInt32, InputStreamOptions);或者
  2. class DataReader(IInputStream).

不幸的是,.InputStream 是一个 IInputStream 接口,但 Powershell 看到一个 System.__ComObject,我不知道如何告诉 Powershell 它是什么。因此,使用 DataReader(IInputStream) 的第二种方法会产生错误。第一种方法,通过调用 ReadAsync 给我 Object of type 'System.__ComObject' cannot be converted to type 'Windows.Foundation.IAsyncAction' when:

$InputStreamOptionPartial = [Windows.Storage.Streams.InputStreamOptions]::Partial
$btByte = New-Object Byte[] 1024
$btIBuffer = [System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeBufferExtensions]::AsBuffer($btByte)
$bfSize = [UInt32]"1024"
$btReadAsync = [Windows.Storage.Streams.IInputStream].GetMethod('ReadAsync',[type[]]@('Windows.Storage.Streams.Buffer','UInt32','Windows.Storage.Streams.InputStreamOptions'))
AwaitAction ($btReadAsync.Invoke($btSocket.InputStream,@($btIBuffer,$bfSize,$InputStreamOptionPartial)))

顺便说一句:我也试过在 Powershell 的 C# 脚本中执行它,但我什至无法让 C# 脚本使用 WinRT 命名空间,只有 using system.io

有人可以向我解释(或输入)如何让 DataReader() 接受 StreamSocket.IInputStream 或从 ReadAsync 获得工作缓冲区吗?也欢迎使用替代脚本解决方案。谢谢。

根据 documentationIInputStream.ReadAsync() returns IAsyncOperationWithProgress<IBuffer,uint> 并且您正在尝试使用 AsTask(IAsyncAction).

这里是一个使用AsTask(IAsyncOperationWithProgress<IBuffer,uint>)的例子:

https://gist.github.com/lselden/ab2e04fbac785e0644c4b562bf5e35cd

$asTaskGeneric = ([System.WindowsRuntimeSystemExtensions].GetMethods() | ? {
    $_.Name -eq 'AsTask' -and 
    $_.GetParameters().Count -eq 1 -and 
    $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperationWithProgress`2' })[0];

Function AwaitWithProgress($WinRtTask, $ResultType1, $ResultType2) {
    $asTask = $asTaskGeneric.MakeGenericMethod($ResultType1, $ResultType2)
    $netTask = $asTask.Invoke($null, @($WinRtTask))
    $netTask.Wait(-1) | Out-Null
}

然后您可以 运行 您的调用方法如下:

AwaitWithProgress ($btReadAsync.Invoke($btSocket.InputStream,@($btIBuffer,$bfSize,$InputStreamOptionPartial))) ([Windows.Storage.Streams.IBuffer]) ([UInt32])