VC定制个性化的MessageBox解决方法
相信学过VC的人都知道MessageBox()函数的用法:
intMessageBox( HWNDhWnd,//handletoownerwindow LPCTSTRlpText,//textinmessagebox LPCTSTRlpCaption,//messageboxtitle UINTuType//messageboxstyle );
虽然在参数uType中可以指定一些样式,但你在程序中能够对MessageBox的外观所做的定义却不多。原因是当调用MessageBox()函数后,它在内部有自己的消息循环(所有的模式对话框都有自己的消息循环),返回时MessageBox对话框窗口已经被Destroy,所以你没有办法得到MessageBox对话框的窗口句柄。但你可以根据自己的不同需求用下面的方法中去定制你的MessageBox:
如果你只是想用自己的icon去代替系统MessageBox提供的那几种有限的icon,用MessageBoxIndirect()函数就可以了:
intMessageBoxIndirect( CONSTLPMSGBOXPARAMSlpMsgBoxParams//messageboxparameters ); typedefstruct{ UINTcbSize; HWNDhwndOwner; HINSTANCEhInstance; LPCTSTRlpszText; LPCTSTRlpszCaption; DWORDdwStyle; LPCTSTRlpszIcon; DWORD_PTRdwContextHelpId; MSGBOXCALLBACKlpfnMsgBoxCallback; DWORDdwLanguageId; }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钩子:
HHOOKhMsgBoxHook=SetWindowsHookEx( WH_CBT,//Typeofhook CBTProc,//Hookprocedure(seebelow) NULL,//Modulehandle.MustbeNULL(seedocs) GetCurrentThreadId()//OnlyinstallforTHISthread!!! );
重要的是SetWindowHookEx()函数的后边两个参数,用它可以区别安装是一个全局钩子还是一个线程钩子,在这里我们只要安装一个线程钩子。所以我们将Modulehandle设置为NULL,同时将threadID设为本线程的ID。
在SetWindowHookEx()函数中有一个hookprocedure,这是window调用的一个回调函数,在windows系统中有一个HOOK链,我们在编写hookprocedure时要注意保证此链的完整,所以我们的hookprocedure要调用CallNextHookEx()函数。下面就是我们的hookprocedure:
LRESULTCALLBACKCBTProc(intnCode,WPARAMwParam,LPARAMlParam) { HWNDhwnd; if(nCode<0) returnCallNextHookEx(hMsgBoxHook,nCode,wParam,lParam); switch(nCode) { caseHCBT_ACTIVATE: //现在wParam中就是messagebox的句柄 hwnd=(HWND)wParam; //我们已经有了messagebox的句柄,在这里我们就可以定制messagebox了! return0; } //Callthenexthook,ifthereisone returnCallNextHookEx(hMsgBoxHook,nCode,wParam,lParam); }
移除HK_CBT钩子:
只要调用UnhookWindowsHookEx()函数就可以了
好了,我们将在上面的三步写成一个函数,如下:
intMsgBoxEx(HWNDhwnd,TCHAR*szText,TCHAR*szCaption,UINTuType) { intret; //Installathreadhook,sowecancustomizeit hMsgBoxHook=SetWindowsHookEx( WH_CBT, CBTProc, NULL, GetCurrentThreadId() ); //Displayastandardmessagebox ret=MessageBox(hwnd,szText,szCaption,uType); //removethewindowhook UnhookWindowsHookEx(hMsgBoxHook); returnret; }
其实你也可以钩住WM_CREATE消息,不过那样处理要复杂一些。在早期的windowsplatformSDK中就有这样一个例子。
你可能说,定制一个MessageBox有什么用处,我想有下面的用途:
1.你可以用CreateWindowEx()给MessageBox添加一个checkbox控件,并子类化MessageBox来处理checkbox的消息
2.通过子类化改变messagebox、button或icon,以便和你整个程序的界面风格相一致
只要有了MessageBox对话框的句柄,你能做的很多,很多...
另外,如果你对模式对话框的机理很了解,你可以自己写出一个"MessageBox"来代替系统MessageBox用在你的程序中。你可以参考JeffreyRichter的《Windows95程式设计指南》,在书中给出了模式对话框的伪码。这本书的繁体电子版可以在候捷的个人网站上下载。这种方法也比较简单(添加一个消息循环,Enable/DisableOwner窗口),示例代码这里就不实现了。读者可以参考相关资料加以完善。