C# 使用 Windows API 操作控件: SendMessage
在C#中,程序采用了的驅(qū)動(dòng)采用了事件驅(qū)動(dòng)而不是原來(lái)的消息驅(qū)動(dòng),雖然.net框架提供的事件已經(jīng)十分豐富,但是在以前的系統(tǒng)中定義了豐富的消息對(duì)系統(tǒng)的編程提供了方便的實(shí)現(xiàn)方法,因此在C#中使用消息有時(shí)候還是大大提高編程的效率的。
定義消息
在c#中消息需要定義成windows系統(tǒng)中的原始的6進(jìn)制數(shù)字,比如
const int WM_Lbutton = 0x0; //定義了鼠標(biāo)的左鍵點(diǎn)擊消息
public const int USER = 0x000 // 是windows系統(tǒng)定義的用戶消息
消息發(fā)送
消息發(fā)送是通過(guò)windows提供的API函數(shù)SendMessage來(lái)實(shí)現(xiàn)的它的原型定義為
消息的接受
在C#中,任何一個(gè)窗口都有也消息的接收處理函數(shù),就是defproc函數(shù)
你可以在form中重載該函數(shù)來(lái)處理消息
其實(shí),C#中的事件也是通過(guò)封裝系統(tǒng)消息來(lái)實(shí)現(xiàn)的,如果你在DefWndProc函數(shù)中不處理該
那么,他會(huì)交給系統(tǒng)來(lái)處理該消息,系統(tǒng)便會(huì)通過(guò)代理來(lái)實(shí)現(xiàn)鼠標(biāo)單擊的處理函數(shù),因此你可以通過(guò)
defproc函數(shù)來(lái)攔截消息,比如你想攔截某個(gè)按鈕的單擊消息
C#中其他的消息處理方法
在C#中有的時(shí)候需要對(duì)控件的消息進(jìn)行預(yù)處理,比如你用owc的spreedsheet控件來(lái)處理Excel文件,你不想讓用戶可以隨便選中
數(shù)據(jù)進(jìn)行編輯,你就可以屏蔽掉鼠標(biāo)事件,這個(gè)時(shí)候就必須攔截系統(tǒng)預(yù)先定義好的事件(這在MFC中稱為子類(lèi)化),你可以通過(guò)C#提供的一個(gè)接口
IMessageFilter來(lái)實(shí)現(xiàn)消息的過(guò)濾
備注:主要描述在調(diào)用API函數(shù)SendMessage時(shí)數(shù)據(jù)類(lèi)型的轉(zhuǎn)換。
SendMessage是一個(gè)在user32.dll中聲明的API函數(shù),在C#中導(dǎo)入如下:
using System.Runtime.InteropServices; [DllImport("user32.dll", EntryPoint="SendMessageA")] public static extern int SendMessage (IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);?
本文描述其參數(shù) lParam 的用法,主要是數(shù)據(jù)類(lèi)型之間的轉(zhuǎn)化。
● 一種最簡(jiǎn)單的處理方式是聲明多個(gè)SendMessage函數(shù)(overload),用所需的數(shù)據(jù)類(lèi)型直接替換IntPtr。例如:
//聲明: [DllImport("user32.dll", EntryPoint="SendMessageA")] private static extern int SendMessage (IntPtr hwnd, int wMsg, IntPtr wParam, string lParam);[DllImport("user32.dll", EntryPoint="SendMessageA")] private static extern int SendMessage (IntPtr hwnd, int wMsg, IntPtr wParam, ref Rectangle lParam);//調(diào)用: string s = "hello, floodzhu"; SendMessage(this.textBox1.Handle, WM_SETTEXT, IntPtr.Zero, s);Rectangle rect = new Rectangle(); SendMessage(this.richTextBox1.Handle, EM_GETRECT, (IntPtr)0, ref rect);● 對(duì)要求返回字符串的類(lèi)型(out string)可以用 StringBuilder 代替,此時(shí)不需要 out/ref。例如:
[DllImport("user32.dll", EntryPoint="SendMessageA")] private static extern int SendMessage (IntPtr hwnd, int wMsg, int wParam, StringBuilder lParam);private void button1_Click(object sender, System.EventArgs e) {const int buffer_size = 1024;StringBuilder buffer = new StringBuilder(buffer_size);SendMessage(this.textBox1.Handle, WM_GETTEXT, buffer_size, buffer);//MessageBox.Show(buffer.ToString()); }● 如果想用 InPtr 類(lèi)型統(tǒng)一處理的話,可以借助于 Marshal 或者 GCHandle 的相關(guān)方法。例如:
[DllImport("user32.dll", EntryPoint="SendMessageA")] private static extern int SendMessage (IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);private void button2_Click(object sender, System.EventArgs e) {Rectangle rect = new Rectangle();IntPtr buffer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Rectangle)));Marshal.StructureToPtr(rect, buffer ,true);SendMessage(this.richTextBox1.Handle, EM_GETRECT, (IntPtr)0, buffer);rect = (Rectangle)Marshal.PtrToStructure(buffer, typeof(Rectangle));Marshal.FreeHGlobal(buffer); }或者
private void button2_Click(object sender, System.EventArgs e) {Rectangle rect = new Rectangle();GCHandle gch = GCHandle.Alloc(rect);SendMessage(this.richTextBox1.Handle, EM_GETRECT, (IntPtr)0, (IntPtr)gch);rect = (Rectangle)Marshal.PtrToStructure((IntPtr)gch, typeof(Rectangle));gch.Free(); } SendMessage-------PostMessage 1、首先是返回值意義的區(qū)別,我們先看一下 MSDN 里的聲明: LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam);BOOL PostMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam); 1.其中 4 個(gè)參數(shù)的意義是一樣的,返回值類(lèi)型不同(其實(shí)從數(shù)據(jù)上看他們一樣是一個(gè) 32 位的數(shù),只是意義不一樣),LRESULT 表示的是消息被處理后的返回值,BOOL 表示的是消息是不是 Post 成功。2、PostMessage 是異步的,SendMessage 是同步的。 PostMessage 只把消息放入隊(duì)列,不管消息是否被處理就返回,消息可能不被處理;而 SendMessage 等待消息被處理完了之后才返回,如果消息不被處理,發(fā)送消息的線程將一直被阻塞。3、如果在同一個(gè)線程內(nèi),SendMessage 發(fā)送消息時(shí),由 USER32.DLL 模塊調(diào)用目標(biāo)窗口的消息處理程序,并將結(jié)果返回。SendMessage 在同一線程中發(fā)送消息并不入線程消息隊(duì)列。PostMessage 發(fā)送消息時(shí),消息要先放入線程的消息隊(duì)列,然后通過(guò)消息循環(huán)分派到目標(biāo)窗口(DispatchMessage)。如果在不同線程內(nèi),SendMessage 發(fā)送消息到目標(biāo)窗口所屬線程的消息隊(duì)列,然后發(fā)送消息的線程在 USER32.DLL 模塊內(nèi)監(jiān)視和等待消息處理,直到目標(biāo)窗口處理完返回。SendMessage 在返回前還做了很多工作,比如,響應(yīng)別的線程向它 SendMessage。Post 到別的線程時(shí),最好用 PostThreadMessage 代替 PostMessage,PostMessage 的 hWnd 參數(shù)可以是 NULL,等效于 PostThreadMessage + GetCurrentThreadId。Post WM_QUIT 時(shí),應(yīng)使用 PostQuitMessage 代替。4、系統(tǒng)只整編(marshal)系統(tǒng)消息(0 到 WM_USER 之間的消息),發(fā)送用戶消息(WM_USER 以上)到別的進(jìn)程時(shí),需要自己做整編。用 PostMessage、SendNotifyMessage、SendMessageCallback 等異步函數(shù)發(fā)送系統(tǒng)消息時(shí),參數(shù)里不可以使用指針,因?yàn)榘l(fā)送者并不等待消息的處理就返回,接受者還沒(méi)處理指針就已經(jīng)被釋放了。5、在 Windows 2000/XP 里,每個(gè)消息隊(duì)列最多只能存放 10,000 個(gè) Post的消息,超過(guò)的還沒(méi)被處理的將不會(huì)被處理,直接丟掉。這個(gè)值可以改得更大:[HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Windows NT/CurrentVersion/Windows] USERPostMessageLimit,最小可以是4000。PostMessage只負(fù)責(zé)將消息放到消息隊(duì)列中,不確定何時(shí)及是否處理 SendMessage要等到受到消息處理的返回碼(DWord類(lèi)型)后才繼續(xù) PostMessage執(zhí)行后馬上返回 SendMessage必須等到消息被處理后才會(huì)返回。總結(jié)
以上是生活随笔為你收集整理的C# 使用 Windows API 操作控件: SendMessage的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: PPT插入内嵌视频
- 下一篇: C# : 调用C++动态库(dll)