界面库技术概述

原创 2006年06月04日 14:39:00

在做“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中的一个例子,用于了解各种控件的消息。

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

C++ Thunk技术(初学版)

今天看到一篇关于Thunk技术的文章,特转过来。这里想说的是:代码中的关键点为用指令jmp pFunc跳转到你想要执行的函数pFunc。指令“jmp xxxx”占5个字节,代码中用了个一字节对齐的结构...

深入解析Windows窗口创建和消息分发

Windows GUI采用基于事件驱动的编程模型,事实上几乎所有的界面库都是这样做的。在纯粹的Window32 SDK编程时代,人们还可以搞懂整个Windows窗体创建和消息的流通过程,但是在现在各种...
  • Qsir
  • Qsir
  • 2017-07-25 08:54
  • 137

位运算 实现加法

用位运算实现加法也就是计算机用二进制进行运算,32位的CPU只能表示32位内的数,这里先用1位数的加法来进行,在不考虑进位的基础上,如下 1 + 1 = 0 1 + 0 = 1 0 +...

JEECG V3.0版本 (工作流在线定义+UI快速开发库+代码生成器) 全新架构技术,漂亮的界面+智能代码生成+智能工作流

简要说明 JEECG V3.0版本推翻原有SSH2架构,采用SpringMVC+Hibernate+Spring jdbc基础架构, 采用面向声明的开发模式,基于泛型方式编写极少代码即可实现复...

【转】翻转吧,界面!-3D UI概述

随着技术发展,界面越来越简易化,二维的操作难以提供更好的交互体验,因此平面化的操作界面转向3D 空间维度发展,我们又迎来的了新的时代,一场革命性新的交互体验;本次分享,作为3D UI的概述,以下就整体...

Android零基础入门第16节:Android用户界面开发概述

Android提供了非常丰富的用户界面组件,借助于这些用户界面组件,开发者可以非常方便地进行用户界面开发,而且可以开发出非常优秀的用户界面。

(四) 用户界面开发过程概述

本节概述了用户界面设计的三个阶段以及每个阶段的相关内容。应用程序用户界面和用户体验 首先,我们得先澄清一下术语“用户界面”和“用户体验”的相关含义。 应用程序的用户界面通常包括那些,用户能够在他们的...

银光用户界面控件Essential studio for Silverlight功能概述

Essential Tools for Silverlight是一款功能强大的用户界面控件,包含了很多常用的用户界面元素,如:Ribbons、多种编辑器、文件上传控件、tab控件、Group Ba...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)