在看vc知识库38期的com部分《atl布幔之下的秘密》一文中,开始的时候对thunk非常的不理解,在跟一位学长探讨之后,呵呵,主要是学到了vc下如何得到察看程序的汇编代码。终于明白了是怎么一回事。
开始对mov dword ptr [esp+0x4], pThis中[esp+0x4]指向WindowProc函数的hwnd参数非常的不理解,似乎按照函数的入栈规则,[esp+0x4]是不可能指向WindowProc函数的hwnd参数。懂得察看汇编代码后,发现在窗口建立过程只运行一次的StartWndProc中对thunk初始化(init函数)后,得到了WNDPROC pProc = (WNDPROC)&(pThis->m_thunk.thunk);(注意这里typedef LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM);)
改变窗口过程为pProc后,然后就是调用pProc,首先将4个参数压入栈中,这段汇编代码如下:
004014FF mov esi,esp
00401501 mov ecx,dword ptr [ebp+14h]
00401504 push ecx
00401505 mov edx,dword ptr [ebp+10h]
00401508 push edx
00401509 mov eax,dword ptr [ebp+0Ch]
0040150C push eax
0040150D mov ecx,dword ptr [ebp+8]
00401510 push ecx
00401511 call dword ptr [ebp-8] ;调用thunk代码
前面
push就是把参数入栈,call调用要保存返回地址,所以相当push一次。所以这个时候[esp+4]刚好就是hwnd的堆栈地址。然后通过call调用thunk代码。
Thunk代码如下:
0012FED0 mov dword ptr [esp+4],12FEC8h
0012FED8 jmp @ILT+95(ZWindow::WindowProc) (00401064)
下面就是进入了windowProc的处理过程之中。
所以这个thunk就是在 windowProc执行之前,将hwnd指向正确的地方,因为每一个window都包含一个WndProcThunk变量,所以可以保持唯一性。WNDPROC的声明形式也能保证参数的准确入栈操作。
以前的blog在csdn上,不过实在受不了csdn的blog的不稳定。本来我平时就不喜欢写东西,到我有想法写东西的时候,csdn的blog要不是打不开就是速度太慢。。。
还是vckbase这里好 -_-
我把以前我写的东东转到这里来,希望大家多多批评!!!
Q:什么是com?
A:com是指组件对象模型,是关于如何创建组件和如何通过组件建构应用程序的一个规范。
Q:什么是“客户”?
A: 对于一个应用程序或者组件,如果他使用了其他组件,那么我们称这个应用程序或者组件为“客户”。“客户”通过其他接口与组件连接。对于用户来说,一个组件就是一个接口集。用户只能通过接口才能和组件打交道。
Q:什么是接口?
A:对于com来说,接口是一个包含函数指针数组的内存结构。每一个数组元素包含的是一个由组件所实现的函数的地址。一个com组件可以提供多个接口(称之为“接口集”),而每个接口则包含了一系列函数。每一个接口都有一个唯一的接口标示符(IID)。
Q:定义接口和实现接口分开的动机是什么?
A:是把对象内部的工作细节(相对于客户而言)都隐藏起来,这样在客户代码不需要重新编译的条件下,实现接口类的内部的数据成员数目和顺序都可以改变。
Q:COM接口层次上的“多层继承”和实现层次上的“多层继承”的概况?
A:COM 是禁止多层接口继承的,一个原因是多层继承会使接口在二进制层次上无法保持“编译器无关性”。而在实现层次上,多层继承是存在的。
Q:类型库(type library)的用途?
A:类型库(符号化的idl)是一个二进制文件,他保存了能够识别COM接口的其他语言所映射的接口定义。从而使其他语言能够使用这些组件的接口。
Q:IUnknown接口中QueryInterface, AddRef, Release函数的作用是?
A:QueryInterface用来决定组件是否支持某个特定的接口。AddRef和Release用来控制接口的生命周期。若某个接口的vtbl的前3个函数不是这三个函数,那么这个接口不是一个com接口。
Q:“得知两个接口指针是不是指向同一对象的唯一方法是查询这两个接口的IUknown,然后比较结果?” 这句话不明白 p。49 《COM 技术内幕》
A:这个IUknown应该是指指向组件的指针。在QueryInterface中确保它一定指向组建对象地址的开始处。
Q:什么是CLSID?
A: 除了用GUID 来表示接口以外,还可以用GUID来标示唯一的组件(COM 实现类)。叫做CLSID。
Q:在com中经常看到某个对象同时使用-〉和.操作符同时使用的情况,why?
A:c++中存在对操作符号的重载,所以可以重载-〉号,但是好像'.'号是不能重载的(有待考察) !
A:什么是代理、残根DLL?
Q:一个代理就是同另一个组件行为相同的组件,代理必须是DLL形式的。因为它必须访问客户进程的地址空间以便对传给接口函数的参数进行调整。残根DLL对返回给客户的参数进行调整以及对客户传递来的参数进行反调整。
A:什么是双重接口、调度接口?
Q:IDispatch::Invoke函数所实现的函数集被称作一个调度接口。双重接口也是一种度接口,它使得可以通过invoke调用的函数也能够通过vtbl访问。
A:COM支持哪三种激活方式?
Q:CoGetClassObject绑定到类厂的引用。CoCreateInstance绑定到新的类实例的引用。CoGetInstanceFromFile绑定到指向文件中永久实例的引用。
A:什么是COM服务器?
Q:创建一个或者几个COM类,需要把所有的类打包并安装到系统上,这个包称之为COM类。COM服务器的形式有:进程内、进程外、windows下的系统服务。COM服务器除容纳其类的实现外,还需要1)注册和注销服务器中所有的类和他本身2)向SCM提供类对象(类厂)3)管理服务器的生命周期