一、发送方
User32.dll中提供了发送消息的系统API。
[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern int SendMessage(
IntPtr hWnd, //目标窗体句柄
int Msg, //WM_COPYDATA
int wParam, //自定义数值
ref CopyDataStruct lParam//传递消息的结构体
);
调用此函数可以实现进程间的通讯,但是这种方式的实现需要有一定条件的。分析函数参数可以发现,第一个参数需要传递目标窗体句柄,所以消息接收方必须是一个窗体,这样才可以有一个能被获取到的句柄。
下面进行参数分析。
(1)IntPtr hwnd 目标窗体句柄,即接收方的窗体句柄
句柄:整个Windows编程的基础。一个句柄是指使用的一个唯一的整数值,即一个4字节(64位程序中为8字节)长的数值,来标识应用程序中的不同对象和同类中的不同的实例,诸如,一个窗口,按钮,图标,滚动条,输出设备,控件或者文件等。应用程序能够通过句柄访问相应的对象的信息,但是句柄不是指针,程序不能利用句柄来直接阅读文件中的信息。如果句柄不在I/O文件中,它是毫无用处的。 句柄是Windows用来标志应用程序中建立的或是使用的唯一整数,Windows大量使用了句柄来标识对象。
User32.dll中提供了查找窗体并获取句柄的系统API。
/// <summary>
/// Find Window by Name
/// </summary>
/// <param name="lpClassName">指向一个以null结尾的字符串,指定了窗口类(一个WNDCLASS结构)的名字。如果lpszClassName为NULL,则所有的类名都匹配。</param>
/// <param name="lpWindowName">指向一个以null结尾的字符串,指定了窗口的名字(窗口的标题)。如果lpWindowName为NULL,所有的窗口名都匹配。</param>
/// <returns></returns>
[DllImport("User32.dll", EntryPoint = "FindWindow")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
具体用法如下
(2)WM_COPYDATA
消息传递时区别传递消息的类型,不同的消息类型会有不同的处理方式,常见的类型如下
#region 定义常量消息值
public const int WM_GETTEXT = 0x0D;
public const int WM_SETTEXT = 0x0C;
public const int WM_SIZEING = 0x0214;
public const int WM_COPYDATA = 0x004A;
public const int WM_LBUTTONDBLCLK = 0x0203;
#endregion
这里我们只需将WM_COPYDATA传递给形参即可。
(3)wParam自定义数值:相同的参数类型下可以根据此数值做进一步区分。
我们定义了两种消息类型,命令Command和消息Message,在发送消息时将对应消息类型传递到形参中,接收端可根据此参数的不同做处理(可见第二部分):
#region 自定义消息
public const int WM_MESSAGE = 0x0001;
public const int WM_COMMAND = 0x0002;
#endregion
(4)CopyDataStruct lParam 传递消息的结构体
此结构体的定义如下
[StructLayout(LayoutKind.Sequential)]
public struct CopyDataStruct
{
public IntPtr dwData;//用户定义的数据
public int cbData;//字符串长度
[MarshalAs(UnmanagedType.LPTStr)]
public string lpData;//字符串
}
在封装消息时,需要将消息组装到结构体中,然后将对应参数传递到函数形参中。
public static int SendMessage(string message, int wmtype, IntPtr hWnd)
{
CopyDataStruct data;
data.dwData = (IntPtr)wmtype;
data.lpData = message;
data.cbData = Encoding.UTF8.GetBytes(message).Length + 1;
return SendMessage(hWnd, WM_COPYDATA, 0, ref data);
}
二、接收方
由一可知,此时接收方是一个窗体,这里我们只讨论WPF窗体。winform更加简单,直接重写重写winform的消息处理方法WndProc即可。
而对WPF,没有这个消息处理方法,因此要利用HwndSource来接收并处理消息。
实现如下
/// <summary>
/// 重写资源初始化函数,增加HwndSource消息处理
/// </summary>
/// <param name="e"></param>
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
HwndSource hwndSource = PresentationSource.FromVisual(this) as HwndSource;
if (hwndSource != null)
{
hwndSource.AddHook(new HwndSourceHook(WndProc));
}
}
/// <summary>
/// 消息处理函数
/// </summary>
/// <param name="hwnd"></param>
/// <param name="msg"></param>
/// <param name="wParam"></param>
/// <param name="lParam"></param>
/// <param name="handled"></param>
/// <returns></returns>
IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == WM_COPYDATA)
{
CopyDataStruct cds = (CopyDataStruct)Marshal.PtrToStructure(lParam, typeof(CopyDataStruct));//从发送方接收到的数据结构
string param = cds.lpData;//获取发送方传过来的消息
switch ((int)cds.dwData)
{
case WM_COMMAND:
receive.Text = receive.Text + "\n" + "command:" + param; break;
case WM_MESSAGE:
receive.Text = receive.Text + "\n" + "message:" + param; break;
}
}
return hwnd;
}