我正在尝试使用Windows消息在两个C#/ .NET 3.5应用程序之间进行通信,但是我发送的消息似乎在某些时候(但不是所有时间)都被接收 - 为什么会发生这种情况,以及如何确保始终正确处理消息。我有一个客户端对象如下:
[DllImport("User32.dll", EntryPoint = "FindWindow")]
public static extern Int32 FindWindow(String lpClassName, String lpWindowName);
[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern IntPtr SendMessage(IntPtr hWindow, int message, IntPtr wParam, IntPtr lParam);
public class WMTCPBridge
{
private IntPtr TargetHwnd
public void SendNumericMessage(Int32 messageCode,
Int32 MessagePayload)
{
//for some reason String.fomat("blah 0x{0:X}",TargetHwnd) shows the number in decimal
string sendNotice = String.Format("Sending to window 0x{0}", TargetHwnd.ToString("X"));
myTextOutput.Writeline(sendNotice);
sendNotice = String.Format("Sending to window {0}", TargetHwnd);
myTextOutput.Writeline(sendNotice);
IntPtr unmanagedInt = Marshal.AllocHGlobal(sizeof(Int32));
Marshal.WriteInt32(unmanagedInt,MessagePayload);
IntPtr result = IntPtr.Zero;
try
{
result = SendMessage(TargetHwnd, WM_COPYDATA, (IntPtr)messageCode,
unmanagedInt);
}
finally
{
Marshal.FreeHGlobal(unmanagedInt);
}
myTextOutput.Writeline("Result is " + result);
if ((int)result == 0)
{
myTextOutput.Writeline("Error code : " + GetThreadError());
}
}
public void GetTargetHandle(string targetName)
{
TargetHwnd = (IntPtr)FindWindow(null, targetName);
if (TargetHwnd == null)
{
myTextOutput.Writeline("Could not connect to UI");
}
else
{
String outputLine = string.Format("Connected to window number 0x{0}", TargetHwnd.ToString("X"));
myTextOutput.Writeline(outputLine);
outputLine = string.Format("Connected to window number {0}", TargetHwnd);
myTextOutput.Writeline(outputLine);
}
}
}
我的测试应用程序的主要形式拥有WMTCPBridge类型的对象,通过调用GetTargetHandle开始通信,并通过调用SendNumericMessage方法发送单个消息。服务器是一个测试工具,代表现有的应用程序,我希望避免不必要的更改。正是这个现有的应用程序驱动接口的选择(我必须使用WM_COPYDATA,我必须通过wparam发送消息类型代码,如果我想发送一个整数,我应该通过lparam而不是Copydatastruct发送整数)。服务器应用程序的主要形式覆盖了wndproc方法,如下所示:
protected override void WndProc(ref Message m)
{
Int32 messageCode=0;
Int32 messagePayload=0;
Debug.WriteLine(m);
switch (m.Msg)
{
case WM_COPYDATA:
{
messageCode = (int)m.WParam;
messagePayload = Marshal.ReadInt32(m.LParam);
WriteLine("Received message with code " + messageCode +
" and payload " + messagePayload);
break;
}
case WM_CLOSE:
{
WriteLine("Close blocked!");
return;
break;
}
}
base.WndProc(ref m);
}
当我一起运行服务器和客户端时,客户端报告它正在发送消息来处理我可以通过Winspector看到的是服务器窗口的句柄,sendMessage函数返回0并且应用程序错误为0.通常,服务器不报告获取任何消息,Winspector不显示任何发送到服务器的WM_COPYDATA消息。但是,如果我继续从客户端发送消息,服务器将收到一些消息 - 我通常会有条纹,其中所有消息都通过或没有。当我修改客户端以发送WM_CLOSE消息时,服务器将不可避免地接收它们并关闭 - 即使我尝试使用WndProc方法捕获WM_CLOSE消息,如上所示。
我的讯息发生了什么变化?我特别困惑,因为MSDN说SendMessage函数只在消息处理后才返回。
答案 0 :(得分:1)
您不能忽视Windows希望LPARAM指向COPYDATASTRUCT结构的事实。但是,您只分配4个字节,而不足以存储该结构。接下来发生的事情是不可预测的,Windows将读取您分配的内存,查找COPYDATASTRUCT.cbData和lpData的值。你可能会很幸运,它读取cbData = 0.或者没那么幸运并且读取非零值。这将使它取消引用lpData并且几乎总是生成AccessViolation异常。你可以知道发生这种情况时,SendMessage()会返回一个值。一个你没有检查,所以你不知道什么时候出错。
只要您想继续使用WM_COPYDATA, 就可以为其提供正确的参数。更好的方法是使用命名管道或套接字。这也避免了使用FindWindow(),这是一种非常不可靠的方法来查找窗口句柄。