0

我正在尝试使用 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 函数仅在处理完消息后才返回。

4

1 回答 1

1

您不能忽略 Windows 希望 LPARAM 指向 COPYDATASTRUCT 结构这一事实。但是,您只分配 4 个字节,不足以存储该结构。接下来发生的事情是不可预测的,Windows 将读取您分配的内存,寻找 COPYDATASTRUCT.cbData 和 lpData 的值。您可能会很幸运,它读取 cbData = 0。或者不那么幸运并读取非零值。这将使其取消引用 lpData 并且几乎总是会生成 AccessViolation 异常。您可以判断何时发生这种情况,SendMessage() 返回一个值。一个你没有检查的,所以你不知道什么时候出错。

只要您想继续使用 WM_COPYDATA,您就必须为其提供适当的参数。更好的方法是使用命名管道或套接字。这也避免了使用 FindWindow(),这是一种非常不可靠的查找窗口句柄的方法。

于 2010-11-02T15:15:54.083 回答