定制MessageBox

来源:互联网 发布:澳大利亚有淘宝吗 编辑:程序博客网 时间:2024/04/28 07:15
大家都知道MessageBox()函数的用法:

int MessageBox(
  HWND hWnd,          // handle to owner window
  LPCTSTR lpText,     // text in message box
  LPCTSTR lpCaption,  // message box title
  UINT uType          // message box style
);

虽然在参数uType中可以指定一些样式,但你在程序中对MessageBox的外观能定义
的不多。原因是当调用MessageBox()函数后,它在内部有自己的消息循环(所有的
模式对话框都有自己的消息循环),返回时MessageBox对话框窗口已经被Destroy,
所以你没有办法得到MessageBox对话框的窗口句柄。但你可以根据自己的不同需
求用下面的方法中去定制你的MessageBox:

如果你只是想用自己的icon去代替系统MessageBox提供的那几种有限的icon,
用MessageBoxIndirect()函数就可以了:

int MessageBoxIndirect(
  CONST LPMSGBOXPARAMS lpMsgBoxParams  // message box parameters
);

typedef struct {
  UINT      cbSize;
  HWND      hwndOwner;
  HINSTANCE hInstance;
  LPCTSTR   lpszText;
  LPCTSTR   lpszCaption;
  DWORD     dwStyle;
  LPCTSTR   lpszIcon;
  DWORD_PTR dwContextHelpId;
  MSGBOXCALLBACK lpfnMsgBoxCallback;
  DWORD     dwLanguageId;
} MSGBOXPARAMS, *PMSGBOXPARAMS;

看到MSGBOXPARAMS结构中的lpszIcon吧,在使用过程中你应当准备一个合适的
MSGBOXPARAMS结构,如果你要用自己的icon,你一定要用MB_USERICON这个flag。

上面我们也讲到不能定制MessageBox对话框的原因是没有办法得到它的窗口句柄
,但我们真的没有办法了吗?当然有办法,那就是使用HOOK(钩子)机制。
在windows系统中有多种HOOK,但在这里我们只用到HK_CBT类型的钩子。
HK_CBT钩子过程在WM_MOVE、WM_SIZE、WM_ACTIVE、WM_CREATE、WM_DESTROY时被
系统调用,所以HK_CBT钩子可以在这里用。下面让我们看如何实现MessageBox的
定制过程。

    1,安装HK_CBT钩子;
    2,调用MessageBox()函数;
    3,移除HK_CBT钩子。

整个过程很简单吧?我将介绍第一步和第三步。

安装HK_CBT钩子:

HHOOK hMsgBoxHook = SetWindowsHookEx(

        WH_CBT,                // Type of hook
        CBTProc,               // Hook procedure (see below)
        NULL,                  // Module handle. Must be NULL (see docs)
        GetCurrentThreadId()   // Only install for THIS thread!!!
        );

重要的是SetWindowHookEx()函数的后边两个参数,用它可以区别安装是一个全局
钩子还是一个线程钩子,在这里我们只要安装一个线程钩子。所以我们将Module handle
设置为NULL,同时将thread ID设为本线程的ID。

在SetWindowHookEx()函数中有一个hook procedure,这是window调用的一个回调
函数,在windows系统中有一个HOOK链,我们在编写hook procedure时要注意保证
此链的完整,所以我们的hook procedure要调用CallNextHookEx()函数。下面就
是我们的hook procedure:

LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    HWND hwnd;
    if(nCode < 0)
        return CallNextHookEx(hMsgBoxHook, nCode, wParam, lParam);
    switch(nCode)
    {
    case HCBT_ACTIVATE:
        // 现在wParam中就是message box的句柄
        hwnd = (HWND)wParam;
        // 我们已经有了message box的句柄,在这里我们就可以定制message box了!
        return 0;
    }
    // Call the next hook, if there is one
    return CallNextHookEx(hMsgBoxHook, nCode, wParam, lParam);
}

移除HK_CBT钩子:

只要调用UnhookWindowsHookEx()函数就可以了

好了,我们将在上面的三步写成一个函数,如下:

int MsgBoxEx(HWND hwnd, TCHAR *szText, TCHAR *szCaption, UINT uType)
{
    int ret;
    // Install a thread hook, so we can customize it
    hMsgBoxHook = SetWindowsHookEx(
        WH_CBT,
        CBTProc,
        NULL,
        GetCurrentThreadId()
        );
    // Display a standard message box
    ret = MessageBox(hwnd, szText, szCaption, uType);
    // remove the window hook
    UnhookWindowsHookEx(hMsgBoxHook);
    return ret;

}

其实你也可以钩住WM_CREATE消息,不过那样处理要复杂一些。我记得在早期的
windows platform SDK中有这样一个例子。

你可能说,定制一个MessageBox有什么用处,我想有下面的用途:

    1.你可以用CreateWindowEx()给MessageBox添加一个check box控件,并子
      类化MessageBox来处理check box的消息

    2.通过子类化改变messagebox、button或icon,以便和你整个程序的界面风
      格相一致

      只要有了MessageBox对话框的句柄,你能做的很多,很多...

      另外,如果你对模式对话框的机理很了解,你可以自己写出一个
      "MessageBox"来代替系统MessageBox用在你的程序中。
      你可以参考Jeffrey Richter的《Windows 95程式设计指南》,在书中给出
      了模式对话框的伪码。这本书的繁体电子版可以在候捷的个人网站上下载。
      这种方法也比较简单(添加一个消息循环,Enable/Disable Owner窗口),
      我就在示例代码中不实现了。由于我工作较忙,在写示例代码时没有多少
      注释和防错,请读者谅解 
 
原创粉丝点击