从 powershell 发送 WM_COPYDATA 消息,指针有问题
sending WM_COPYDATA messages from powershell, trouble with pointers
我正在尝试将控制消息从 powershell 发送到 mpc-hc。 Mpc's api 使用 WM_COPYDATA 消息。
在查看 here, here and here:
之后,这是我目前所知道的
Add-Type @"
using System;
using System.Runtime.InteropServices;
public class Messages {
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, ref IntPtr lParam);
}
public struct COPYDATASTRUCT
{
public IntPtr dwData; // Any value the sender chooses. Perhaps its main window handle?
public int cbData; // The count of bytes in the message.
public IntPtr lpData; // The address of the message.
}
"@
$WM_COPYDATA = 0x004A;
$CMD_OSDSHOWMESSAGE = 0xA0005000
$MpcWindow1 = Start-Process -PassThru -FilePath "C:\Program Files (x86)\K-Lite Codec Pack\MPC-HC64\mpc-hc64.exe" -ArgumentList "/new"
$MpcMessage = "hello"
$cds = New-Object COPYDATASTRUCT
$cds.dwData = $CMD_OSDSHOWMESSAGE
$cds.cbData = $MpcMessage.Length
$cds.lpData = $MpcMessage[0] #without [0] throws an exception
[Messages]::SendMessage($MpcWindow1.MainWindowHandle, $WM_COPYDATA, (Get-Process powershell).MainWindowHandle, [ref]$cds[0])
执行它给我:
Exception calling "SendMessage" with "4" argument(s): "Cannot convert the "COPYDATASTRUCT" value of type
"COPYDATASTRUCT" to type "System.IntPtr"."
At C:\Users\Petersburg SDA\Videos\dev\wm_copydata.ps1:27 char:1
+ [Messages]::SendMessage($MpcWindow1.MainWindowHandle, $WM_COPYDATA, (Get-Process ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : PSInvalidCastException
我不确定 $MpcMessage[0] 是否正确,但仅使用变量就得到了这个(除上述之外)
Exception setting "lpData": "Cannot convert the "hello" value of type "System.String" to type "System.IntPtr"."
At C:\Users\Petersburg SDA\Videos\dev\wm_copydata.ps1:25 char:1
+ $cds.lpData = $MpcMessage #throws an exception
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], SetValueInvocationException
+ FullyQualifiedErrorId : ExceptionWhenSetting
将其更改为 [ref]$MpcMessage 得到
Exception setting "lpData": "Cannot convert the "System.Management.Automation.PSReference`1[System.String]" value of
type "System.Management.Automation.PSReference`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089]]" to type "System.IntPtr"."
At C:\Users\Petersburg SDA\Videos\dev\wm_copydata.ps1:25 char:1
+ $cds.lpData = [ref]$MpcMessage #throws an exception
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], SetValueInvocationException
+ FullyQualifiedErrorId : ExceptionWhenSetting
诚然,我使用 powershell 的次数不多,但事实证明翻译代码异常困难。
您必须使用编组 class 将结构编组为指针。
示例:
$StructPointerSize = [System.Runtime.InteropServices.Marshal]::SizeOf($CDS)
$StructPointer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($StructPointerSize)
[System.Runtime.InteropServices.Marshal]::StructureToPtr($CDS,$StructPointer,$true
这将分配一些内存,然后将结构复制到该内存块。
在此之后,指向您的结构的指针将位于 $StructPointer 变量中。
恢复暂停视频的完整示例(使用反射创建类型):
$Domain = [System.AppDomain]::CurrentDomain
$AssemblyName = [System.Reflection.AssemblyName]::new('Messages')
$Assembly = $Domain.DefineDynamicAssembly($AssemblyName,'Run')
$ModuleBuilder = $Assembly.DefineDynamicModule('Messages')
# Define the struct (source : https://msdn.microsoft.com/en-us/library/windows/desktop/ms649010(v=vs.85).aspx)
$StructAttributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, BeforeFieldInit'
$COPYDATASTRUCT = $ModuleBuilder.DefineType('COPYDATASTRUCT',$StructAttributes)
$COPYDATASTRUCT.DefineField('dwData',[int],'Public')
$COPYDATASTRUCT.DefineField('cbData',[Uint32],'Public')
$COPYDATASTRUCT.DefineField('lpData',[System.IntPtr],'Public')
$COPYDATASTRUCT.CreateType()
# Define the class that will hold the PInvoke method
$MessageClass = $ModuleBuilder.DefineType('Messages','Public')
# Define the PInvoke Method
$SendMessage = $MessageClass.DefinePInvokeMethod(
'SendMessageW',
'User32.dll',
@('Public','Static','PinvokeImpl'),
[System.Reflection.CallingConventions]::Standard,
[System.IntPtr],
@([System.IntPtr],[Uint32],[System.IntPtr],[System.IntPtr]),
[System.Runtime.InteropServices.CallingConvention]::Winapi,
[System.Runtime.InteropServices.CharSet]::Unicode
)
$SendMessage.DefineParameter(1,'In','hWnd')
$SendMessage.DefineParameter(2,'In','Msg')
$SendMessage.DefineParameter(3,'In','wParam')
$SendMessage.DefineParameter(4,'In','lParam')
$SendMessage.SetImplementationFlags($SendMessage.GetMethodImplementationFlags() -bor [System.Reflection.MethodImplAttributes]::PreserveSig)
$MessageClass.CreateType()
$WM_COPYDATA = 0x004A;
$CMD_PLAY = 0xA0000004
$CurrentHandle = [System.Diagnostics.Process]::GetCurrentProcess().MainWindowHandle
$Arguments = '/Slave' + " " + $CurrentHandle.ToString()
Start-Process -FilePath 'C:\Program Files\MPC-HC\mpc-hc64.exe' -ArgumentList $Arguments
$CDS = [COPYDATASTRUCT]::new()
$CDS.dwData = $CMD_PLAY
$CDS.lpData = [System.IntPtr]::Zero
$CDS.cbData = 0
$StructPointerSize = [System.Runtime.InteropServices.Marshal]::SizeOf($CDS)
$StructPointer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($StructPointerSize)
[System.Runtime.InteropServices.Marshal]::StructureToPtr($CDS,$StructPointer,$true)
$p = (Get-Process mpc-hc64).MainWindowHandle
[messages]::SendMessageW($p,$WM_COPYDATA,0,$StructPointer)
我正在尝试将控制消息从 powershell 发送到 mpc-hc。 Mpc's api 使用 WM_COPYDATA 消息。 在查看 here, here and here:
之后,这是我目前所知道的Add-Type @"
using System;
using System.Runtime.InteropServices;
public class Messages {
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, ref IntPtr lParam);
}
public struct COPYDATASTRUCT
{
public IntPtr dwData; // Any value the sender chooses. Perhaps its main window handle?
public int cbData; // The count of bytes in the message.
public IntPtr lpData; // The address of the message.
}
"@
$WM_COPYDATA = 0x004A;
$CMD_OSDSHOWMESSAGE = 0xA0005000
$MpcWindow1 = Start-Process -PassThru -FilePath "C:\Program Files (x86)\K-Lite Codec Pack\MPC-HC64\mpc-hc64.exe" -ArgumentList "/new"
$MpcMessage = "hello"
$cds = New-Object COPYDATASTRUCT
$cds.dwData = $CMD_OSDSHOWMESSAGE
$cds.cbData = $MpcMessage.Length
$cds.lpData = $MpcMessage[0] #without [0] throws an exception
[Messages]::SendMessage($MpcWindow1.MainWindowHandle, $WM_COPYDATA, (Get-Process powershell).MainWindowHandle, [ref]$cds[0])
执行它给我:
Exception calling "SendMessage" with "4" argument(s): "Cannot convert the "COPYDATASTRUCT" value of type
"COPYDATASTRUCT" to type "System.IntPtr"."
At C:\Users\Petersburg SDA\Videos\dev\wm_copydata.ps1:27 char:1
+ [Messages]::SendMessage($MpcWindow1.MainWindowHandle, $WM_COPYDATA, (Get-Process ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : PSInvalidCastException
我不确定 $MpcMessage[0] 是否正确,但仅使用变量就得到了这个(除上述之外)
Exception setting "lpData": "Cannot convert the "hello" value of type "System.String" to type "System.IntPtr"."
At C:\Users\Petersburg SDA\Videos\dev\wm_copydata.ps1:25 char:1
+ $cds.lpData = $MpcMessage #throws an exception
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], SetValueInvocationException
+ FullyQualifiedErrorId : ExceptionWhenSetting
将其更改为 [ref]$MpcMessage 得到
Exception setting "lpData": "Cannot convert the "System.Management.Automation.PSReference`1[System.String]" value of
type "System.Management.Automation.PSReference`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089]]" to type "System.IntPtr"."
At C:\Users\Petersburg SDA\Videos\dev\wm_copydata.ps1:25 char:1
+ $cds.lpData = [ref]$MpcMessage #throws an exception
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], SetValueInvocationException
+ FullyQualifiedErrorId : ExceptionWhenSetting
诚然,我使用 powershell 的次数不多,但事实证明翻译代码异常困难。
您必须使用编组 class 将结构编组为指针。 示例:
$StructPointerSize = [System.Runtime.InteropServices.Marshal]::SizeOf($CDS)
$StructPointer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($StructPointerSize)
[System.Runtime.InteropServices.Marshal]::StructureToPtr($CDS,$StructPointer,$true
这将分配一些内存,然后将结构复制到该内存块。 在此之后,指向您的结构的指针将位于 $StructPointer 变量中。
恢复暂停视频的完整示例(使用反射创建类型):
$Domain = [System.AppDomain]::CurrentDomain
$AssemblyName = [System.Reflection.AssemblyName]::new('Messages')
$Assembly = $Domain.DefineDynamicAssembly($AssemblyName,'Run')
$ModuleBuilder = $Assembly.DefineDynamicModule('Messages')
# Define the struct (source : https://msdn.microsoft.com/en-us/library/windows/desktop/ms649010(v=vs.85).aspx)
$StructAttributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, BeforeFieldInit'
$COPYDATASTRUCT = $ModuleBuilder.DefineType('COPYDATASTRUCT',$StructAttributes)
$COPYDATASTRUCT.DefineField('dwData',[int],'Public')
$COPYDATASTRUCT.DefineField('cbData',[Uint32],'Public')
$COPYDATASTRUCT.DefineField('lpData',[System.IntPtr],'Public')
$COPYDATASTRUCT.CreateType()
# Define the class that will hold the PInvoke method
$MessageClass = $ModuleBuilder.DefineType('Messages','Public')
# Define the PInvoke Method
$SendMessage = $MessageClass.DefinePInvokeMethod(
'SendMessageW',
'User32.dll',
@('Public','Static','PinvokeImpl'),
[System.Reflection.CallingConventions]::Standard,
[System.IntPtr],
@([System.IntPtr],[Uint32],[System.IntPtr],[System.IntPtr]),
[System.Runtime.InteropServices.CallingConvention]::Winapi,
[System.Runtime.InteropServices.CharSet]::Unicode
)
$SendMessage.DefineParameter(1,'In','hWnd')
$SendMessage.DefineParameter(2,'In','Msg')
$SendMessage.DefineParameter(3,'In','wParam')
$SendMessage.DefineParameter(4,'In','lParam')
$SendMessage.SetImplementationFlags($SendMessage.GetMethodImplementationFlags() -bor [System.Reflection.MethodImplAttributes]::PreserveSig)
$MessageClass.CreateType()
$WM_COPYDATA = 0x004A;
$CMD_PLAY = 0xA0000004
$CurrentHandle = [System.Diagnostics.Process]::GetCurrentProcess().MainWindowHandle
$Arguments = '/Slave' + " " + $CurrentHandle.ToString()
Start-Process -FilePath 'C:\Program Files\MPC-HC\mpc-hc64.exe' -ArgumentList $Arguments
$CDS = [COPYDATASTRUCT]::new()
$CDS.dwData = $CMD_PLAY
$CDS.lpData = [System.IntPtr]::Zero
$CDS.cbData = 0
$StructPointerSize = [System.Runtime.InteropServices.Marshal]::SizeOf($CDS)
$StructPointer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($StructPointerSize)
[System.Runtime.InteropServices.Marshal]::StructureToPtr($CDS,$StructPointer,$true)
$p = (Get-Process mpc-hc64).MainWindowHandle
[messages]::SendMessageW($p,$WM_COPYDATA,0,$StructPointer)