SendMessage和PostMessage的用法


定義消息



在c#中消息需要定義成windows系統中的原始的6進制數字,比如



const int WM_Lbutton = 0x0; //定義了鼠標的左鍵點擊消息



public const int USER = 0x000 // 是windows系統定義的用戶消息



消息發送



消息發送是通過windows提供的API函數SendMessage來實現的它的原型定義爲



[DllImport("User.dll",EntryPoint="SendMessage")]

private static extern int SendMessage(

int hWnd,   // handle to destination window

int Msg,    // message

int wParam, // first message parameter

int lParam // second message parameter

);



消息的接受



在C#中,任何一個窗口都有也消息的接收處理函數,就是defproc函數



你可以在form中重載該函數來處理消息



protected override void DefWndProc ( ref System.WinForms.Message m )

{

switch(m.msg)

{

case WM_Lbutton :

///string與MFC中的CString的Format函數的使用方法有所不同

string message = string.Format("收到消息!參數爲:{0},{}",m.wParam,m.lParam);

MessageBox.Show(message);///顯示一個消息框

break;

default:

base.DefWndProc(ref m);///調用基類函數處理非自定義消息。

break;

}

}

其實,C#中的事件也是通過封裝系統消息來實現的,如果你在DefWndProc函數中不處理該

那麼,他會交給系統來處理該消息,系統便會通過代理來實現鼠標單擊的處理函數,因此你可以通過

defproc函數來攔截消息,比如你想攔截某個按鈕的單擊消息



C#中其他的消息處理方法

在C#中有的時候需要對控件的消息進行預處理,比如你用owc的spreedsheet控件來處理Excel文件,你不想讓用戶可以隨便選中

數據進行編輯,你就可以屏蔽掉鼠標事件,這個時候就必須攔截系統預先定義好的事件(這在MFC中稱爲子類化),你可以通過C#提供的一個接口

IMessageFilter來實現消息的過濾

public class Form: System.Windows.Forms.Form,IMessageFilter

{

const int WM_MOUSEMOVE = 0x00

public bool PreFilterMessage(ref Message m) 

{  Keys keyCode = (Keys)(int)m.WParam & Keys.KeyCode; 

if(m.Msg == m.Msg==WM_MOUSEMOVE) //||m.Msg == WM_LBUTTONDOWN

{

//MessageBox.Show("Ignoring Escape...");  

return true; 

} 

return false; 
}
}

備註:主要描述在調用API函數SendMessage時數據類型的轉換。

SendMessage是一個在user32.dll中聲明的API函數,在C#中導入如下:

using System.Runtime.InteropServices;
[DllImport("user32.dll", EntryPoint="SendMessageA")]
public static extern int SendMessage (IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);

本文描述其參數 lParam 的用法,主要是數據類型之間的轉化

● 一種最簡單的處理方式是聲明多個SendMessage函數(overload),用所需的數據類型直接替換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);
//調用:
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);

● 對要求返回字符串的類型(out string)可以用 StringBuilder 代替,此時不需要 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 類型統一處理的話,可以藉助於 Marshal 或者 GCHandle 的相關方法。例如:

[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、首先是返回值意義的區別,我們先看一下 MSDN 裏的聲明:



LRESULT SendMessage(

         HWND hWnd,

         UINT Msg,

         WPARAM wParam,

         LPARAM lParam

);

BOOL PostMessage(

         HWND hWnd,

         UINT Msg,

         WPARAM wParam,

         LPARAM lParam

);



  其中 4 個參數的意義是一樣的,返回值類型不同(其實從數據上看他們一樣是一個 32 位的數,只是意義不一樣),LRESULT 表示的是消息被處理後的返回值,BOOL 表示的是消息是不是 Post 成功。



2、PostMessage 是異步的,SendMessage 是同步的。

  PostMessage 只把消息放入隊列,不管消息是否被處理就返回,消息可能不被處理;而 SendMessage 等待消息被處理完了之後才返回,如果消息不被處理,發送消息的線程將一直被阻塞。



3、如果在同一個線程內,SendMessage 發送消息時,由 USER32.DLL
模塊調用目標窗口的消息處理程序,並將結果返回。SendMessage 在同一線程中發送消息並不入線程消息隊列。PostMessage
發送消息時,消息要先放入線程的消息隊列,然後通過消息循環分派到目標窗口(DispatchMessage)。



 如果在不同線程內,SendMessage 發送消息到目標窗口所屬線程的消息隊列,然後發送消息的線程在 USER32.DLL
模塊內監視和等待消息處理,直到目標窗口處理完返回。SendMessage 在返回前還做了很多工作,比如,響應別的線程向它
SendMessage。Post 到別的線程時,最好用 PostThreadMessage 代替
PostMessage,PostMessage 的 hWnd 參數可以是 NULL,等效於 PostThreadMessage +
GetCurrentThreadId。Post WM_QUIT 時,應使用 PostQuitMessage 代替。



4、系統只整編(marshal)系統消息(0 到 WM_USER 之間的消息),發送用戶消息(WM_USER 以上)到別的進程時,需要自己做整編。



  用 PostMessage、SendNotifyMessage、SendMessageCallback 等異步函數發送系統消息時,參數裏不可以使用指針,因爲發送者並不等待消息的處理就返回,接受者還沒處理指針就已經被釋放了。



5、在 Windows 2000/XP 裏,每個消息隊列最多隻能存放 10,000 個 Post
的消息,超過的還沒被處理的將不會被處理,直接丟掉。這個值可以改得更大:[HKEY_LOCAL_MACHINE/SOFTWARE/
Microsoft/Windows NT/CurrentVersion/Windows] USERPostMessageLimit,最小可以是
4000。

PostMessage只負責將消息放到消息隊列中,不確定何時及是否處理

    SendMessage要等到受到消息處理的返回碼(DWord類型)後才繼續

    PostMessage執行後馬上返回

    SendMessage必須等到消息被處理後纔會返回。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章