Сообщения WM_COPYDATA не обрабатываются - PullRequest
0 голосов
/ 02 ноября 2010

Я пытаюсь установить связь между двумя приложениями C # / .NET 3.5 с помощью сообщений Windows, но сообщения, которые я отправляю, кажутся полученными некоторое время (но не всегда) - почему это происходит и как я гарантирую, что сообщения должным образом обрабатываются все время. У меня есть объект клиента следующим образом:

[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, сервер неизбежно получит их и закроет - даже когда я попытался перехватить сообщения WM_CLOSE с помощью метода WndProc, как показано выше.

Что происходит с моими сообщениями? Я особенно запутался, потому что MSDN говорит, что функция SendMessage возвращает только после обработки сообщения.

1 Ответ

1 голос
/ 02 ноября 2010

Нельзя игнорировать тот факт, что Windows хочет, чтобы LPARAM указывал на структуру COPYDATASTRUCT. Однако вы выделяете только 4 байта, что недостаточно для хранения этой структуры. Что произойдет дальше, непредсказуемо, Windows будет считывать память, выделенную вами, в поисках значений COPYDATASTRUCT.cbData и lpData. Возможно, вам повезет, и он читает cbData = 0. Или не так повезло и читает ненулевое значение. Что делает разыменование lpData и почти всегда генерирует исключение AccessViolation. Вы можете сказать, когда это происходит, SendMessage () возвращает значение. Тот, который вы не проверили, поэтому вы не знаете, когда это пойдет не так.

Пока вы хотите продолжать использовать WM_COPYDATA, у вас есть для предоставления ему правильных аргументов. Гораздо лучший подход - использовать именованные каналы или сокеты. Что также позволяет избежать использования FindWindow (), очень ненадежного способа найти дескриптор окна.

...