C#自定义消息通信往往采用事件驱动的方式实现,但有时候我们不得不采用操作系统的消息通信机制,例如在和底层语言开发的DLL交互时,是比较方便的。下面列举了一些实现方式,供大家参考:
一、通过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、实现消息过滤器接口
{
//截取消息,进行处理
public bool PreFilterMessage(ref System.Windows.Forms.Message m)
{
switch (m.Msg)
{
case CUSTOM_MESSAGE: //拦截自定义消息
MessageBox.Show("消息收到!");
return true;
return false; //返回false则消息未被裁取,系统会处理
}
}
}
{
Application.AddMessageFilter(new MyMessager());
}