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
#include
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;
}