用了这么长时间的MFC,感觉不错是不错,可是那个界面呀,真让人难受啊.虽然在VS2008中有了提供,但是还是不理想,所以就想找个皮肤库,现在比较好用的(个人感觉)SkinMagic,不过它不支持VS2008悲哀呀.所以打算自己写一个.
关于那些基础知识和HOOK的知识我就不详细说了,可以去
http://www.fengfly.com/plus/view-171863-1.html
这里来看下.虽然这个是用C#但还是让我收获不小
我们现在采用在要HOOK方式来给界面换肤,下面就来说一具体的步骤,
这里用到的是VS2008,不采用VC 6.0这样就可以直接要VS 2008中使用了.由于也可以让别人来使用,所以我们就用DLL的方式来换肤.
具体步骤:
1.新建一个Dll工程,这里我命名的是"dll",选择"带静态链接 MFC 的规则 DLL(R)",然后完成
2.打开工程的主要cpp文件,我的是dll.cpp.然后添加一个RunHook函数
/******************************************************************** 函数名称: 安装钩子 ===================================================================== 参数说明: dwThreadID当前进程的ID,hModule默认方式0 --------------------------------------------------------------------- 文件作者: King.Sollyu *********************************************************************/ HHOOK hWndHook ; //全局变量 HINSTANCE hMod = NULL; BOOL __stdcall RunHook( DWORD dwThreadID, HMODULE hModule = 0) { hWndHook = SetWindowsHookEx( WH_CALLWNDPROC, (HOOKPROC) HOOKProc, hMod , dwThreadID ); return TRUE; }
这个函数是我们的主要函数,它是我们的主入口,下面来看一下 HOOKProc 为个钩子函数
/******************************************************************** 函数名称: 定义钩子函数 ===================================================================== 参数说明: 无 --------------------------------------------------------------------- 文件作者: King.Sollyu *********************************************************************/ LRESULT CALLBACK HOOKProc( int nCode, WPARAM wParam, LPARAM lParam ) { PCWPSTRUCT wc = (PCWPSTRUCT) lParam; HWND hWnd = wc->hwnd; if( hWnd ) { wchar_t ClassName[MAX_PATH] =_T(""); GetClassName( hWnd, ClassName, MAX_PATH ); CWnd *pWnd = CWnd::FromHandle( hWnd ); if (wcscmp( ClassName, _T("Edit") ) == 0) //修改编辑框的窗口函数 { CWnd *pWnd = CWnd::FromHandle( hWnd ); pWnd->ModifyStyleEx(WS_EX_CLIENTEDGE,0,0); //取消编辑框的边框 WNDPROC WndProc; WndProc = (WNDPROC) GetWindowLong( hWnd, GWL_WNDPROC ); CDrawEdit *pEdit=(CDrawEdit*)GetWindowLong(hWnd,GWL_USERDATA); if (pEdit != NULL&& pEdit->m_Flag==1 ) { SetWindowLong(hWnd, GWL_USERDATA, 0); SetWindowLong( hWnd, GWL_WNDPROC, (LONG)pEdit->m_OldProc); pEdit->m_OldProc = NULL; delete pEdit; } else if (pEdit == NULL ) { if( WndProc !=EditWindowProc ) { pEdit = new CDrawEdit(); pEdit->m_OldProc = WndProc; SetWindowLong(hWnd,GWL_USERDATA,(long)pEdit); WndProc = (WNDPROC) SetWindowLong( hWnd, GWL_WNDPROC, (LONG) EditWindowProc); } } } } return CallNextHookEx( hWndHook, nCode, wParam, lParam ); }
这样我们会发现CDrawEdit这个是这个错误.CDrawEdit是我们自定义的一个Edit类,它的主要功能是实现自绘Edit框.下面是CDrawEdit在代码
/******************************************************************** 函数名称: 定义编辑框类 ===================================================================== 参数说明: 无 --------------------------------------------------------------------- 文件作者: King.Sollyu *********************************************************************/ class CDrawEdit { public: WNDPROC m_OldProc; //记录编辑框的窗口函数 int m_Flag; public: CDrawEdit() { m_OldProc = NULL; m_Flag = 0; } HBRUSH CtlColor(HWND hWnd,HDC hDC, UINT nCtlColor) { CDC* dc = CDC::FromHandle(hDC); //获取画布对象 CRect rect; ::GetClientRect(hWnd,rect); //获取客户区域 rect.InflateRect(1,1,1,1); //将客户区域增大一个像素 CPen pen(PS_SOLID,1,RGB(0,255,0)); //创建画笔 dc->SelectObject(&pen); CBrush brush (RGB(0,255,0)); //创建画刷 dc->FrameRect(rect,&brush); //绘制边框 return brush; } }; /******************************************************************** 函数名称: 编辑框窗口函数 ===================================================================== 参数说明: 无 --------------------------------------------------------------------- 文件作者: King.Sollyu *********************************************************************/ LRESULT __stdcall EditWindowProc(HWND hWnd,UINT Msg,WPARAM wParam, LPARAM lParam ) { CPoint pt; CDrawEdit *pEdit=(CDrawEdit*)GetWindowLong(hWnd,GWL_USERDATA); switch (Msg) { case WM_PAINT: { pEdit->CtlColor(hWnd,::GetDC(hWnd),0); break; } case WM_DESTROY: { WNDPROC procOld=pEdit->m_OldProc; SetWindowLong(hWnd,GWL_WNDPROC,(long)procOld); //恢复原来的窗口函数 CWnd* pWnd = ::CWnd::FromHandle(hWnd); //将按钮对象与句柄分离 if (pWnd) { pWnd->Detach(); } pEdit->m_Flag = 1; return 1; } default : { break; } } return CallWindowProc(pEdit->m_OldProc, hWnd, Msg, wParam, lParam ); }
把必要的函数在头文件中声明下,或者放在RunHook 这个函数之前.
添加卸载钩子函数
/******************************************************************** 函数名称: 卸载钩子 ===================================================================== 参数说明: 无 --------------------------------------------------------------------- 文件作者: King.Sollyu *********************************************************************/ BOOL __stdcall StopHook() { UnhookWindowsHookEx(hWndHook); return TRUE; }
3.导出函数
打开dll.def,在下面添加
RunHook StopHook
下面是整个dll.def的内容
; dll.def : 声明 DLL 的模块参数。 LIBRARY "dll" EXPORTS ; 此处可以是显式导出 RunHook StopHook
然后编译一下.本人测试通过
这样我们就可以实现所有的"编辑框"的换肤.现在再新建一个exe工程来测试一下我们dll是不是可以正常的使用
1.新建一个exe工程添加到当前的工程中.这里我选的是"基于对话框(D)"和"在静态库中使用 MFC(E) " .其它默认
2.在对话框中添加一个"编辑框"
3.在exeApp.cpp中包含dll.h和使用隐式连接dll.lib,#include "../dll/dll.h" #pragma comment(lib,"../debug/dll.lib")
并在InitInstance函数中添加代码
RunHook(GetCurrentThreadId());
编译一下,本人用win 7旗舰版+VS 2008编译通过
下面是程序的运行截图