这里记录一下使用Win32 API在同一台机器上进行同进程通信、不同进程通信。
使用的API
[DllImport("User32.dll", EntryPoint = "FindWindow")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("User32.dll", EntryPoint = "FindWindowEx")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpClassName, string lpWindowName);
/// <summary>
/// 消息发送API
/// </summary>
/// <param name="hWnd">指定要接收消息的窗口的句柄</param>
/// <param name="Msg">指定被发送的消息ID</param>
/// <param name="wParam">指定附加的消息特定信息</param>
/// <param name="lParam">指定附加的消息特定信息</param>
/// <returns>返回值指定消息处理的结果,依赖于所发送的消息</returns>
[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, ref string lParam);
[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern int SendMessage(IntPtr win, int WM_COPYDATA, IntPtr wParam, ref COPYDATASTRUCT cds);
/// <summary>
/// 异步消息发送API
/// </summary>
/// <param name="hWnd">指定要接收消息的窗口的句柄</param>
/// <param name="Msg">指定被发送的消息ID</param>
/// <param name="wParam">指定附加的消息特定信息</param>
/// <param name="lParam">指定附加的消息特定信息</param>
/// <returns></returns>
[DllImport("User32.dll", EntryPoint = "PostMessage")]
public static extern int PostMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern int PostMessage(IntPtr win, int WM_COPYDATA, IntPtr wParam, ref COPYDATASTRUCT cds);
[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern int PostMessage(IntPtr win, int WM_COPYDATA, IntPtr wParam, ref string str);
/// <summary>
/// 接收信息
/// </summary>
/// <param name="hwnd">带文本的窗口或控件的句柄</param>
/// <param name="buf">指向接收文本的缓冲区的指针</param>
/// <param name="nMaxCount">指定要保存在缓冲区内的字符的最大个数,其中包含NULL字符。如果文本超过界限,它就被截断</param>
/// <returns></returns>
[DllImport("User32.Dll")]
public static extern int GetWindowText(int hwnd, StringBuilder buf, int nMaxCount);
找到本机要接收的窗体
// 不能再加上类名限定,否则会找不到
IntPtr win = Win32API.FindWindow(null,"窗体标题");
1. 同一进程
这比较简单。
- 数字:直接调用SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam),在“protected override void DefWndProc(ref Message m)”接收的时候取Message.LParam、Message.WParam,因为数字与IntPtr之间可以强转。
- 字符串:注意类型的转换
// 发
Win32API.PostMessage(win,0x890,IntPtr.Zero,Marshal.StringToHGlobalAnsi(str));
// 收
string str = Marshal.PtrToStringAnsi(m.LParam);
2. 不同进程
- 数字:与上面一致。
- 字符串:比较麻烦
- 方法1
自定义结构体,注意该方法必须使用WM_COPYDATA 参数,就是说不能自定义消息号了。可能会觉得这里使用结构体多此一举,之所以不直接使用string,是因为在使用同样的方式接收数据的时候,会报错“string没有提供无参的构造函数”,struct有默认的无参构造函数。 这个方法的问题在于:如果是跟第三方的API在进行通信的时候,你不能要求别人也必须按照你的struct来传参,所以只有在接收、发送都是自己的程序的时候,以及在协同开发有约定类型、第三方有指定类型的时候,才适用。
public struct COPYDATASTRUCT { public IntPtr dwData; public int cbData; [MarshalAs(UnmanagedType.LPStr)] public string lpData; } const int WM_COPYDATA = 0x004A; // 发 string str = "test"; byte[] sarr = System.Text.Encoding.Default.GetBytes(str); int len = sarr.Length; COPYDATASTRUCT cds; cds.dwData = (IntPtr)Convert.ToInt16(123);//可以是任意值 cds.cbData = len + 1;//指定lpData内存区域的字节数 cds.lpData = str;//发送给目标窗口所在进程的数据 Win32API.PostMessage(win, WM_COPYDATA, IntPtr.Zero, ref cds); // 收 protected override void DefWndProc(ref Message m) { switch (m.Msg) { case WM_COPYDATA: COPYDATASTRUCT cds = new COPYDATASTRUCT(); Type t = cds.GetType(); cds = (COPYDATASTRUCT)m.GetLParam(t); string strResult = cds.dwData.ToString() + ":" + cds.lpData; MessageBox.Show(strResult); break; default: base.DefWndProc(ref m); break; } }
- 方法2
传递控件句柄,通过GetWindowText来接数据。这里不需要再封装struct,可以自定义消息号,但需要有控件的Handle来作为参数,而且只能传递字符串。
// 发送:0x890是自定义的消息号,btn_Send是界面的一个控件,这会将该控件的Text值发送出去 Win32API.PostMessage(win, 0x890, this.btn_Send.Handle, IntPtr.Zero); // 接收 protected override void DefWndProc(ref Message m) { StringBuilder sb = new StringBuilder(1024 * 2); switch (m.Msg) { case 0x890: Win32API.GetWindowText(m.WParam.ToInt32(), sb, sb.Capacity); MessageBox.Show(sb.ToString()); break; default: base.DefWndProc(ref m); break; } }
- 方法1