Inside COM读书笔记-----ExE中的服务器

1.      不同的进程

每一个EXE文件都将在不同的进程中运行,而每一个进程都有其自己的进程空间。一个进程空间中的逻辑地址0X0000ABBA所对应的物理地址将不同于另外一个进程中同一逻辑地址所对应的物理地址。所以若一个进程将地址0X0000ABBA传给另一个进程,后者访问到的逻辑单元将不是前一个进程所希望的。

同每一个EXE都有自己的进程不同,DLL将映射到链接他们的EXE文件的进程空间中,因此DLL也被称作进程中服务器,而EXE则被称作是进程外服务器。在某些情况下,EXE也被称作本地服务器。

对于跨进程边界的借口,需要考虑的:

  • 一个进程需要能够调用另外一个进程中的函数
  • 一个进程需要能够将数据传递给另外一个进程
  • 客户无需关心他所访问的服务器是进程内服务器还是进程外服务器

 

本地过程调用


对于进程间的通信,有几种不同的方法,如动态数据交换(DDE)、命名管道以及共享内存等。COM所用的方法是本地过程调用(LPC)。LPC是同一机器上不同进程间通信的一种方法。LPC是如何工作呢?他们是由操作系统实现的,由于操作系统知道同每一个进程逻辑地址空间向对应的物理地址,因此操作系统可以调用任何进程的任何函数。

 

调整


调用EXE中的函数只是第一步,另外我们还需要一种方法将函数调用的参数从一个进程空间传到另一个进程的地址空间中,这种方法称为“调整”。LPC技术可以将数据从一个进程复制到另一个进程中去,需要其他一些信息以将参数打包并传递给其他进程。比如对指针的处理将将不同于对整数的处理,将指针引用的结构赋值到另一个进程中去。

 

代理/存根DLL


当调用Win32函数时,系统实现上讲调用一个DLL的函数,而此函数将通过LPC调用windows中的实际代码,这种结构可以将用户进程同Windows代码隔离开。

COM使用的结构与此类似,客户将同一个模仿组件的DLL进行通信,这个DLL可以为客户完成参数的调整及LPC调用。一个代理就是同另外一个组件行为相同的组件,代理必须是DLL形式,因为他们需要访问客户进程的地址空间以便对传给接口函数的数据进行调整,组件还需要一个被称为存根的DLL,以从客户传来的数据进行反调整。

 

2.      IDL/MIDL简介


借助IDL(接口定义语言)可以编写接口的一个描述,然后用MIDL编译器生成代理和存根DLL。

 

IDL接口描述举例

//interface ISum

[

    object,

    uuid(B3B82DE4-4EA0-475F-A386-22C5DF7DC213),

    helpstring("IPrimeinterface"),

    oleautomation

]

interface ISum : IUnknown

{

    HRESULTSum(int x, inty, [out, retval]int* retval);

}

 

IDL的语法同C++并无太大的差异,其中object表示所定义的接口为一个com接口,关键字object是Microsoft对于IDL的一个扩展,第二个关键字uuid为相应接口的IID。第三个关键字用于将一个帮助串放到一个类型库中,

  •  Pointer_default关键字

使用IDL的目的是为了提供足够的信息,以便函数参数可以调整。Pointer_default关键字具有三个不同的选项

                        i.             Ref---将指针当成是引用对待,此时表示此指针将总是指向一个合法的地址,并可被反引用。不能为空,在调整前后他们将指向同一内存地址。

                      ii.             Unique---此类指针可以为空,并且在函数内可以修改他们的值。但不能为之指定别名。

                     iii.             Ptr---此选项指定相应的指针就是一个c指针,此类指针可以是一个别名、可以为NULL并且其值可以被修改。

  •  IDL中输入与输出参数

MIDL可以使用in、out参数属性对代理和存根代码进一步优化。对于被标记为in的参数,MIDL将指导仅仅需要将此参数指从客户传递给组件,存根代码不需要送回任何值。Out关键字告诉MIDL相应的参数仅被用来从组件向客户传回有关的数据。代理不需要对输出参数指进行调整也不需要将此值传送给组件。

  • IDL中的字符串

Com中对于字符串的标准约定是使用Unicode字符及wchar_t。

  • IDL中的import关键字

Import用于将其他IDL文件中的定义包含到当前文件中,同C++中的预处理指令#include命令式类似的,

 

MIDL编辑器


           可以用如下命令编译idl文件

           Midl foo.idl

           Idl文件有一个library语句,将生成一个类型库。

 

代理/存根的登记

3.      本地服务器的实现


EXE无法输出函数,所有在进程中服务器依赖的一些输出函数:

DllGetClassObjectPRIVATE

    DllRegisterServerPRIVATE

    DllUnregisterServerPRIVATE

    DllCanUnloadNow  PRIVATE

需要找到替代物,替换DllCanUnloadNow是很简单的,exe不是被动的,可以对自己控制生命期。DllRegisterServer和DllUnregisterServer可以直接通过exe文件提供相应的命令行参数,来完成自登记。

 

类厂的启动


COM通过维护一个关于被登记的类厂的内部表格,当客户用合适的参数调用CoGetClassObject时,COM将首先检查此关于类厂的私有表格,已得到与客户请求的CLSID相应的类厂,若存在将进行查找并启动EXE,EXE可以调用COM函数CoRegisterClassObject来完成类厂的登记。只需要建立相应的类厂并将其接口指针传给CoRegisterClassObject即可。

  • CoRegisterClassObject

第一个参数是被登记类的CLSID,第二个和第三个参数是一起使用的,第四个参数表示EXE的单个实例能否支持一个组件的多个实例,他能提供单个组件REGCLS_SINGLEUSER和CLSCTX_LOCAL_SERVER,可以支持多个组件的实例,REGCLS_MULTI_SEPARATE。

 

CoRegisterClassObject(__in REFCLSID rclsid,

    __inLPUNKNOWN pUnk,

    __inDWORD dwClsContext,

    __inDWORD flags,

    __outLPDWORD lpdwRegister

);

  • 类厂的释放

当服务器被关闭时,他必须冲内部表格中删除相应的类厂,使用COM库德CoRevokeClassObject。

 

对LockServer的修改


           进程中服务器将输出函数DllCanUnloadNow,用此函数以决定能否从内存中讲服务器卸载。对于本地服务器,需要对LockServer进行修改,其原因在于:DLL不能控制他们的生命期,他们的装载和卸载都是由另外的EXE文件完成。但EXE本身可以控制,当退出的时候发送退出消息即可。C和C++程序的标准入口点为main。程序的执行将从main函数开始,当main退出时,程序也就终止了。Windows程序通过入口函数WinMain,通过引入windows消息循环来不致EXE退出,

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

iot-genius

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值