HOOK自绘原理

做“HOOK文件打开/保存对话框”的过程中,我首先研究了界面库的相关知识。界面库一般都是由C/C++这种中低级语言编码,这是因为在Windows下的界面库实现技术大都以直接操作控制Windows的消息和调用Windows的API为主,这就是这种中低级语言的优势了。无论何种界面库,最为根本的原理就是获得或者截获窗口的某些消息,按照自己的需要处理这些消息,画出自己需要的界面。

按照Windows下的界面库的使用方法来分类,可以分为两种: 1、 通过派生、继承界面库中的类来使用库。这类界面库现在是占绝大多数。这类界面库通常可以对同种类型的控件、窗口自己控制显示风格。这种类型的界面库典型的代表就是GuiToolkit、ProfUIS。 2、 通过Link头文件,使用DLL来使用的界面库。这类界面库一般都是商业化的界面库。这类界面库一般对于同种类型的控件、窗口都是显示统一的风格。这种类型的界面库的典型代表是Skin++、AppFace。

上面的分类,其实同时也代表着两种界面库实现技术,也就是获取用于自绘窗口的消息的两种来源: 1、 通过子类化、超类化改变窗口风格。

其实就是调用Windows的API SetWindowLong或者通过类的派生和继承来改变Windows窗口的默认的消息处理函数。 2、 使用HOOK技术改变Windows的默认消息处理。

 

一、SetWindowLong  SetWindowLong(HWND hWnd,                    //需要改变UI的窗口的窗口句柄 Int nIndex,                                                           //替换窗口的默认消息处理函数时为GWL_WNDPROC Long dwNewLong)                                           //新的默认的窗口消息处理函数 调用这个API函数可以替换一个窗口的默认的消息处理函数,这样就可以在新的窗口消息处理函数中截获到目标窗口的相关消息,然后根据需要处理这些消息。这个API用于获取当前窗口的消息,它不能获取窗口中子窗口的消息。  这种类型的界面库,一般是开源的或者是提供了头文件和Lib文件的界面库。 这个Windows API大家很可能很少直接调用,但是它的封装――――SubclassWindow这个成员函数,我想大家都使用过。先让我们看看各种类型的子类化过程中的SetWindowLong都藏在什么地方,它们是如何工作的。 1、 MFC下的实现 在MFC程序中,可以先在工程中添加一个用于子类化的窗口类,然后就可以通过ClassWizard这个工具来完成剩下的子类化的工作了。我们以一个自定义的Button类CMyButton类来举例。在打开ClassWizard为我们的基于Dialog的工程中的一个Button资源添加一个对应的变量的时候我们就可以看到可以直接定义了CMyButton类,如下图:  我们为ID为IDC_BUTTON1的一个按钮资源定义一个类型为CMyButton的成员变量m_MyBtn。这时候ClassWizard就会在重载的虚函数DoDataExchange中为我们添加上一条语句,如下: void CMFCSampleDlg::DoDataExchange(CDataExchange* pDX) {  CDialog::DoDataExchange(pDX);  //{{AFX_DATA_MAP(CMFCSampleDlg)  DDX_Control(pDX, IDC_BUTTON1, m_MyBtn);//这就是ClassWizard为我们添加的  //}}AFX_DATA_MAP } 让我们来看看DDX_Control这个函数为我们做了什么,在MFC的源代码DLGDATA.CPP文件中我们能找到这个函数的源代码: void AFXAPI DDX_Control(CDataExchange* pDX, int nIDC, CWnd& rControl) {  if (rControl.m_hWnd == NULL)    // not subclassed yet  {  …… …   //注意看看,噢,原来SubclassWindow在这里   if (!rControl.SubclassWindow(hWndCtrl))   {    ASSERT(FALSE);      // possibly trying to subclass twice?    AfxThrowNotSupportedException();   } …… …  } }

让我们再看看MFC下的SubclassWindow这个成员函数的实现,在MFC的源代码wincore.cpp中可以看到所有MFC下窗口类的基类CWnd中的SubclassWindow的实现: BOOL CWnd::SubclassWindow(HWND hWnd) {  if (!Attach(hWnd))   return FALSE;

 // allow any other subclassing to occur  PreSubclassWindow();

 // now hook into the AFX WndProc  WNDPROC* lplpfn = GetSuperWndProcAddr();  //注意看看,原来它也是使用SetWindowLong啊  WNDPROC oldWndProc = (WNDPROC)::SetWindowLong(hWnd, GWL_WNDPROC,   (DWORD)AfxGetAfxWndProc());  ……  …

 return TRUE; } 上面的源代码跟踪说明,ClassWizard为我们自动完成的子类化工作中,其实也是调用的SetWindowLong这个Windows API。当然在MFC下你也可以自己调用SubclassWindow这个成员函数去手动的完成子类化。

 

2、 ATL/WTL下的实现 在ATL的源代码中,文件ATLWIN.H文件中,我们可以找到所有ATL窗口类的基类CwindowImplBaseT中的SubClassWindow的实现: template <class TBase, class TWinTraits> BOOL CWindowImplBaseT< TBase, TWinTraits >::SubclassWindow(HWND hWnd) {  ATLASSERT(m_hWnd == NULL);  ATLASSERT(::IsWindow(hWnd));  m_thunk.Init(GetWindowProc(), this);  WNDPROC pProc = (WNDPROC)&(m_thunk.thunk);  //注意看看,它也是调用SetWindowLong的!  WNDPROC pfnWndProc = (WNDPROC)::SetWindowLong(hWnd, GWL_WNDPROC, (LONG)pProc);  if(pfnWndProc == NULL)   return FALSE;  m_pfnSuperWindowProc = pfnWndProc;  m_hWnd = hWnd;  return TRUE; } 在ATL/WTL没有MFC下的那种自动子类化的机制,如果需要子类化一般都是直接调用SubclassWindow这个成员函数。

 

二、SetWindowsHookEx SetWindowsHookEx( int idHook,                      //HOOK的类型 HOOKPROC lpfn,                                               //HOOK的回调函数 HINSTANCE hMod,                                            //应用程序的实例句柄 DWORD dwThreadId)                                        //线程的ID SetWindowsHookEx这个API可以设置很多种类型的HOOK,它们分别在不同的时间截获线程ID为dwThreadId的线程的不同消息。在编写界面库的时候一般设置类型为WH_CALLWNDPROC的HOOK,用以在窗口处理消息之前获得消息并进行处理。

在HOOK的CALLBACK函数中可以获得窗口句柄,进而获得窗口的类型和窗口的风格,这样就可以知道需要处理的消息类型。

一般都是从截获WM_CREATE消息开始,然后处理WM_PAINT、WM_NCPAINT等等和UI有关的消息以达到自绘窗口UI的目的。 使用HOOK技术的界面库中当然也可以使用SetWindowLong技术,在C++写的界面库中一般是在获得了窗口句柄以后,即使用SetWindowLong技术将窗口句柄关联到一种特定的窗口类上。并且可以根据窗口的风格让同一个类做出不同风格的显示效果,可惜的是现在市面上的界面库都很少进行这种比较繁琐的区分工作。 这种类型的界面库一般都是以DLL文件格式出现,现在最为流行的是将界面库封装为一个COM DLL,在应用程序中创建这个COM组件,获得相关的接口,调用相关的接口函数来为应用程序安装HOOK。在做“Picasso风格的文件打开/保存对话框”过程中,我们一直以Yahoo Messenger的Save对话框作为参考,其实Yahoo Messenger就是使用的这种类型的界面库的。Yahoo messenger的界面库截获了进程中所有线程的消息,并做了相关的处理,所以它可以截获一些系统的窗口的创建消息和UI相关的消息,以达到改变Windows系统窗口的显示风格的目的。

 

最后介绍一下几个比较好的开源的界面库或者例子。 1、 ClassXP(http://www.yonsm.net/read.php?26) 一个个人的开源界面库,C语言写的,不完善,使用HOOK技术。Yahoo messenger的界面库就类似于这个界面库,只不过Yahoo messenger做的更完善而已。 2、 ProfUIS(http://www.codeproject.com/docking/prod_profuis.asp) 一个部分开源的界面库,有完善功能的商业版。比较完善,MFC写的,使用的是SetWindowLong技术。 3、 GuiToolkit(http://www.codeproject.com/library/guitoolkit.asp) 一个开源的界面库,比较完善,MFC写的,使用的是SetWindowLong技术。 4、 MSDN中的ControlSpy例子。 MSDN中的一个例子,用于了解各种控件的消息。

时间: 2024-08-02 20:19:10

HOOK自绘原理的相关文章

界面组合SDK中Hook机制实现原理

在前一篇简要介绍了基于Flex的界面组合SDK,其中使用Hook机制实现UI Part生命周期管理. Master-Details关系构建和UI Part注入.Hook即钩子,其作用可以理解为通过透明的方式为某一对象挂 上额外的功能,从而实现透明扩展.Hook机制可以应用于各种平台,不限语言,其传递的是一种思想. 利用Hook机制实现这些功能的最大优点是,在提供这些功能的同时不增加用户复杂性,它能够完全兼容 基于Flex组件的设计,使用户丝毫感觉不出来我们正在使用SDK.也就是说,这种Hook机

HTML解析原理概括(转载)

HTML解析原理 标准的web前端工程师需要知道 ◎浏览器(或者相应播放器)的渲染/重绘原理  这我得加把劲了.我还真的说的不是很清楚,我就G下,结果不是很多,找到了有一个,就记下来了... 以下部分来自handawei-javaeye的blog:   Web页面运行在各种各样的浏览器当中,浏览器载入.渲染页面的速度直接影响着用户体验 简单地说,页面渲染就是浏览器将html代码根据CSS定义的规则显示在浏览器窗口中的这个过程.先来大致了解一下浏览器都是怎么干活的: 1. 用户输入网址(假设是个h

CI框架源码解读之利用Hook.php文件完成功能扩展的方法_php实例

本文实例讲述了CI框架源码解读之利用Hook.php文件完成功能扩展的方法.分享给大家供大家参考,具体如下: 看了hook.php的源码,就知道CI使用hook来进行扩展的原理了. hook的基本知识 http://codeigniter.org.cn/user_guide/general/hooks.html CI中hook的使用经历了一个:开启hook,定义hook,调用hook,执行hook的过程. 手册中已经告知了开启.定义.调用的方法.那么hook的实现原理是啥呢. <?php if

《Android安全技术揭秘与防范》—第8章8.5节Hook检测/修复

8.5 Hook检测/修复Hook的目的是为了对目标进程函数的替换和注入,Hook的危害是巨大的,Hook后的应用程序毫无安全可言.其实,自从PC时代起,Hook与反Hook一直就是一个旷日持久的战争.那么对于刚发展不久的Android操作系统安全方向而言,Hook的检测与修复无疑是给Android安全研究人员带来了巨大的挑战.本节我们就具体地看看,就Android操作系统而言,如何检测一个进程是否被Hook了,如何修复被Hook的进程消除其安全隐患. 8.5.1 Hook检测上面演示了很多的H

windows下VC界面 DIY系列1----写给想要写界面的C++程序员的话

        很早就想写关于C++ UI开发的一系列博文,博客专栏刚审核通过,就马上开始刷博文,不能辜负自己的一番热血,我并不是写界面的高手,只想通过写博文提高我自己的技术积累,也顺便帮助大家解决界面开发的瓶颈. 能来到这里看我写文章, 第一说明你是windows下开发的程序员! 第二你对漂亮软件界面开发感兴趣! 第三或许你也像我当初一样对C++软件界面开发 无所适从!或许我写的不专业,但是我只想让利用C++编写客户端界面的新手程序员们对C++界面开发没有误区 . 我是从windows开发出身

面向.NET开发人员的Ajax 技术平台策略(2)

ajax|策略|技术平台 2. Anthem.NET 目前是1.0版本,其设计理念是通过另外一个思路,遵循这样的理念--既然ASP.NET的各个标准控件没有实现提交功能,那么我可以产生一个提交的接口,然后继承原来的标准控件,然后再实现这个接口,这样每个控件都可以向服务器端单独进行提交. 每个控件的发生过程类似MagicAjax.NET,Anthem.NET提供了各个控件Javascript端的提交函数-这等于也截取了__doPostBack,之后Anthem.NET 还提供了完善的客户端的事件比

Web前台开发工程师如何定位自己

先给前台开发工程师的工作下个一句话定义:运用前端技术,实现体验的良好传达.如果在前面加上 Web,那么是针对 Web 这个领域的,主要是互联网,也可以将移动通信网络和其他传媒网络(比如IPTV)包含在内,因为其理念是一致的. 现在要在未毕业的学生中找到一个符合技能条件的 Web 前台开发工程师可以说是少之又少.而相关领域的从业者,又因为不被重视.干杂活.薪水低等原因,觉得选错了行当,又停止了在这个方向上的努力学习.最终导致企业招不到一位满意的 Web 前台开发工程师.这涉及到两个定位的问题,即

Web前端工程师定位浅谈

先给前端工程师的工作下个一句话定义:运用前端技术,实现体验的良好传达.如果在前面加上Web,那么是针对Web这个领域的,主要是互联网,也可以将移动通信网络和其他传媒网络(比如IPTV)包含在内,因为其理念是一致的. 现在要在未毕业的学生中找到一个符合技能条件的Web前端工程师可以说是少之又少.而相关领域的从业者,又因为不被重视.干杂活.薪水低等原因,觉得选错了行当,又停止了在这个方向上的努力学习.最终导致企业招不到一位满意的Web前端工程师.这涉及到两个定位的问题,即1)企业如何给Web前端工程

Delphi中拦截其它程序的网络数据封包

有时候我们需要对其它应用程序发送和接收的网络数据进行拦截,比如要对IE发送的HTTP头进行分析,得到请求的地址等.这次我们可以用一些例如WPE, Sniffer之类的工具来达到目的.但是工具功能有限,要想实现更强大的功能,还是我们自己动手吧. 拦截网络数据封包的方法有三种,一是将网卡设为混杂模式,这次就可以监视到局域网上所有的数据包,二是HOOK目标进程的发送和接收的API函数,第三种方法是自己实现一个代理的DLL.在这里我们使用HOOK API的方法,这样易于实现,而且也不会得到大量的无用数据