最近老大提出要改界面了,一个一个的贴图甚是麻烦,就想有没有一种一劳永逸的办法呢?查了资料,用WM_CBT钩子,写了个demo,不仅自己写的对话框可以重绘,而且系统对话框也可以重绘.
首先定义一个全局的钩子句柄 HHOOK g_hHook; 然后是钩子处理函数. (晕,从vc6里copy过来,代码缩进都没了)
LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if(nCode < 0)
{
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
switch(nCode)
{
case HCBT_ACTIVATE:
HWND hWnd = (HWND)wParam;
char szTitle[256];
GetWindowText(hWnd, szTitle, sizeof(szTitle));
CHAR szClassName[255];
GetClassName(hWnd, szClassName, sizeof(szClassName));
LONG lWindowStyle = GetWindowLong(hWnd, GWL_STYLE) ;
TRACE("windowtext=%s, classname=%s, WindowLong = %x /n", szTitle, szClassName, lWindowStyle);
long nStyle = WS_CAPTION | WS_DLGFRAME ;
if((WNDPROC)GetProp(hWnd, "SkinWindowOldWndProc") == NULL &&
(lWindowStyle & WS_CAPTION) && (lWindowStyle & WS_DLGFRAME))
{
WNDPROC OldWndProc=(WNDPROC)GetWindowLong(hWnd,GWL_WNDPROC);
LONG lWindowStyle = GetWindowLong(hWnd, GWL_STYLE);
lWindowStyle &= ~WS_SYSMENU; //去掉窗口的系统菜单
SetWindowLong(hWnd, GWL_STYLE, lWindowStyle);
//用SetProp函数将旧消息处理函数的地址传到新消息处理函数中,以便调用默认的处理函数以及最后恢复。
SetProp(hWnd,"SkinWindowOldWndProc",(HANDLE)OldWndProc);
SetWindowLong(hWnd, GWL_WNDPROC, (LONG)NewWndProc);
return 0;
}
}
// 调用下一个Hook
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
接着是新的消息处理函数:
LRESULT CALLBACK NewWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static WNDPROC OldWndProc=NULL;
OldWndProc=(WNDPROC)GetProp(hwnd,"SkinWindowOldWndProc");
ASSERT(OldWndProc != NULL);
CHAR szClassName[255];
GetClassName(hwnd, szClassName, sizeof(szClassName));
if(uMsg == WM_NCPAINT || uMsg == WM_NCACTIVATE)
{
char szTitle[256];
ZeroMemory(szTitle, sizeof(szTitle));
GetWindowText(hwnd, szTitle, sizeof(szTitle));
LRESULT lResult = CallWindowProc(OldWndProc,hwnd,uMsg,wParam,lParam);
CRect rtWnd, rtTitle, rtButtons;
GetWindowRect(hwnd, &rtWnd);
rtTitle.left = GetSystemMetrics(SM_CXFRAME);
rtTitle.top = GetSystemMetrics(SM_CYFRAME);
rtTitle.right = rtWnd.right - rtWnd.left - GetSystemMetrics(SM_CXFRAME);
rtTitle.bottom = rtTitle.top + GetSystemMetrics(SM_CYSIZE);
HDC hDC = GetWindowDC(hwnd);
SetStretchBltMode(hDC, COLORONCOLOR);
CBitmap bitmap;
BITMAP bm;
HDC hdcMem = CreateCompatibleDC(hDC);
CPoint point;
//CBrush Brush(0xad7931);
//HGDIOBJ oldBrush = SelectObject(hDC, Brush);
//填充顶部框架
point.x = rtWnd.Width();
point.y = GetSystemMetrics(SM_CYSIZE) + GetSystemMetrics(SM_CYFRAME)+ 1;
// PatBlt(hDC, 0, 0, point.x, point.y, PATCOPY);
//填充左侧框架
point.x = GetSystemMetrics(SM_CXFRAME) + 0;
point.y = rtWnd.Height();
bitmap.LoadBitmap(IDB_LEFT);
bitmap.GetBitmap(&bm);
SelectObject(hdcMem, bitmap);
StretchBlt(hDC, 0, 0, point.x, point.y, hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);
bitmap.DeleteObject();
//PatBlt(hDC, 0, 0, point.x, point.y, PATCOPY);
//填充右侧框架
point.x = GetSystemMetrics(SM_CXFRAME) + 0;
point.y = rtWnd.Height();
bitmap.LoadBitmap(IDB_RIGHT);
bitmap.GetBitmap(&bm);
SelectObject(hdcMem, bitmap);
StretchBlt(hDC, rtWnd.Width()-point.x, 0, point.x, point.y, hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);
bitmap.DeleteObject();
//PatBlt(hDC, rtWnd.Width()-point.x, 0, point.x, point.y, PATCOPY);
//SelectObject(hDC, oldBrush);
//填充底部框架
point.x = rtWnd.Width();
point.y = GetSystemMetrics(SM_CYFRAME) + 0;
bitmap.LoadBitmap(IDB_BOTTOM);
bitmap.GetBitmap(&bm);
SelectObject(hdcMem, bitmap);
StretchBlt(hDC, 0, rtWnd.Height()-point.y, point.x, point.y,hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);
bitmap.DeleteObject();
//PatBlt(hDC, 0, rtWnd.Height()-point.y, point.x, point.y, PATCOPY);
bitmap.LoadBitmap(IDB_TITLE);
bitmap.GetBitmap(&bm);
SelectObject(hdcMem, bitmap);
StretchBlt(hDC, -5, 0,
rtTitle.right - rtTitle.left + 15, rtTitle.bottom - rtTitle.top + 5,
hdcMem, 0, 1, bm.bmWidth, bm.bmHeight, SRCCOPY);
SetTextColor(hDC, RGB(255, 255, 255));
SetBkMode(hDC, TRANSPARENT);
DrawText(hDC, szTitle, strlen(szTitle), rtTitle, DT_LEFT);
bitmap.DeleteObject();
DeleteDC(hdcMem);
ReleaseDC(hwnd, hDC);
return lResult;
}
//窗体销毁时,把原来的消息处理函数还原
if(WM_NCDESTROY == uMsg )
{
SetWindowLong(hwnd,GWL_WNDPROC,(LONG)OldWndProc);
RemoveProp(hwnd,"SkinWindowOldWndProc");
return CallWindowProc(OldWndProc,hwnd,uMsg,wParam,lParam);
}
return CallWindowProc(OldWndProc,hwnd,uMsg,wParam,lParam);
}
//接下来的两个消息处理为屏蔽通用对话框上的"?"按钮和"X"按钮 //其实这种屏蔽方法还不是很完全 //如果见到WM_NCLBUTTONDOWN函数就返回TRUE的话,对话框就不能移动了。 //如果要完全屏蔽的话,需要重写WM_NCLBUTTONDOWN处理函数,即自己写窗口移动的函数。 if(WM_NCLBUTTONDOWN == uMsg) { if(strcmp(szClassName, "#32770") == 0) { CallWindowProc(OldWndProc,hwnd,uMsg,wParam,lParam); SendMessage(hwnd, WM_NCPAINT, wParam, lParam); } CallWindowProc(OldWndProc,hwnd,uMsg,wParam,lParam); return TRUE; }
if(WM_NCMOUSEMOVE == uMsg ) { if(strcmp(szClassName, "#32770") == 0) { return TRUE; } CallWindowProc(OldWndProc,hwnd,uMsg,wParam,lParam); }
//窗体销毁时,把原来的消息处理函数还原 if(WM_DESTROY == uMsg ) { SetWindowLong(hwnd,GWL_WNDPROC,(LONG)OldWndProc); RemoveProp(hwnd,"SkinWindowOldWndProc"); return CallWindowProc(OldWndProc,hwnd,uMsg,wParam,lParam);
} return CallWindowProc(OldWndProc,hwnd,uMsg,wParam,lParam); } 最后在app的InitInstance函数里加入: g_hHook = SetWindowsHookEx(WH_CBT, CBTProc, NULL, GetCurrentThreadId());
在ExitInstance函数里加入: UnhookWindowsHookEx(g_hHook);
效果图如下: