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、實現訊息過濾器介面
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());
}