【看到网上有很多转载,深感荣幸,今天工作之余在CSDN整理一下】
一. 前言
作者没干过项目,非编程专业人士,仅凭兴趣支撑。所以,程序中一定有很多弱智的段落或者内存泄露之类,所以,这里的文章及代码,只供学习交流之用。其他用途后果自负,所有代码都可无限制免费引用与修改。
原始地址:http://blog.csdn.net/ljfblog/article/details/14224117
转载者请附本节,谢谢。
二. 基本原理:
1.用SetWindowsHookEx截获应用程序级消息。
m_hHook = SetWindowsHookEx(WH_CALLWNDPROC, WindowHook,AfxGetApp()->m_hInstance,GetCurrentThreadId());
...
LRESULT CALLBACK CLjfDialog::WindowHook(int code, WPARAM wParam, LPARAM lParam)
{
CWPSTRUCT* pStruct = (CWPSTRUCT*)lParam;
//处理截获的消息,本程序中把不同类型窗口分类处理,即CLjfDialog,CLjfButton
}
2.用SetWindowLong(hWnd, GWL_WNDPROC,LjfDialogProc)子类化截获到的窗口。
LRESULT CALLBACK CLjfDialog::LjfDialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
//这里就可以截断或处理所有窗口消息了。建议像本代码一样,各自类型的窗口各自处理。
}
3.用CMap或者直接用vector记录已经被子类化的窗口。
4.卸载钩子,释放内存。
三. 核心代码
//安装钩子
void CLjfDialog::Install(CString sSkinIniFileName)
{
if (m_hHook == NULL )
m_hHook = SetWindowsHookEx(WH_CALLWNDPROC,
WindowHook,AfxGetApp()->m_hInstance,GetCurrentThreadId());
CLjfIni ini(sSkinIniFileName);
m_sSkinName=ini.GetString("skin","skin","");
if(m_sSkinName == "") m_sSkinName = "skin";//默认
CString sIniPath;sIniPath.Format("%s\\skin.ini",m_sSkinName);
m_sif.GetInfoFromIniFile(sIniPath);
GetCBitmap("bodyfocus",m_cbmpDlgFocus);
GetCBitmap("bodyblur",m_cbmpDlgBlur);
GetCBitmap("sysbtn",m_cbmpSysBtn);
m_sif.PreparBitmapTranRgn(&m_cbmpDlgFocus);
}
//钩子回调消息过程
LRESULT CALLBACK CLjfDialog::WindowHook(int code, WPARAM wParam, LPARAM lParam)
{
CWPSTRUCT* pStruct = (CWPSTRUCT*)lParam;
if (code == HC_ACTION)
{
HWND hWnd = pStruct->hwnd;
if (pStruct->message != WM_CREATE && pStruct->message != 0x01E2) goto EndProc;
TCHAR strClassName[20];GetClassName (hWnd, strClassName, sizeof(strClassName));
TRACE(strClassName);TRACE("---------\r\n");
//是CDialog类,无标题栏
if (_tcscmp(strClassName, _T("#32770")) == 0 && GetWindowLong(hWnd,GWL_STYLE)&WS_CAPTION)
{
AddSubClass(hWnd,sLjfDialogOldProcTag,(DWORD)(ULONG)LjfDialogProc);
goto EndProc;
}
}
EndProc:
return CallNextHookEx (m_hHook, code, wParam, lParam);
}
//子类化
bool CLjfDialog::AddSubClass( HWND &hWnd,TCHAR * oldProcTag,DWORD Proc)
{
if (GetProp(hWnd, oldProcTag) != NULL ) return false;//已被子类化
VERIFY(AddWndHook(hWnd) != NULL);
WNDPROC oldWndProc = (WNDPROC)(long)::GetWindowLong(hWnd, GWL_WNDPROC);// 取原来窗口过程
if (oldWndProc == NULL) return false;
ASSERT((DWORD)(ULONG)oldWndProc != Proc);
if (!SetProp(hWnd, oldProcTag, oldWndProc) ) return false;// 保存到窗口属性
if (!SetWindowLong(hWnd, GWL_WNDPROC,Proc))// 子类化
RemoveProp(hWnd, oldProcTag);
return true;
}
//对话框子类化回调函数
LRESULT CALLBACK CLjfDialog::LjfDialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
WNDPROC oldWndProc = (WNDPROC)GetProp(hWnd, sLjfDialogOldProcTag);
CLjfDialog* pWnd = GetWndHook(hWnd);
UINT uMsg1=uMsg;
if (pWnd == NULL) goto EndLjfDialogProc;
switch (uMsg)
{
case WM_NCCALCSIZE:
return pWnd->OnNcCalcsize(oldWndProc, hWnd, uMsg1, wParam, lParam);
case WM_NCPAINT:
pWnd->OnNcPaint();
return HTNOWHERE;
case WM_SHOWWINDOW:
pWnd->OnShowWindow(wParam != NULL);
break;
case WM_NCDESTROY:
pWnd->OnNcDestroy();
break;
case WM_CREATE:
pWnd->OnCreate((CREATESTRUCT *)lParam);
break;
case WM_NCHITTEST:
return pWnd->OnNcHitTest(oldWndProc, hWnd, uMsg1, wParam, lParam);
case WM_NCACTIVATE:
pWnd->OnNcActivate((BOOL)LOWORD(wParam));
return HTCAPTION;
case 0x00AE://WM_NCUAHDRAWCAPTION
case 0x00AF://WM_NCUAHDRAWFRAME
//在windows最佳性能时依然在NCBUTTONDOWN和调整大小时有系统最大最小关闭按钮出现,有待解决.
//case WM_NOTIFY:
return HTCAPTION;
case WM_MOVE:
pWnd->OnMove(LOWORD(lParam),HIWORD(lParam));
break;
case WM_NCLBUTTONDOWN:
pWnd->OnNcLButtonDown((UINT)wParam,CPoint(LOWORD(lParam),HIWORD(lParam)));
break;
case WM_NCLBUTTONUP:
pWnd->OnNcLButtonUp((UINT)wParam,CPoint(LOWORD(lParam),HIWORD(lParam)));
break;
case WM_NCMOUSEMOVE:
pWnd->OnNcMouseMove((UINT)wParam,CPoint(LOWORD(lParam),HIWORD(lParam)));
break;
case WM_SIZE:
pWnd->OnSize();
break;
case WM_SETTEXT:
pWnd->OnSetText();
break;
}
EndLjfDialogProc:
if(IsWindow(hWnd))
return CallWindowProc(oldWndProc, hWnd, uMsg1, wParam, lParam);
else
return HTNOWHERE;
}
四. DEMO下载
http://download.csdn.net/detail/ljfblog/6510269