MASM32开发COM组件【三】

用汇编语言访问com对象

大量的细节使得Com看上去很复杂,但是使用起来却很简单。最难的部分就是理解里面的数据结构,尽管COM是语言无关的,但是他借用了很多c++的术语来描述自己。

为了能使用某个对象的com接口函数,你必须首先要从类厂中创建这个对象,并且让他来返回接口指针。这个过程被CoCreateInstance这个 API函数完成。当你使用完接口时,要调用Release方法。一个COM对象可以看作是一个服务,调用com的应用程序就是他的客户端。

在调用com接口函数之前,你需要了解接口是什么,一个com接口就是一个函数指针表,我们还是从IUnknown接口开始,如果你创建了一个组件导出了 IUnknown接口,那么你就有了一个全功能的com对象。IUnknown有三个基本的几口方法,既然所有的接口都是从它派生出来,那么我们一定要记住,一个接口实际上就是一个函数指针成员组成的结构体。 
例如: 
IUnknown STRUCT DWORD 
; IUnknown methods 
IUnknown_QueryInterface   QueryInterface_Pointer ? 
IUnknown_AddRef     AddRef_Pointer ? 
IUnknown_Release     Release_Pointer ? 
IUnknown ENDS

它只有12个字节长,它具有3个DWORD指针来指向实际的实现函数。对于虚函数表,你一定听说过,这些指针定义如下,因此我们可以让masm在编译我们的调用时进行一些类型检查。

QueryInterface_Pointer   typedef ptr QueryInterface_Proto 
AddRef_Pointer     typedef ptr AddRef_Proto 
Release_Pointer     typedef ptr Release_Proto

最后我们定义我们的函数如下: 
QueryInterface_Proto   typedef PROTO :DWORD, :DWORD, :DWORD 
AddRef_Pointer     typedef PROTO :DWORD 
Release_Pointer     typedef PROTO :DWORD

为了保持masm32松散的类型检查一致,函数参数都定义为dword

定义接口是一个相当大的编辑就是,masm不支持前向引用。因此,我们不得不颠倒一下定义的顺序。先定义函数头,再定义函数指针,最后定义接口。实际上在使用接口时,你需要一个指向它的指针。 
CoCreateInstance函数能用来直接返回一个接口指针。它实际上指向了拥有接口的对象。这个结构看上去如图所示:

这个结构里有大量的间接访问,使用宏可以简化它。 
当客户端调用COM库创建com组件时,它传进了一个地址用于存放对象指针。这个就是我们所说的ppv. 从c++的角度来讲,叫做指向指针的指针,void类型代表无类型。它保存了另一个指针pv的地址。pv指向了虚函数表。

例如:我们使用CoCreateInstance函数成功的返回了一个接口指针ppv,我们想看下它是否支持其他的接口,我们可以调用QueryInterface方法。用c++的方法描述QueryInterface如下: 
(HRESULT) SomeObject::QueryInterface (this:pObject, IID:pGUID, ppv2:pInterface)

用汇编写法如下:

01  ; get pointer to the object 
02  mov eax, ppv  
03  ; and use it to find the interface structure 
04  mov edx, [eax]  
05   
06  ; push the function parameters onto the stack 
07  push OFFSET ppv2 
08  push OFFSET IID_ISomeOtherInterface 
09  push dword ppv  
10   
11  ; and then call that method 
12  call dword ptr [edx + 0]  
13   
14  ;使用invoke调用简化如下: 
15   
16  ; get pointer to the object 
17  mov eax, ppv  
18  ; and use it to find the interface structure 
19  mov edx, [eax]  
20  ; and then call that method 
21  invoke (IUnknown PTR [edx]).IUnknown_QueryInterface, ppv, 
22      ADDR IID_SomeOtherInterface, ADDR ppv_new  
23 

注意IUnknown PTR [edx]这个类型转换,是让编译器知道使用哪个结构来得到QueryInterface函数在虚表中的正确偏移。其中有一个模糊的地方,注意我修改了函数名字为"IUnknown_QueryInterface",这个名字修饰时必要的。当你有一个大的com工程,有许多相似的接口,你就会遇到麻烦。不同的接口对应不同的方法表示,是非常有效的。

coinvoke 宏,这个宏定义在oaidl.inc文件中。使用它,可以进一步简化com调用。

01  ;——————————————————————— 
02  ; coinvoke MACRO 
03  ; 
04  ; 
05  ; pInterface    pointer to a specific interface instance 
06  ; Interface     the Interface’s struct typedef 
07  ; Function      which function or method of the interface to perform 
08  ; args          all required arguments 
09  ;                   (type, kind and count determined by the function) 
10  ; 
11  coinvoke MACRO pInterface:REQ, Interface:REQ, Function:REQ, args:VARARG 
12      LOCAL istatement, arg 
13      FOR arg, <args>     ;; run thru args to see if edx is lurking in there 
14          IFIDNI <&arg>, <edx
15              .ERR <edx is not allowed as a coinvoke parameter> 
16          ENDIF 
17      ENDM 
18      istatement CATSTR <invoke (Interface PTR[edx]).&Interface>,<_>,<&Function, pInterface> 
19      IFNB <args>     ;; add the list of parameter arguments if any 
20          istatement CATSTR istatement, <, >, <&args>  
21      ENDIF  
22      mov edx, pInterface 
23      mov edx, [edx
24      istatement 
25  ENDM 
26  ;——————————————————————— 
27    
28   
29  ;因此,前面的QueryInterface方法调用就可以简化成: 
30   
31  coinvoke ppv ,IUnknown, QueryInterface, ADDR IID_SomeOtherInterface,  
32  ADDR ppnew 
注意这里名字修饰是隐藏在宏中处理的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值