c#如何處理自定義消息
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, // 具體取決於消息 uintlParam // 具體取決於消息 ); [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; } } } }
參考文章
c#如何處理自定義消息
c#如何處理自定義消息