用了这么长时间的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)"和" " .其它默认
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编译通过
下面是程序的运行截图