0

Windows メッセージを使用して 2 つの 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 を介してメッセージ タイプ コードを送信する必要があります。単一の整数を送信する場合は、Copydatastruct ではなく lparam を介して整数を送信する必要があります)。サーバー アプリケーションのメイン フォームには、次のようにオーバーライドされた 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 に答える