问题描述
我一直在尝试使用其记录的 copYDATA API 与另一个软件进行通信。用户 Xanotos 对 in this question I asked 非常有帮助,它的发送方法已排序并且工作正常。我遇到的问题是接收 WndProc
方法似乎没有捕捉到预期的响应。这是 COPYDATA API 文档的链接以供参考。
目前的方法如下。测试表明 WndProc
确实收到消息,但不是我期望的消息,即 struct
取决于发送的消息。
声明:
[DllImport("User32.dll",SetLastError = true,EntryPoint = "FindWindow")]
public static extern IntPtr FindWindow(String lpClassName,String lpWindowName);
[DllImport("User32.dll",EntryPoint = "SendMessage")]
public static extern IntPtr SendMessage(IntPtr hWnd,int Msg,int wParam,ref copYDATASTRUCT lParam);
[StructLayout(LayoutKind.Sequential)]
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.
}
public struct ExternalGetPositionType
{
public double X;
public double Y;
public double Z;
public double W;
}
const int WM_copYDATA = 0x004A;
const int EXTERNAL_CD_COMMAND_RUN_ASYNC = 0x8001;
const int EXTERNAL_CD_GET_POSITION_PCS = 0x8011;
const int EXTERNAL_CD_GET_POSITION_MCS = 0x8012;
发送方法(这些都可以正常工作并返回真):
public static IntPtr RunAsync(IntPtr hwnd,string str)
{
// We have to add a \0 terminator,so len + 1 / len + 2 for Unicode
int len = Encoding.Default.GetByteCount(str);
var buff = new byte[len + 1]; // len + 2 for Unicode
Encoding.Default.GetBytes(str,str.Length,buff,0);
IntPtr ret;
GCHandle h = default(GCHandle);
try
{
h = GCHandle.Alloc(buff,GCHandleType.Pinned);
var cds = new copYDATASTRUCT();
cds.dwData = (IntPtr)EXTERNAL_CD_COMMAND_RUN_ASYNC;
cds.lpData = h.AddrOfPinnedobject();
cds.cbData = buff.Length;
ret = SendMessage(hwnd,WM_copYDATA,ref cds);
}
finally
{
if (h.IsAllocated)
{
h.Free();
}
}
return ret;
}
public static IntPtr GetPosition(IntPtr hwnd,bool pcs,ExternalGetPositionType position)
{
// We cheat here... It is much easier to pin an array than to copy around a struct
var positions = new[]
{
position
};
IntPtr ret;
GCHandle h = default(GCHandle);
try
{
h = GCHandle.Alloc(positions,GCHandleType.Pinned);
var cds = new copYDATASTRUCT();
cds.dwData = pcs ? (IntPtr)EXTERNAL_CD_GET_POSITION_PCS : (IntPtr)EXTERNAL_CD_GET_POSITION_MCS;
cds.lpData = h.AddrOfPinnedobject();
cds.cbData = Marshal.SizeOf<ExternalGetPositionType>();
ret = SendMessage(hwnd,ref cds);
}
finally
{
if (h.IsAllocated)
{
h.Free();
}
}
return ret;
}
WndProc
方法 - 这是事情没有按预期工作的地方。调用 if(m.Msg == WM_copYDATA)
时,GetPosition
永远不会返回 true。
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_copYDATA) //this does not execute
{
copYDATASTRUCT cds = Marshal.PtrToStructure<copYDATASTRUCT>(m.LParam);
label5.Text = "message received";
if (cds.dwData == (IntPtr)EXTERNAL_CD_COMMAND_RUN_ASYNC)
{
label5.Text = "norMAL";
string str = Marshal.PtrToStringAnsi(cds.lpData);
Debug.WriteLine($"EXTERNAL_CD_COMMAND_RUN_ASYNC: {str}");
toolStripStatusLabel1.Text = $"COMMAND";
m.Result = (IntPtr)100; // If you want to return a value
}
else if (cds.dwData == (IntPtr)EXTERNAL_CD_GET_POSITION_PCS) //this does not execute
{
label5.Text = "MSC";
if (cds.cbData >= Marshal.SizeOf<ExternalGetPositionType>())
{
var position = Marshal.PtrToStructure<ExternalGetPositionType>(cds.lpData);
Debug.WriteLine($"EXTERNAL_CD_GET_POSITION_PCS: X = {position.X},Y = {position.Y},Z = {position.Z},W = {position.W}");
toolStripStatusLabel1.Text = $"External MCS = {position.X}";
label4.Text = position.X.ToString();
m.Result = (IntPtr)200;
}
else
{
m.Result = (IntPtr)0;
}
}
else if (cds.dwData == (IntPtr)EXTERNAL_CD_GET_POSITION_MCS) //this does not execute
{
label5.Text = "MSC"; //this does not execute
if (cds.cbData >= Marshal.SizeOf<ExternalGetPositionType>())
{
var position = Marshal.PtrToStructure<ExternalGetPositionType>(cds.lpData);
Debug.WriteLine($"EXTERNAL_CD_GET_POSITION_MCS: X = {position.X},W = {position.W}");
toolStripStatusLabel1.Text = $"External MCS = {position.X}";
label4.Text = position.X.ToString();
m.Result = (IntPtr)300;
}
else
{
m.Result = (IntPtr)0;
}
}
return;
}
MessageBox.Show(m.Msg.ToString()); //this DOES execute
base.WndProc(ref m);
}
两个程序都是 32 位的。
知道为什么我没有收到预期的 Msg
吗?
解决方法
您提供的文档不清楚,但您似乎需要将 wParam
设置为接收窗口句柄才能接收响应数据。
在第 3 页的底部有一个例子:
::SendMessage(m_pWnd->GetSafeHwnd(),WM_COPYDATA,(WPARAM)this->GetSafeHwnd(,(LPARAM)&MyCDS);
而你的代码有这个:
ret = SendMessage(hwnd,ref cds);
相反,你需要
ret = SendMessage(hwnd,this.Handle,ref cds);
您必须还将 wParam
上的 SendMessage
重新定义为 IntPtr
。在任何情况下都是错误的,可能只是因为您使用的是 32 位。