Windows窗体消息接收 - WndProc

本文转自:https://dotblogs.com.tw/yc421206/2011/01/23/20971

Windows窗体主要是由事件所驅動,使用者所操作的動作或是系統核心觸發的中斷都是交由Windows先行處理,再透過Windows Message機制傳送給窗体應用程式,在Windows裡我們不需要直接與硬體溝通,也不允許直接溝通,通通都是要透過Windows核心協調分配,我們只要專心處理Windows所觸發的事件就可以了;比如我們在鑑盤按下任意鍵,鍵盤所按下的中斷訊號被Windows接收後,會判斷目前的作用窗体並將被按下的訊息傳送給那個窗体,有鈕被按下了,最後KeyDown事件被觸發,由Windows傳送來的事件我們還可以知道是哪一個鍵被按下了。

這種由系統發出的訊息叫Windows Message,而 WindowProc Callback Function 就是用來接收Windows Message的一個函數,比如滑鼠左鍵被按下時會收到 WM_LBUTTONDOWN,鍵盤被按下時會收到 VM_KEYDOWN ,不管用什麼語言開發WIndows Message傳遞機制都不變,只是在不同的語言所包裝的方法名稱不一樣而已。在大內世界裡System.Windows.Forms.Form已經將這些Message包裝起來,WM_LBUTTONDOWN = MouseDown事件、WM_PAINT = Paint 事件,然後再透過對應的EventArgs參數來判斷詳細的Message內容,這些事件各位其實都不陌生,因為都是我們常用的。

private void Form1_KeyDown(object sender, KeyEventArgs e)
{ 
  //鍵盤按下事件
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{ 
  //滑鼠左鍵按下
}

System.Windows.Forms.Control類別,它負責了所有可見控制項的的基本處理,滑鼠、鍵盤以及重繪操作,裡面的WndProc方法就是和WindowProc 函式一樣,用來接收Windows Message指令的方法,使用方法很簡單,只要覆寫WndProc方法,這個窗体就能自己處理由Windows Message傳來的訊息,接下來來練習一下怎麼接收訊息,首先http://www.woodmann.com/fravia/sources/WINUSER.H包含了所有Message ID的定義,這將對我們在宣告常數時有很大的幫助喔。

image

 

WndProc方法的使用

先來看個簡單的例子,在Form裡覆寫WndProc方法:

const int WM_MOUSEMOVE = 0x0200;//定義ID
protected override void WndProc(ref Message m)
{
    if (m.Msg == WM_MOUSEMOVE)
    {
        string binary = System.Convert.ToString(m.LParam.ToInt32(), 2).PadLeft(32, '0');//轉成二進制
        string lWord = "";
        string hWord = "";
        for (int i = 0; i < 16; i++)
        {
            hWord += binary[i].ToString();
        } 

        for (int i = 17; i < 32; i++)
        {
            lWord += binary[i].ToString();
        }
        int x = Convert.ToInt32(lWord, 2);//二進制轉十進制
        int y = Convert.ToInt32(hWord, 2);
        this.lab_WM_MOVE_1.Text = string.Format("使用WM_MOUSEMOVE ,X:{0},Y:{1}", x, y); 

        ushort xpos = (ushort)m.LParam;//表示X座標
        ushort ypos = (ushort)(m.LParam.ToInt32() >> 16);//表示X座標
        this.lab_WM_MOVE_2.Text = string.Format("使用WM_MOUSEMOVE ,X:{0},Y:{1}", xpos, ypos);
    }
    base.WndProc(ref m);

稍為解釋一下程式碼,滑鼠的移動訊息是丟到lParam參數裡了,從MSDN的WM_MOUSEMOVE Message可以知道 ,裡面寫著low word是X座標,high word是Y座標

image

這時有人問,LParam並沒有看到什麼low word跟high word阿?,別忘了電腦是由二進制在處理資料的,VS的變數都是塞已經處理好的十進制,只要將其轉回二進制,就能了解了,來複習一下計算機概論吧

string binary = System.Convert.ToString(m.LParam.ToInt32(), 2).PadLeft(32, '0');//轉成二進制 

LParam訊息是由32bit兜起來的,也就是2個16 word,根據MSDN所述來處理資料。

image

這樣一來就看的懂為什麼上面的例子要處理二進制,瞭解二進制的處理方法後,我們再來用.NET的轉型來處理,才不會一下子不知所措。

ushort xpos = (ushort)m.LParam;//表示X座標
ushort ypos = (ushort)(m.LParam.ToInt32() >> 16);//表示Y座標
this.label2.Text = string.Format("X:{0},Y:{1}", xpos, ypos);

上面的範例在.Net的MouseMove事件裡變得更簡單,只要用MouseEventArgs參數就能處理滑鼠座標了。

private void Form1_MouseMove(object sender, MouseEventArgs e)
{
    this.lab_WM_MOVE_2.Text = string.Format("X:{0},Y:{1}", e.X, e.Y);
}

看到這裡我相信有用心看的人都稍為瞭解了,打鐵要趁熱,再來看一個例子,這次來玩玩WM_KEYDOWN Message;沒錯,資料是丟到wParam,很好!這次你也看懂了,所以我們要來處理wParam參數。不過WM_KEYDOWN Message主要顯示虛擬鍵盤(Virtual-Key Codes),是不包含系統鍵值,只有大寫;若要取得系統鍵值,可以用WM_CHAR Message

const int WM_KEYDOWN = 0x0100;
const int WM_CHAR = 0x0102;
protected override void WndProc(ref Message m)
{
    if (m.Msg == WM_KEYDOWN)
    {
        char keys = (char)m.WParam;
        this.lab_WM_KeyDown.Text = string.Format("使用WM_KEYDOWN ,按下:{0}", keys.ToString());
    } 

    if (m.Msg == WM_CHAR)
    {
        char keys = (char)m.WParam;
        this.lab_WM_KeyChar.Text = string.Format("使用WM_CHAR ,按下:{0}", keys.ToString());
    }
    base.WndProc(ref m);
}

 

把上面程式碼的轉換成.NET的事件,分別對應到KeyDown與KeyPress

private void Form1_KeyDown(object sender, KeyEventArgs e)
{
    this.lab_KeyDown.Text = string.Format("KeyDown 事件 ,按下:{0}", ((char)e.KeyCode).ToString());
} 

private void Form1_KeyPress(object sender, KeyPressEventArgs e)
{
    this.lab_KeyPress.Text = string.Format("KeyPress事件 ,按下:{0}", e.KeyChar.ToString());
}

 

Message結構

由winuser.h得知,在Win32API裡是定义成這樣

typedef struct tagMSG {
    HWND        hwnd;
    UINT        message;
    WPARAM      wParam;
    LPARAM      lParam;
    DWORD       time;
    POINT       pt;
}
對應到.Net的Message結構
public struct Message
{
    public IntPtr HWnd { get; set; }
    public int Msg { get; set; }
    public IntPtr WParam { get; set; }
    public IntPtr LParam { get; set; }
    public IntPtr Result { get; set; }
    …
}

 

image

 

我拿比較重要的訊息稍微解釋一下:

tagMSG.hwnd及Message.HWnd,是系統給予的唯一編碼,也就是Control.Handle屬性。

tagMSG.Message及Message.Msg,是Windows Message ID,例如像WM_KEYDOWN、WM_KEYUP這些東西,定義在winuser.h裡。

而WParam及LParam是存放重要訊息的參數,不同的Windows Message ID有不同的結果,就像我們上面演練的那樣,必須要去分析它們才能取得正確的資訊。

 

結論:

在Windows世界裡TestBox、Label、ComboBox控制項其實都是窗体,只是窗体的類別不一樣而已,記得以前常在用的窗体列舉,會先找父窗体,再由父窗体裡找到控制項,找到自己要的控制項的條件,我通常都是用窗体屬性+Handle抓到我要的控制項,然後再傳送Message給控制項。

瞭解到Windwos怎麼接收Message後,下篇再來寫怎麼傳遞Message。

 

WndProc.zip

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如果你想让 VB.NET 窗体能够接收来自其他进程的信息,你可以使用 Windows API 中的 SendMessage 函数或 PostMessage 函数。 具体来说,你可以通过调用 SendMessage 或 PostMessage 函数,将一个自定义的消息发送给 VB.NET 窗体。然后,你可以在 VB.NET 窗体中重载 WndProc 方法,以便在消息接收时进行处理。 下面是一个简单的示例,演示了如何通过 SendMessage 函数向 VB.NET 窗体发送自定义消息: ```vb.net Public Class Form1 Private Const WM_MYMESSAGE As Integer = &H8000 Protected Overrides Sub WndProc(ByRef m As Message) If m.Msg = WM_MYMESSAGE Then ' 在这里处理接收到的消息 End If MyBase.WndProc(m) End Sub Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click ' 向当前窗体发送自定义消息 SendMessage(Me.Handle, WM_MYMESSAGE, 0, 0) End Sub Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer End Class ``` 在上面的代码中,我们定义了一个常量 WM_MYMESSAGE,它表示我们自定义的消息。然后,在重载的 WndProc 方法中,我们检查接收到的消息是否是 WM_MYMESSAGE,如果是,就在这里处理它。最后,在 Button1 的 Click 事件中,我们通过 SendMessage 函数将自定义消息发送给当前窗体。 需要注意的是,如果你想从其他进程中发送消息到 VB.NET 窗体,你需要知道窗体的句柄(也就是窗体的 IntPtr 类型的 Handle 属性)。你可以使用 FindWindow 函数或 EnumWindows 函数来查找窗体句柄。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值