• [转载]windows消息分流器


    [转载]windows消息分流器
    自己看核心编程,对消息分流器不太理解,这篇写的不错,所以转载。
    文章作者:欣欣
    原文链接:http://blog.csdn.net/hopkins9961629/archive/2006/01/25/588184.aspx

    很好理解,windows操作系统使用消息处理机制,那么,我们所设计的程序如何才能分辨和处理系统中的各种消息呢?这就是消息分流器的作用.

    简单来说,消息分流器就是一段代码,在我的讲述中,将分7重来循序渐进的介绍它.从最初的第1重到最成熟的第7重,它的样子会有很大的变化.但,实现的功能都是一样的,所不同的,仅仅是变得更加简练罢了.

    程序开始时候,会是main函数,然后会生成初始的窗口,同时会调用WndProc函数.这是一个自定义的函数,名字也会有变化,但其功能是一样的,就是运行消息分流器.WndProc函数如下:

    LRESULT CALLBACK WndProc (HWND hwnd, UINT msg,WPARAM wParam, LPARAM lParam)
    {

    //......

    return DefWindowProc(hwnd, msg, wParam, lParam);

    }

    这其中,hwnd是窗口的句柄,msg是系统发送来的消息的名字.wParam和lParam则是随消息一起发送来的消息参数.

    WndProc函数使用了消息分流器,下面把消息分流器的内容解释一下:

    一重,当不同的消息出现时,在其中写入相应的程序语句即可。
    LRESULT CALLBACK WndProc (HWND hwnd, UINT msg,WPARAM wParam, LPARAM lParam)
    {
    switch(msg)
    {
       case WM_CREATE:
       // ...
       return 0;

       case WM_PAINT:
       // ...
       return 0;

       case WM_DESTROY:
       //...
       return 0;
    }
    return DefWindowProc(hwnd, msg, wParam, lParam);
    }

    二重,运用三个消息分流器进行处理。
    LRESULT CALLBACK WndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
    switch(msg)
    {
       case WM_CREATE:
       return HANDLE_WM_CREATE(hwnd, wParam, lParam, Cls_OnCreate);

       case WM_PAINT:
       return HANDLE_WM_PAINT(hwnd, wParam, lParam, Cls_OnPaint);

       case WM_DESTROY:
       return HANDLE_WM_DESTROY(hwnd, wParam, lParam, Cls_OnDestroy);
    }
    return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    这里的HANDLE_WM_CREATE,HANDLE_WM_PAINT,HANDLE_WM_DESTROY就是消息分流器。
    与消息不同之处就是在前面增加了“HANDLE_”字符,windows的消息分流器就是这样的模样。
    它的本质就是宏定义。
    其中的四个参数有三个都是从本函数的入口参数中直接得到的,即为hwnd, wParam, lParam。
    只有第四的参数是表明调用的函数。
    消息分流器是在winowsx.h文件中定义的。由此,可以看出第四个参数是调用的函数,其定义如下:

    #define HANDLE_WM_CREATE(hwnd, wParam, lParam, fn) ((fn)((hwnd), (LPCREATESTRUCT)(lParam)) ? 0L : (LRESULT)-1L)

    #define HANDLE_WM_PAINT(hwnd, wParam, lParam, fn) ((fn)(hwnd), 0L)

    #define HANDLE_WM_DESTROYCLIPBOARD(hwnd, wParam, lParam, fn) ((fn)(hwnd), 0L)

    0L是表示int类型的变量,其数值为0。
    int类型时,可在后面加l或者L(小写和大写形式)
    表明无符号数时,可在后面加u或者U(小写和大写形式)
    float类型时,可在后面加f或者F(小写和大写形式)
    例如:
    128u 1024UL 1L 8Lu 3.14159F 0.1f

    LRESULT是一个系统的数据类型,其定义如下:
    typedef LONG_PTR LRESULT;

    LONG_PTR也是一个系统的数据类型,其定义如下:
    #if defined(_WIN64)
    typedef __int64 LONG_PTR;
    #else
    typedef long LONG_PTR;
    #endif
    由此可见,LRESULT的实质就是64的long类型的变量

    那么(LRESULT)-1L的实质并不是减法,而是((LRESULT)(-1L)),即强制类型转换

    三重,把消息分流器的宏定义代换回去,就成了下面的样子
    LRESULT CALLBACK WndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
    switch(msg)
    {
       case WM_CREATE:
       return Cls_OnCreate(hwnd, (LPCREATESTRUCT)(lParam)) ? 0L : (LRESULT)-1L;
       // 如果处理了消息,则Cls_OnCreate应返回TRUE,导致WndProc返回0,否则Cls_OnCreate返回FALSE,导致WndProc返回-1;

       case WM_PAINT:
       return Cls_OnPaint(hwnd), 0L;
       // 逗号表达式;Cls_OnPaint是void类型,这里返回0;

       case WM_DESTROY:
       return Cls_OnDestroy(hwnd), 0L; // 同Cls_OnPaint
    }
    return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    在逗号表达式,C++会计算每个表达式,但完整的逗号表达式的结果是最右边表达式的值。
    所以,会return 0。
    然后,就可以手动的编写各个处理函数了:Cls_OnCreate,Cls_OnPaint,WM_DESTROY。

    四重,
    LRESULT CALLBACK WndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
    switch(msg)
    {
       HANDLE_MSG(hwnd, WM_CREATE, Cls_OnCreate);
       HANDLE_MSG(hwnd, WM_PAINT, Cls_OnPaint);
       HANDLE_MSG(hwnd, WM_DESTROY, Cls_OnDestroy);
    }
    return DefWindowProc(hwnd, msg, wParam, lParam);
    }

    HANDLE_MSG也是一个宏,它在windowsx.h中定义,如下:
    #define HANDLE_MSG(hwnd, message, fn) case (message): return HANDLE_##message((hwnd), (wParam), (lParam), (fn))

    这个宏要做的就是根据不同的message(##用来连接前后的字符串),把自己“变成”相应的HANDLE_XXXXMESSAGE形式的宏,再通过相应的宏来执行消息处理代码。
    说白了,就是把message的消息做为替换,##就是一个替换的标志。
    如果没有##,就成了HANDLE_message了,这样,宏是不会被代换的。
    如果就单独一个,则会代换,如hwnd和fn。

    比如实际代码中写入:
    HANDLE_MSG(hwnd, WM_CREATE, Cls_OnCreate)
    则经过转换就变成:
    case (WM_CREATE): return HANDLE_WM_CREATE((hwnd), (wParam), (lParam), (Cls_OnCreate))
    这与二重一模一样。

    以上四重,是消息分离器的基本使用,但,这不完整,消息分离器主要应用在对话框消息处理中。
    这里,窗口子类化是我们经常使用的手段,这也可以通过消息分流器实现,

    第五重
    LRESULT CALLBACK Dlg_Proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
    switch(msg)
    {
       HANDLE_MSG(hwnd, WM_INITDIALO , Cls_OnInitDialog); // 不能直接使用HANDLE_MSG宏
       HANDLE_MSG(hwnd, WM_COMMAND, Cls_OnCommand); // 不能直接使用HANDLE_MSG宏
    }
    return false;
    }
    由于是窗口子类化,所以,最后,返回的是false,以表明,如果没有约定响应的消息,
    则返回父亲窗口false,如果有,则返回ture,这是与前四重不同的地方。
    一般情况下,对话框过程函数应该在处理了消息的情况下返回TRUE,如果没有处理,则返回FALSE。
    如果对话框过程返回了FALSE,那么对话框管理器为这条消息准备默认的对话操作。

    但是,这其中有错误,因为有的消息,需要单独处理。单独处理的消息列表见SetDlgMsgResult宏。

    第六重
    这点小问题,这就需要用到SetDlgMsgResult(hwnd, msg, result)宏。

    LRESULT CALLBACK Dlg_Proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
    switch(msg)
    {
    case WM_INITDIALO:
    return (SetDlgMsgResult(hwnd, Msg, HANDLE_WM_INITDIALO((hwnd), (wParam), (lParam), (fn)));

    case WM_COMMAND:
    return (SetDlgMsgResult(hwnd, Msg, HANDLE_WM_COMMAND((hwnd), (wParam), (lParam), (fn)));
    }
    return false;
    }
    这里,就用直接用到了第二重的消息分流器,而抛弃了其他。

    这个宏定义如下:
    #define SetDlgMsgResult(hwnd, msg, result)
    (
    (
    (msg) == WM_CTLCOLORMSGBOX ||
    (msg) == WM_CTLCOLOREDIT ||
    (msg) == WM_CTLCOLORLISTBOX ||
    (msg) == WM_CTLCOLORBTN ||
    (msg) == WM_CTLCOLORDLG ||
    (msg) == WM_CTLCOLORSCROLLBAR ||
    (msg) == WM_CTLCOLORSTATIC ||
    (msg) == WM_COMPAREITEM ||
    (msg) == WM_VKEYTOITEM ||
    (msg) == WM_CHARTOITEM ||
    (msg) == WM_QUERYDRAGICON ||
    (msg) == WM_INITDIALOG
    ) ?
    (BOOL)(result) :
    (SetWindowLongPtr((hwnd), DWLP_MSGRESULT, (LPARAM)(LRESULT)(result)), TRUE)
    )

    为了表述清楚,所以用了此格式,这是一个三项表达式,首先对消息类型进行考察。

    如果对话框过程处理的消息恰巧为返回特定值中的一个,则如实返回result;
    不要被前面的BOOL蒙蔽,BOOL在头文件中的定义实际上是一个int型,
    一旦需要返回非TRUE或FALSE的其他值,照样可以;

    这样,我们的Cls_OnInitDialog就能够正确的返回它的BOOL值了,
    而Cls_OnCommand在处理之后,也可以由后面的逗号表达式正确的返回一个TRUE表示消息已处理。

    第七重
    我们还可以把case也包含进来,就成了如下的样子。

    LRESULT CALLBACK Dlg_Proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
    switch(msg)
    {
    chHANDLE_DLGMSG(hwnd, WM_INITDIALOG, Cls_OnInitDialog);
    chHANDLE_DLGMSG(hwnd, WM_COMMAND, Cls_OnCommand);
    }
    return false;
    }

    chHANDLE_DLGMSG是牛人定义的一个宏,它把case也包含进来了。
    #define chHANDLE_DLGMSG(hwnd, message, fn) case (message): return (SetDlgMsgResult(hwnd, uMsg, HANDLE_##message((hwnd), (wParam), (lParam), (fn))))

    这样,程序中的语句
    switch (uMsg)
    {
       chHANDLE_DLGMSG(hwnd, WM_INITDIALOG, Dlg_OnInitDialog);
       chHANDLE_DLGMSG(hwnd, WM_SIZE,       Dlg_OnSize);
       chHANDLE_DLGMSG(hwnd, WM_COMMAND,    Dlg_OnCommand);
    }

    就被翻译成:
    switch (uMsg)
    {
    case (WM_INITDIALOG):
       return (SetDlgMsgResult(hwnd, uMsg, HANDLE_WM_INITDIALOG((hwnd), (wParam), (lParam), (Dlg_OnInitDialog))));

    case (WM_SIZE)
       return (SetDlgMsgResult(hwnd, uMsg, HANDLE_WM_SIZE((hwnd), (wParam), (lParam), (Dlg_OnSize))));

    case (WM_COMMAND)
       return (SetDlgMsgResult(hwnd, uMsg, HANDLE_WM_COMMAND((hwnd), (wParam), (lParam), (Dlg_OnCommand))));
    }

    这样,消息分流器,就介绍完毕.

  • 相关阅读:
    JS移动端滑屏事件
    css3,background-clip/background-origin的使用场景,通俗讲解
    addEventListener和on的区别
    JavaScript 变量生命周期
    label标签跳出循环
    js替换指定字符串
    使用ECMAscript5中的forEach函数遍历数组
    截取js数组中某段值(slice)
    数组的一个强大函数splice,[增,删,改]
    删除数组值
  • 原文地址:https://www.cnblogs.com/wubiyu/p/1273624.html
Copyright © 2020-2023  润新知