[C#.NET][WinForm] Windows 窗口讯息接收 - WndProc 的使用

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的定义,这将对我们在宣告常数时有很大的帮助喔。

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坐标

这时有人问,LParam并没有看到什么low word跟high word阿?,别忘了计算机是由二进制在处理数据的,VS的变量都是塞已经处理好的十进制,只要将其转回二进制,就能了解了,来复习一下计算器概论吧

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

LParam讯息是由32bit兜起来的,也就是2个16 word,根据MSDN所述来处理数据。

 

这样一来就看的懂为什么上面的例子要处理二进制,了解二进制的处理方法后,我们再来用.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; }

    …

}

 

我拿比较重要的讯息稍微解释一下:

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。

 

 原文:http://www.dotblogs.com.tw/yc421206/archive/2011/01/24/20971.aspx

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值