在对网页中的 ActiveX 控件进行操作的时候,需要获得该元素对应的 ActiveX 对象接口的指针。以 Flash 对象为例,代码类似于这个样子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | IHTMLElement* elem = NULL; // get the element.... IHTMLObjectElement* objelem = NULL; elem->QueryInterface(IID_IHTMLObjectElement, (PVOID*)&objelem); IShockwaveFlash* swf = NULL; objelem->get_object((IDispatch**)&swf); BSTR bsMovie = NULL; swf->get_Movie(&bsMovie); OutputDebugStringW(bsMovie); ::SysFreeString(bsMovie); swf->Release(); objelem->Release(); elem->Release(); |
有趣的是,我们还可以不经过 get_object,只使用 QueryInterface 就能获得 IShockwaveFlash 的接口指针进行调用,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 | IHTMLElement* elem = NULL; // get the element.... IShockwaveFlash* swf = NULL; elem->QueryInterface(IID_IShockwaveFlash, (PVOID*)&swf); BSTR bsMovie = NULL; swf->get_Movie(&bsMovie); OutputDebugStringW(bsMovie); ::SysFreeString(bsMovie); swf->Release(); elem->Release(); |
我很怀疑这里面 get_Movie 的可靠性,便跟踪了进去,发现:

可以看到,目标函数的地址并不是位于 Flash OCX 领空之中的,而是在 mshtml 之中一个名为 TearoffThunk21 的函数——事实上,从字面上来看,它已经说明自己是个 thunk 了。那么,继续检查这个函数,它的实现如下:
1 2 3 4 5 6 7 | mov eax, [esp+arg_0] mov dword ptr [eax+20h], 15h mov ecx, [eax+0Ch] mov [esp+arg_0], ecx mov ecx, [eax+10h] mov ecx, [ecx+54h] jmp ecx |
简单分析一下,这个 thunk 做了下面这几件事:
- 从堆栈中获取 this 指针;
- 获取当前函数(get_Movie)在虚表中的索引 15h;
- 从 this 指针中获取真正的 ActiveX 对象指针;
- 将堆栈中的 this 指针用真正的对象指针替换;
- 从 this 指针中获取某个指针,从下文来看,这个指针应该是 ActiveX 对象的虚表指针;
- 从虚表中获取当前函数(get_Movie)的地址(54h / 4 == 15h);
- 跳转到真正的函数中执行。
到这里,结论很自然地就出来了——通过直接 QueryInterface 而得到的 IShockwaveFlash 接口指针是个假的。那么,这个假的接口指针存在的意义又是什么呢?当然,存在即合理,因为有的 IHTMLElement 是查询不到 IHTMLObjectElement 接口的,比如由 <embed></embed> 标签盛放的 Flash 对象。
楼主有没有比较过两个方法得到的指针?
我怀疑通过QueryInterface()得到的也是真的IShockwaveFlash接口指针. 只不过这个flash对象是被Trident以aggregate的方式生成的.
如果真的是,那么 1、3、4 步完全可以省去。