.Net中封装Windows 消息实现进程间通讯

      .NET Framework 中对进程间的通讯支持不多,不过Windows API 已经为我们提供了丰富的进程间通讯的特性。我们可以使用Windows API SendMessage、PostMessage来实现windows 窗体之间的通讯。本文就是将SendMessage封装在一个窗体基类WinMsgData中,把它变成类中的一个方法以方便调用,而接收其他窗体的消息则封装成事件通知的形式提供。其中还对接收到的消息做队列处理,避免对消息发送方造成阻塞。所以只要程序中的WinForm从这个基类继承,就可以很方便的与其他的窗体进行通讯了。

下面看看具体的实现吧。
首先在窗体的构造函数中,我使用RegisterWindowMessage为希望通讯的窗体在系统中注册一个消息值。然后执行一个“Connect”的动作,就是使用PostMessage做广播,看看是否有“志同道合”者。代码如下:

  1.         public event WindowsMessageHandler OnMessage;
  2.         public delegate void WindowsMessageHandler(object sender, 
  3.                                                          WindowsMessageEventArgs e);
  4.         private const string DefaultAttachWindowMessage 
  5.                                                             = "Peanut.DefaultAttachWinMsgString";
  6.         private bool connected = false;
  7.         private int attachMessage;
  8.         private int thisHandle;
  9.         private string attachMessageString;
  10.         private List<IntPtr> AttachWindows;
  11.         private Queue<WindowsMessageEventArgs> incomingMessageQueue 
  12.                                        = new Queue<WindowsMessageEventArgs>();
  13. public WinMsgData() : this (DefaultAttachWindowMessage){}
  14.         public WinMsgData(string attachMessageString)
  15.         {
  16.              this.thisHandle = this.Handle.ToInt32();
  17.             AttachWindows = new List<IntPtr>();
  18.             this.attachMessageString = attachMessageString;
  19.             this.attachMessage = User32.RegisterWindowMessage(attachMessageString);
  20.             Connect();//“连接”
  21.         }
  22.         private void Connect()
  23.         {
  24.             if (User32.PostMessage(User32.HWND_BROADCAST, 
  25.                                  attachMessage, thisHandle ,1) == 0)//连接
  26.                 throw new Exception("PostMessage failed.");
  27.             else
  28.                 connected = true;
  29.         }

调用到Windows API代码如下:
具体如何调用Windows 的API请看Calling Win32 DLLs in C# with P/Invoke

  1. public struct COPYDATASTRUCT
  2.     {
  3.         public IntPtr dwData;
  4.         public int cbData;
  5.         [MarshalAs(UnmanagedType.LPStr)]
  6.         public string lpData;
  7.     }
  8.     public class User32
  9.     {
  10.         public const int WM_COPYDATA = 0x4A;
  11.         public const int WM_DESTROY = 0x0002;
  12.         public const int WM_QUERYENDSESSION = 0x0011;
  13.         public const int WM_QUEUE_NOTIFY = 0x401;
  14.         [DllImport("user32")]
  15.         public static extern int PostMessage(IntPtr hwnd, int wMsg, int wParam, int lParam);
  16.         [DllImport("user32")]
  17.         public static extern int RegisterWindowMessage(string lpString);
  18.         [DllImport("user32")]
  19.         public static extern int SendMessage(IntPtr hWnd, int Msg, 
  20.                        int wParam, ref COPYDATASTRUCT lParam);
  21.         public static IntPtr HWND_BROADCAST
  22.         {
  23.             get { return (IntPtr)0xFFFF; }
  24.         }
  25.     }

 

然后就要重写WndProc来接收处理窗体消息了:

  1.  protected override void WndProc(ref Message m)
  2.         {
  3.             if (m.Msg == attachMessage &
  4.                 m.WParam.ToInt32() != 0 &
  5.                 m.WParam.ToInt32() != thisHandle )
  6.             {
  7.                 if (m.LParam.ToInt32() == 1)
  8.                     User32.PostMessage(m.WParam, attachMessage, thisHandle, 0);//回复
  9.                 if (m.LParam.ToInt32() == -1)
  10.                     AttachWindows.Remove(m.WParam);//删除窗体句柄
  11.                 else //0,1
  12.                     AttachWindows.Add(m.WParam);//保存窗体句柄
  13.             }
  14.             switch (m.Msg)
  15.             {
  16.                 case User32.WM_COPYDATA:
  17.                     COPYDATASTRUCT copyData = new COPYDATASTRUCT();
  18.                     copyData = (COPYDATASTRUCT)m.GetLParam(copyData.GetType());
  19.                     MessageNotify(m.WParam.ToInt32(), copyData.lpData);
  20.                     break;
  21.                 case User32.WM_QUEUE_NOTIFY:
  22.                     if (incomingMessageQueue.Count == 0)
  23.                         break;
  24.                     WindowsMessageEventArgs tempArgs = incomingMessageQueue.Dequeue();
  25.                     if (OnMessage != null)
  26.                         OnMessage(thisHandle, tempArgs);
  27.                     if (incomingMessageQueue.Count > 0)
  28.                         User32.PostMessage(this.Handle, User32.WM_QUEUE_NOTIFY, 0, 0); //下一个
  29.                     break;
  30.                 case User32.WM_QUERYENDSESSION:
  31.                 case User32.WM_DESTROY:
  32.                     User32.PostMessage(User32.HWND_BROADCAST, 
  33.                                   attachMessage, thisHandle, -1);//让其他人删除别再给我发消息
  34.                     break;
  35.             }
  36.             base.WndProc(ref m);
  37.         }

 

假如已经有窗体进程已经在运行,那么它就会收到新窗体起来时是所发送的“Connect”消息。上面的代码中第一个if就时处理这个消息的。首先会给消息发送者一个响应。然后会将发送者(窗体的句柄)保存到的列表AttachWindows中。
窗体后面部分就是真正处理通讯消息的部分了。MessageNotify方法首先将消息入队,接着给自己发送一个PostMessage通知自己处理,然后就马上返回。大家可能已经注意到WM_QUEUE_NOTIFY了,不错,这个就是通知自己处理消息的。“处理消息”的就是从消息队列中取出一个消息,将消息作为事件参数触发事件。子窗体只要订阅这个事件,并做处理即可。

上面已经把处理消息的过程讲完了,但还没看到发送消息的部分。下面马上给出。
还是先看代码吧,下面的代码是上面提到的MessageNotify方法及发送消息的方法SendMessage();

 

 

  1. private void MessageNotify(int target, string message)
  2.         {
  3.             incomingMessageQueue.Enqueue(new WindowsMessageEventArgs(target, message));
  4.             User32.PostMessage(this.Handle, User32.WM_QUEUE_NOTIFY, 0, 0);
  5.         } 
  6. public void SendMessage(int tarWin, string message)
  7.         {
  8.             if (!connected)
  9.                 throw new Exception("not connected!");
  10.             byte[] bytes = System.Text.Encoding.UTF8.GetBytes(message);
  11.             int length = bytes.Length;
  12.             COPYDATASTRUCT copyData = new COPYDATASTRUCT();
  13.             copyData.dwData = (IntPtr)1;
  14.             copyData.lpData = message;
  15.             copyData.cbData = length + 1;
  16.             System.Threading.Thread.Sleep(100);
  17.             try
  18.             {
  19.                 User32.SendMessage((IntPtr)tarWin, User32.WM_COPYDATA, thisHandle, ref copyData);
  20.             }
  21.             catch (Exception e)
  22.             {
  23.                 throw new Exception("windows send message error", e);
  24.             }
  25.         }

还有消息的参数类:

  1. public class WindowsMessageEventArgs : EventArgs
  2.     {
  3.         [ ==== Fields ==== ]#region [ ==== Fields ==== ]
  4.         private int targetWinHandle;
  5.         private string data; 
  6.         #endregion
  7.         public WindowsMessageEventArgs(int targetWinHandle, string data)
  8.         {
  9.             this.targetWinHandle = targetWinHandle;
  10.             this.data = data;
  11.         }
  12.         [ ==== Properties ==== ]#region [ ==== Properties ==== ]
  13.         /**//// <summary>
  14.         /// 消息发送者窗体句柄值
  15.         /// </summary>
  16.         public int TargetWinHandle
  17.         {
  18.             get { return targetWinHandle; }
  19.         }
  20.         public string Data
  21.         {
  22.             get { return data; }
  23.         } 
  24.         #endregion

子窗体只要从WinMsgData继承,并订阅消息事件:
base.OnMessage += new WinMsgData.WindowsMessageHandler(HandleMyMessage);
要发送消息的时候调用base.SendMessage(yourMessageToSend)就可以了。使用方便。
效果图:


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值