HOOK自己接触的其实非常少,所以也从来没敢谈论过什么,这个MessageBox还真的研究了好几个hour呢,挺怕被大牛们嘲笑的,今天我也不要脸了,说说我的一些看法,欢迎扔砖头,西红柿。
众所周知,一个标准的messagebox框通常不是自定义风格的,因为我们从来不能正常找到它的句柄handle.API函数MessageBox只有在messagebox框被销毁的时候才能返回,因此,我们的程序从来不曾看到messagebox窗口的消息,因为MessageBox这个API调用了它自己内部包含了消息循环,跟DialogBox这个创建非模态对话框的API函数类似的方式。
首先说如何修改标准OK按钮上的文本。
主要借助了hook,截取messagbox框的消息的最好的方法是安装一个Hook,这个非常容易实现----SetWindowsHookEx就可以实现。Hook有很多不同的类型,每个类型是为不同的目的所设计,这里不啰嗦,其实我也斗胆敢啰嗦。
这里用到的是WM_CBT hook,这个Hook被调用只为了很少的一些消息,比如,WM_ACTIVATE,WM_CREATE,WM_DESTROY,WM_SIZE,WM_MOVE等等。因为我们想在窗口创建期间来实现我们的自定义风格,因此这个Hook算是符合我们的目的吧。
安装一个Hook
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!!! );
需要简单注意一下GetCurrentThreadId(),仅仅安装在本线程。
移走一个Hook
UnhookWindowsHookEx(hMsgBoxHook);
核心部分--hook过程
上面已经说了无论何时当有WM_ACTIVATE,WM_CREATE消息被发送到我们的应用程序的时候都会有一个CBT事件发生。
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: // Get handle to the message box! hwnd = (HWND)wParam; // Do customization! return 0; } // Call the next hook, if there is one return CallNextHookEx(hMsgBoxHook, nCode, wParam, lParam); }
完整结合
围绕MessageBox,我们自己写个函数,此函数隐藏hook的细节,当我们想显示一个自定义的MessageBox的时候,就用这个函数替换就OK。
static HHOOK hMsgBoxHook; ... int MsgBoxEx(HWND hwnd, TCHAR *szText, TCHAR *szCaption, UINT uType) { int retval; hMsgBoxHook = SetWindowsHookEx( WH_CBT, CBTProc, NULL, GetCurrentThreadId() ); retval = MessageBox(hwnd, szText, szCaption, uType); UnhookWindowsHookEx(hMsgBoxHook); return retval; }
到此为止就已经把OK按钮的字体变成了我们自己的了。
这里我顺便给MessageBox把最大最小化按钮也给加上了,当然图标也是可以替换的,等等,剩下的就看想象力了。简单说下添加最大最小化按钮过程中的一点细节吧,起初我以为可以在创建的时候添加,因此写的代码如下:
case HCBT_CREATEWND: cbt_createwnd=(LPCBT_CREATEWND)lParam; cbt_createwnd->lpcs->style|=WS_MINIMIZEBOX;//MSDN中说的很清楚了,这里并不起作用 cbt_createwnd->lpcs->cx=300; cbt_createwnd->lpcs->cy=300;
发现真的没任何效果,看了MSDN发现确实说的很清楚了,证据在这里:
HCBT_CREATEWND | Specifies the handle to the new window. | Specifies a long pointer to a CBT_CREATEWND structure containing initialization parameters for the window. The parameters include the coordinates and dimensions of the window. By changing these parameters, a CBTProc hook procedure can set the initial size and position of the window. |
在这里只能设置坐标跟尺寸,想设置风格那就不可能了。
想来想去还是觉得得用窗口子类化,因为这家伙的确是很实用,思路:在窗口的WM_ACTIVATE消息中利用SetWindowLong来进行窗口子类化,在窗口过程函数中顺便我实现了对MessageBox框上的按钮响应了,让他继续弹出一个MessageBox框。。。。。列出一些MSDN的记载吧:
HCBT_ACTIVATE | Specifies the handle to the window about to be activated. | Specifies a long pointer to a CBTACTIVATESTRUCT structure containing the handle to the active window and specifies whether the activation is changing because of a mouse click. |
#include <windows.h> #include <tchar.h> TCHAR szContents[] = _T("I really miss you very much--zxj!"); TCHAR szTitle[] = _T("Hello"); #define IDC_PZG 123 HHOOK hMsgBoxHook; LPCBT_CREATEWND cbt_createwnd; WNDPROC OldWndProc; INT_PTR CALLBACK MessageBoxProc(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam) { switch(Msg) { case WM_COMMAND: switch(LOWORD(wParam)) { case IDC_PZG: MessageBox(hWnd,"痞子","痞子哥",MB_OK|MB_ICONINFORMATION); break; } case WM_DESTROY: PostQuitMessage(0); return 0; default: return CallWindowProc(OldWndProc,hWnd,Msg,wParam,lParam); } return 0; } LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam) { TCHAR ach[40]; HWND hwnd; HWND hwndButton; if(nCode < 0) return CallNextHookEx(hMsgBoxHook, nCode, wParam, lParam); switch(nCode) { case HCBT_CREATEWND: cbt_createwnd=(LPCBT_CREATEWND)lParam; cbt_createwnd->lpcs->style|=WS_MINIMIZEBOX;//MSDN中说的很清楚了,这里并不起作用 cbt_createwnd->lpcs->cx=300; cbt_createwnd->lpcs->cy=300; //hwnd=(HWND)wParam; break; case HCBT_ACTIVATE: //case HCBT_CREATEWND: // Get handle to the message box! hwnd = (HWND)wParam; OldWndProc=(WNDPROC)SetWindowLong(hwnd,GWL_WNDPROC,(LONG)MessageBoxProc); SetWindowLong(hwnd,GWL_STYLE,(long)WS_OVERLAPPEDWINDOW); SetWindowText(hwnd, _T("Message from yiruirui")); hwndButton = GetDlgItem(hwnd, IDOK); SetWindowText(hwndButton, _T("Thankyou")); CreateWindow("button","痞子--AV王子",WS_CHILD|WS_VISIBLE,10,50,250,250,hwnd,(HMENU)IDC_PZG,GetModuleHandle(NULL),NULL); return 0; } return CallNextHookEx(hMsgBoxHook, nCode, wParam, lParam); } int MsgBoxEx(HWND hwnd, TCHAR *szText, TCHAR *szCaption, UINT uType) { int retval; // Install a window hook, so we can intercept the message-box // creation, and customize it hMsgBoxHook = SetWindowsHookEx( WH_CBT, CBTProc, NULL, GetCurrentThreadId() // Only install for THIS thread!!! ); // Display a standard message box retval = MessageBox(hwnd, szText, szCaption, uType); // remove the window hook UnhookWindowsHookEx(hMsgBoxHook); return retval; } int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpCmdLine, int nShowCmd) { // Just display a standard message box. // It doesn't matter that we have no parent window or a // message-loop, because MessageBox has it's own message loop. MsgBoxEx(NULL, szContents, szTitle, MB_OK | MB_ICONSTOP); return 0; }