最近看了一篇讲ATL Thunk技术的好文章(下载),收获较多,在此做一个总结。
Thunk技术的由来
我们知道,经典的Windows程序设计采用纯Windows API来实现,创建一个窗口必须严格遵循“定义窗口类,注册窗口类,创建窗口,显示窗口,更新窗口,启动消息循环”的步骤。虽然经典的Windows程序设计在一定程度上已经体现出了OOP的思想 (对象 = 数据 + 代码),但是与当代的OOP (封装、继承、多态) 还有很大的差距。这种差距类似于C struct和C++ class之间的差距。
为了使Windows编程更加简洁高效,需要对纯Windows API进行封装。市场上的应用程序框架,比如MFC,WTL等,主要就做了这件事情。
按照什么思路来对纯Windows API进行封装呢?那就是对各个函数进行分组,比如许多函数的第一个参数都是HWND hwnd。何不把HWND hwnd作为类的成员变量,调用与hwnd相关的函数就不必再写hwnd参数了。这样就完成了第一步。
接下来问题来了:WndProc函数怎么处理呢?它不能是普通的类方法,因为编译器会给普通类方法悄悄添加一个this指针,从而导致参数格式与WNDPROC不相同。有办法,那就是在方法前面添加static,这样就能将类的WndProc传递给窗口类的lpfnWndProc字段了。这样就完成了第二步。
可是,真正问题才刚刚出现。WndProc现在是类的static方法,但是如果想要WndProc内部调用非static方法呢?要是能够把this指针传递进去就好了。首先想到的方法就是用全局变量。(不然就要增加函数参数,那句跟WNDPROC不同了) 使用全局变量有很大的弊端,那就是这个全局变量到底应该设多大呢?因为你很可能用相同的窗口类创建多个窗口,这时候WndProc都一样,只能以类对象实例的指针来区分。也有解决办法,大不了用动态内存。还有查找的问题呢?用Red-Black tree够快么?显然,这样思考下去就太复杂。就算花了大力气实现了这种思路,动态内存太慢了,查找也得花时间,这跟ATL的要求不吻合:速度快,尺寸小。
实际上ATL采用了另外一种技术来解决这个问题,当然就是“Thunk技术”。Thunk技术看似高深,那是因为关注它的人不多,资料不够充足而已。
Thunk技术的实质
Thunk就是“转换”的意思,从ATL的实现来看,它是一种采用小段汇编代码对WndProc参数hwnd进行偷梁换柱的方法。不要看到汇编就却步,因为它只涉及到两条指令:mov和jmp。博主对汇编也只停留在本科时所学的那点知识,而且忘得差不多,但是理解Thunk技术已经足够。
Thunk技术的模拟
话