WM_COPYDATA实现的不同进程间通信

进程间通信,通过SendMessage向另一进程发送WM_COPYDATA消息,实现不同进程间的消息通信。
需求:已写好一个工具软件,想在不更改当前的软件开发的前提下,实现为后面新开发的软件提供数据推送任务。原先想到使用,WCF以实现通信级别的调用,但对于后续新开发的软件来说,所需实现的东西太多(相当于需要实现一个既定接口的服务端)。所以选择使用SendMessage,发送一个WM_COPYDATA以实现对新软件的通知任务。其中主要是需要对传输一个对象级的处理,需要进行序列化及反序列货处理,这是比较重要的一点。其他方面都比较简单,只是单独发一条WM_COPYDATA消息。

一、找到新软件需被通知的窗口(一般是以配置的形式实现)

  而在原有软件(即主消息推送方)通过既定的扩展软件名称通过:FindWindow函数进行查找对象句柄,以待后续发送消息。

二、发送消息

  需要创建WM_COPYDATA消息所需的传参结构,默认情况下可以传递字符串,所以如只单独传输字符串,那就没什么问题的,直接简单调用即可。但在此,需要传输的为对象,所以,需要将对象先进行一系列处理,以便格式化为字符串进行传输。在接收方再以逆运算得出传输过来的对象。

三、将对象序列化为字符串及反序列化的操作,通过一个帮助类实现

Demo代码:

传输类:
 1 using System;
 2 using System.Collections.Generic;
 3 using System.IO;
 4 using System.Linq;
 5 using System.Runtime.InteropServices;
 6 using System.Runtime.Serialization.Formatters.Binary;
 7 using System.Text;
 8 using System.Threading.Tasks;
 9 namespace Dralee.AppTransferMsg.Common
10 {
11     [Serializable]
12     public class Msg
13     {
14         public string SentBy { get; set; }
15         public string RcvBy { get; set; }
16         public string Message { get; set; }
17         public DateTime SentOn { get; set; }
18         public Msg()
19         {
20         }
21         public Msg(string sentBy, string rcvBy, string msg, DateTime sentOn)
22         {
23             SentBy = sentBy;
24             RcvBy = rcvBy;
25             Message = msg;
26             SentOn = sentOn;
27         }
28         public static byte[] Serialize(Msg msg)
29         {
30             MemoryStream ms = new MemoryStream();
31             BinaryFormatter bf = new BinaryFormatter();
32             bf.Serialize(ms, msg);
33             byte[] buffer = ms.GetBuffer();
34             ms.Close();
35             return buffer;
36             //return Encoding.Unicode.GetString(buffer);
37         }
38         public static Msg Deserialize(byte[] buffer)
39         {
40             MemoryStream ms = new MemoryStream(buffer);
41             BinaryFormatter bf = new BinaryFormatter();
42             Msg msg = (Msg)bf.Deserialize(ms);
43             return msg;
44         }
45     }
46     /// <summary>
47     /// 传输结构
48     /// </summary>
49     public struct CopyDataStruct
50     {
51         public IntPtr dwData;
52         public int cbData;
53         [MarshalAs(UnmanagedType.LPStr)]
54         public string lpData;
55     }
56 }
View Code

 转换帮助类:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 namespace Dralee.AppTransferMsg.Common
 7 {
 8     // CreatedBy: Jackie Lee
 9     // CreatedOn: 2016-08-17
10     /// <summary>
11     /// 字节数组与字符串互转
12     /// </summary>
13     public class HexConverter
14     {
15         /// <summary>
16         /// 字节数组转
17         /// </summary>
18         /// <param name="buffer"></param>
19         /// <returns></returns>
20         public string ByteToString(byte[] buffer)
21         {
22             StringBuilder sb = new StringBuilder();
23             for(int i = 0; i < buffer.Length; ++i)
24             {
25                 sb.AppendFormat("{0:X2}", buffer[i]);
26             }
27             return sb.ToString();
28         }
29         /// <summary>
30         /// 字符串转字节数组
31         /// </summary>
32         /// <param name="str"></param>
33         /// <returns></returns>
34         public byte[] StringToByte(string str)
35         {
36             if(string.IsNullOrEmpty(str) || str.Length % 2 != 0)
37             {
38                 return null;
39             }
40             byte[] buffer = new byte[str.Length / 2];
41             string hex;
42             int j = 0;
43             for(int i = 0; i < buffer.Length;++i)
44             {
45                 hex = new string(new char[] { str[j], str[j + 1]});
46                 buffer[i] = HexToByte(hex);
47                 j += 2;
48             }
49             return buffer;
50         }
51         /// <summary>
52         /// 双字节字符转byte
53         /// </summary>
54         /// <param name="hex"></param>
55         /// <returns></returns>
56         public byte HexToByte(string hex)
57         {
58             if (hex.Length > 2)
59                 return 0;
60             char[] hexs = hex.ToArray();
61             if(hexs.Length == 1)
62             {
63                 return (byte)NumByChar(hexs[0]);
64             }
65             else
66             {
67                 return (byte)(NumByChar(hexs[0]) * 16 + NumByChar(hexs[1]));
68             }
69         }
70         private byte NumByChar(char ch)
71         {
72             switch(ch)
73             {
74                 case '0': return 0;
75                 case '1': return 1;
76                 case '2': return 2;
77                 case '3': return 3;
78                 case '4': return 4;
79                 case '5': return 5;
80                 case '6': return 6;
81                 case '7': return 7;
82                 case '8': return 8;
83                 case '9': return 9;
84                 case 'A': return 10;
85                 case 'B': return 11;
86                 case 'C': return 12;
87                 case 'D': return 13;
88                 case 'E': return 14;
89                 case 'F': return 15;
90                 default:
91                     return 0;
92             }
93         }
94     }
95 }
View Code

API类:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Runtime.InteropServices;
 5 using System.Text;
 6 using System.Threading.Tasks;
 7 namespace Dralee.AppTransferMsg.Common
 8 {
 9     public static class API
10     {
11         public const int WM_COPYDATA = 0x004A;
12         [DllImport("User32.dll", EntryPoint = "FindWindow")]
13         public static extern int FindWindow(string lpClassName, string lpWindowName);
14         /// <summary>
15         /// 发送消息
16         /// </summary>
17         /// <param name="hwnd">目标句柄</param>
18         /// <param name="msg"></param>
19         /// <param name="wParam">参数1</param>
20         /// <param name="lParam">参数2</param>
21         /// <returns></returns>
22         [DllImport("User32.dll", EntryPoint = "SendMessage")]
23         public static extern int SendMessage(int hwnd, int msg, int wParam, ref CopyDataStruct lParam);
24     }
25 }
View Code

发送端:

 1 private void btnSend_Click(object sender, EventArgs e)
 2 {
 3     Msg msg = new Msg { SentBy = txtSentBy.Text.Trim(), RcvBy = txtRcvBy.Text.Trim(), Message = txtMsg.Text.Trim(), SentOn = DateTime.Parse(txtSentOn.Text) };
 4     //long size = GC.GetTotalMemory(true);
 5     int hwnd = API.FindWindow(null, "(测试)->接收者");
 6     if (hwnd == 0)
 7         return;
 8     string msgStr = new HexConverter().ByteToString(Msg.Serialize(msg));
 9     CopyDataStruct cds;
10     cds.dwData = (IntPtr)100;
11     cds.cbData = (int)msgStr.Length + 1;
12     cds.lpData = msgStr;
13             
14     API.SendMessage(hwnd, API.WM_COPYDATA, 0,ref cds);
15 }
View Code

接收端,通过重写以实现对消息监控:

 1 protected override void DefWndProc(ref Message m)
 2 {
 3     switch(m.Msg)
 4     {
 5         case API.WM_COPYDATA:
 6             CopyDataStruct cds = (CopyDataStruct)m.GetLParam(typeof(CopyDataStruct));
 7             //Msg msg = (Msg)m.GetLParam(typeof(Msg));
 8             byte[] buffer = new HexConverter().StringToByte(cds.lpData);
 9             Msg msg = Msg.Deserialize(buffer);
10             txtMsg.Text = msg.Message;
11             txtRcvBy.Text = msg.RcvBy;
12             txtSentBy.Text = msg.SentBy;
13             txtSentOn.Text = msg.SentOn.ToLongDateString();
14             break;
15         default:
16             base.DefWndProc(ref m);
17             break;
18     }
19 }
View Code

效果:

posted @ 2016-08-22 17:13  遊龍  阅读(480)  评论(0编辑  收藏  举报