消息分流器,子控件和API宏

来源:互联网 发布:查看ubuntu的版本号 编辑:程序博客网 时间:2024/05/03 11:27

WindowsX.h文件就是一组#define指令,建立了一组供我们使用的宏。WindowsX.h的宏实际分为三组:消息分流器,子控件和API宏

一,消息分流器

消息分流器(message cracker)使窗口过程的编写更加容易。通常,窗口过程是用一个大的switch语句实现的,利用消息分流器可将switch语句分成小的函数,每个窗口对应一个函数。这样使代码更容易管理。有关窗口过程的另一个问题是每个消息都有wParam和lParam参数,并且根据消息的不同,这些参数的意思也不同。在某些情况下,如对W M _ C O M M A N D消息,w P a r a m包含两个不同的值。w P a r a m参数的高字是通知码,而低字是控件的I D。或者是反过来?我总是忘了次序。如果使用消息分流器,就不用记住或查阅这些内容。消息分流器之所以这样命名,是因为它们对任何给定的消息进行分流。为了处理W M _ C O M M A N D消息,你只需编写这样一个函数:

void Cls_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)

{

    switch(id) {

        case ID_SOMELISTBOX:

            if(codeNotify != LBN_SELCHANGE) break;

            // Do LBN_SELCHANGE processing.

            break;

        case ID_SOMEBUTTON:

            break;

            .

            .

    }

}

这是多么容易!分流器查看消息的w P a r a m和l P a r a m参数,将参数分开,并调用你的函数。为了使用消息分流器,必须对你的窗口过程的s w i t c h语句做一些修改。看一看下面的窗口过程:

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

    switch(uMsg) {

        HANDLE_MSG(hwnd, WM_COMMAND, Cls_OnCommand);

        HANDLE_MSG(hwnd, WM_PAINT, Cls_OnCommand);

        HANDLE_MSG(hwnd, WM_DESTROY, Cls_OnCommand);

        default:

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

        }

}

H A N D L E _ M S G宏在Wi n d o w s X . h中是这样定义的:

#define HANDLE_MSG(hwnd, message, fn) /

        case(message): /

                return HANDLE_##message((hwnd), (wParam), (lParam), (fn));

对于W M _ C O M M A N D消息,预处理程序把这一行代码扩展成下面的代码:

case(WM_COMMAND):

    return HANDLE_WM_COMMAND((hwnd), (wParam), (lParam), (Cls_Oncommand));

  定义在WindowsX.h 中的各H A N D L E _ W M _ *宏是实际的消息分流器。它们分流w P a r a m参数和l P a r a m参数,执行所有必要的转换,并调用适当的消息函数,如前面例举过的C l s _O n C o m m a n d函数。H A N D L E _ W M _ C O M M A N D宏的定义如下:

#define HANDLE_WM_COMMAND(hwnd, wParam, lParam, fn) /
    ((fn)((hwnd), (int)(LOWORD(wParam)), (HWND)(lParam), (UINT)HIWORD(wParam)), 0L)

    当预处理程序扩展这个宏时,其结果是用w P a r a m和l P a r a m参数的内容分流成各自的部分并经适当转换,来调用C l s _ O n C o m m a n d函数。
    在使用消息分流器来处理一个消息之前,应该打开Wi n d o w s X . h文件并搜索要处理的消息。例如,如果搜索W M _ C O M M A N D,将会找到文件中包含下面代码行的部分:

/* void Cls_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) */
#define HANDLE_WM_COMMAND(hwnd, wParam, lParam, fn) /
    ((fn)((hwnd), (int)(LOWORD(wParam)), (HWND)(lParam), (UINT)HIWORD(wParam)), 0L)
#define FORWARD_WM_COMMAND(hwnd, id, hwndCtl, codeNotify, fn) /
    (void)(fn)((hwnd), WM_COMMAND, MAKEWPARAM((UINT)(id),(UINT)(codeNotify)), (LPARAM)(HWND)(hwndCtl))
     第一行是注释行,展示要编写的函数原型。下一行是H A N D L E _ W M _ *宏,我们已经讨论过。最后一行是消息转发器( f o r w a r d e r)。假定在你处理W M _ C O M M A N D消息时,你想调用默认的窗口过程,并让它为你做事。这个函数应该是这个样子:

void Cls_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) {

    //Do some normal processing.

    //Do default processing.

    FORWARD_WM_COMMAND(hwnd, id, hwndCtl, codeNotify, DefWindowProc);

}

F O RWA R D _ W M _ *宏将分流开的消息参数重新构造成等价的w P a r a m和l P a r a m。然后这个宏再调用你提供的函数。在上面的例子中,宏调用D e f Wi n d o w P r o c函数,但你可以简单地使用S e n d M e s s a g e或P o s t M e s s a g e。实际上,如果你想发送(或登记)一个消息到系统中的任何窗口,可以使用一个F O RWA R D _ W M _ *宏来帮助合并各个参数。

二,子控件宏

    子控件宏(Child Control Macro)使发送消息到子控件变得更加容易。这些宏同F O RWA R O _W M _ *宏很相似。每个宏的定义以一个控件类型开始(这个控件是要对它发送消息的控件),后面跟一个下横线和消息名。例如,向一个列表框发送一个L B _ G E T C O U N T消息,就使用Wi n d o w s X . h中的这个宏:

#define ListBox_GetCount(hwndCtl) /

    ((int)(DWORD)SendMessage((hwndCtl), LB_GETCOUNT, 0, 0L))

关于这个宏我想说两件事。第一,它只用一个参数h w n d C t l,这是列表框的窗口句柄。因为L B _ G E T C O U N T消息忽略了w P a r a m和l P a r a m参数,你不必再管这些参数。你可以看到,宏只传递了零。第二,当S e n d M e s s a g e返回时,结果被转换成i n t,所以你不必提供你自己的转换。
关于子控件宏,有一件事我不喜欢,就是这些宏要用控件窗口的句柄。许多时候,你要发
送消息到一个控件,而这个控件是一个对话框的子控件。所以最终你总要调用G e t D l g I t e m,产生这样的代码:
int n = ListBox_GetCount(GetDlgItem(hDlg, ID_LISTBOX));

比起使用S e n d D l g I t e m M e s s a g e,这个代码的运行虽然不慢,但你的程序会包含一些额外的代码。这是由于对G e t D l g I t e m的额外调用。如果需要对同一控件发送几个消息,你可能想调用一次G e t D l g I t e m,保存子窗口的句柄,然后调用你需要的所有的宏,见下面的代码:

HWND hwndCtl = GetDlgItem(hDlg, ID_LISTBOX);

int n = ListBox_GetCount(hwndCtl);

ListBox_AddString(hwndCtl, “Another string”);

.

.

如果按这种方式设计你的代码,你的程序会运行得更快,因为这样就不会反复地调用
G e t D l g I t e m。如果你的对话框有许多控件并且你要寻找的控件在Z序的结尾,则G e t D l g I t e m可能是个很慢的函数。

三,API宏

    A P I宏可以简化某些常用的操作,如建立一种新字体,选择字体到设备环境,保存原来字体的句柄。代码的形式如下:

    HFONT hfontOrig = (HFONT)SelectObject(hdc, (HGDIOBJ)hfontNew);

这个语句要求两个转换以得到没有编译警告错误的编译。在Wi n d o w s X . h中有一个宏,正是为了这个用途而设计:

#define SelectFont(hdc, hfont) /

((HFONT)SelectObject((hdc), (HGDIOBJ)(HFONT)(hfont)))

如果你使用这个宏,你的程序中的代码行就变成:

HFONGT hfontOrig = SelectFont(hdc, hfontNew);

这行代码更容易读,也不容易出错。
在Wi n d o w s X . h中还有其他一些A P I宏,有助于常用的Wi n d o w s任务。建议读者了解并使用这些宏。

原创粉丝点击