C#自定义消息通信往往采用事件驱动的方式实现,但有时候我们不得不采用操作系统的消息通信机制,例如在和底层语言开发的DLL交互时,是比较方便的。下面列举了一些实现方式,以供参考。有关C#中的消息机制,请参考文章C# 消息处理机制及自定义过滤方式。
一、通过SendMessage或postmessage函数发送
1、 定义消息
在C++中引用底层的函数很简单,自定义消息如下
#define WM_TEST WM_USER + 101
而在c#中消息需要定义成windows系统中的原始的16进制数字,比如自定义消息
public const int USER = 0x0400;
public const int WM_TEST =USER+101;
2、 发送消息
消息发送是通过windows提供的API函数SendMessage或postmessage来实现的,它的原型定义:
[DllImport("User32.dll",EntryPoint="SendMessage")] private static extern int SendMessage( IntPtr hWnd, // 窗体句柄 uint Msg, // 消息的标识符 uint wParam, // 具体取决于消息 uint lParam // 具体取决于消息 ); [DllImport("User32.dll",EntryPoint="PostMessage")] private static extern int SendMessage( IntPtr hWnd, // 接收消息的那个窗口的句柄。如设为HWND_BROADCAST,表示投递给系统中的所有顶级窗口。如设为零,表示投递一条线程消息(可参考PostThreadMessage) uint Msg, // 消息的标识符 uint wParam, // 具体取决于消息 uint lParam // 具体取决于消息 );
至于两个函数的区别这里就不累述了,有兴趣的朋友可以自己查阅资料。
3、 消息接收
消息发出之后,在Form中如何接收呢?我们可以重载DefWinproc函数来接收消息。
protected override void DefWndProc ( ref System.Windows.Forms.Message m ) { switch(m.Msg) { case Message.WM_TEST: //处理消息 break; default: base.DefWndProc(ref m); //调用基类函数处理非自定义消息。 break; } }
二、使用PostThreadMessage函数向线程发送消息
1、映射消息结构体原型和自定义消息
public struct tagMSG
{
public int hwnd;
public uint message;
public int wParam;
public long lParam;
public uint time;
public int pt;
}
public const int WM_CX_NULL = 0x400 + 100;
2、发送消息
[DllImport("user32.dll")] private static extern int PostThreadMessage( int threadId, //线程标识 uint msg, //消息标识符 int wParam, //具体由消息决定 int lParam); //具体由消息决定
此函数获取当前线程一个唯一的线程标识符,这点需要特别注意:Win32 API无法识别管理线程,你必须发送消息到Windows的线程ID上,而不是管理线程的ID上。
[DllImport("kernel32.dll")]
private static extern int GetCurrentThreadId();
因此发送消息过程如下:
private int _NewThreadId =GetCurrentThreadId();
PostThreadMessage(_NewThreadId, WM_CX_NULL, 1, 1);
3、接收消息
该函数从调用线程的消息队列里取得一个消息并将其放于指定的结构。此函数可取得与指定窗口联系的消息和由PostThreadMesssge寄送的线程消息。此函数接收一定范围的消息值。GetMessage不接收属于其他线程或应用程序的消息。
[DllImport("user32.dll")] private static extern int GetMessage( ref tagMSG lpMsg, //指向MSG结构的指针,该结构从线程的消息队列里接收消息信息; int hwnd, //取得其消息的窗口的句柄。这是一个有特殊含义的值(NULL)。GetMessage为任何属于调用线程的窗口检索消息; int wMsgFilterMin, //指定被检索的最小消息值的整数 int wMsgFilterMax); //指定被检索的最大消息值的整数
接收实现如下:
public void ThreadExectue() { _NewThreadId= GetCurrentThreadId(); //发送线程和接收线程一定要是同一个线程,否则接收不到消息 while (GetMessage(ref msg, 0, 0, 0) > 0) { if (msg.message == WM_CX_NULL) { MessageBox.Show("消息收到!"); } } }
三、拦截系统消息
使用 Application.AddMessageFilter。
1、实现消息过滤器接口
internal class MyMessager : IMessageFilter { //截取消息,进行处理 public bool PreFilterMessage(ref System.Windows.Forms.Message m) { switch (m.Msg) { case CUSTOM_MESSAGE: //拦截自定义消息 MessageBox.Show("消息收到!"); return true; default: return false; //返回false则消息未被裁取,系统会处理 } } }
2、安装消息过滤器
private void Form1_Load(object sender, EventArgs e) { Application.AddMessageFilter(new MyMessager()); }
C#简单实现自定义消息的发送和接收示例:
//=================================发送窗口代码============================= using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; //手动加入的命名空间 using System.Runtime.InteropServices; namespace SendCustomMessage { public partial class SendForm. Form { public SendForm(IntPtr Handle) { SendToHandle = Handle; InitializeComponent(); } private IntPtr SendToHandle;//这个变量用于保存要发送窗口的句柄 //自定义的消息 public const int USER = 0x500; public const int MYMESSAGE=USER + 1; //消息发送API [DllImport("User32.dll", EntryPoint = "SendMessage")] private static extern int SendMessage( IntPtr hWnd, // 信息发住的窗口的句柄 int Msg, // 消息ID int wParam, // 参数1 ref SENDDATASTRUCT lParam // 参数2 [MarshalAs(UnmanagedType.LPTStr)]StringBuilder lParam ); //发关按钮 private void Send_Click(object sender, EventArgs e) { string myText = textBox1.Text; byte[] myInfo = System.Text.Encoding.Default.GetBytes(myText); int len = myInfo.Length; SENDDATASTRUCT myData; myData.dwData = (IntPtr)100; myData.lpData = myText; myData.DataLength = len + 1; SendMessage(SendToHandle, MYMESSAGE, 100, ref myData);//发送自定义消息给句柄为SendToHandle 的窗口, //本例为创建本窗口的窗口句,创建时,传递给本窗口的构造函数 } } //要发信息数据结构,作为SendMessage函数的LParam参数 public struct SENDDATASTRUCT { public IntPtr dwData; //附加一些个人自定义标志信息,自己喜欢 public int DataLength; //信息的长度 [MarshalAs(UnmanagedType.LPStr)] public string lpData; //要发送的信息 } } //=============================接收窗口代码==================================== using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; /* 制作人:林龙江 制作时间:2007年5月1日 只供参考,有错误之处请指出 ! */ //手动加入的命名空间 using System.Runtime.InteropServices; namespace SendCustomMessage { public partial class Form1 : Form { public Form1() { InitializeComponent(); sendForm. = new SendForm(this.Handle); sendForm.Show(); } SendCustomMessage.SendForm. sendForm; //自定义消息 public const int USER = 0x500; public const int MYMESSAGE = USER + 1; ///重写窗体的消息处理函数DefWndProc,从中加入自己定义消息 MYMESSAGE 的检测的处理入口 protected override void DefWndProc(ref Message m) { switch (m.Msg) { //接收自定义消息MYMESSAGE,并显示其参数 case MYMESSAGE: SendCustomMessage.SENDDATASTRUCT myData = new SendCustomMessage.SENDDATASTRUCT();//这是创建自定义信息的结构 Type mytype = myData.GetType(); myData = (SendCustomMessage.SENDDATASTRUCT)m.GetLParam(mytype);//这里获取的就是作为LParam参数发送来的信息的结构 textBox1.Text = myData.lpData; //显示收到的自定义信息 break; default: base.DefWndProc(ref m); break; } } } }
参考文章