COM 技术内幕 - 第 10 章 EXE 中的服务器
一、使用 EXE 实现服务器,需要跨进程边界进行访问。
二、跨越进程边界的接口需要考虑的条件:
1)一个进程需要调用另一个进程的过程
2)在进程间传递数据
3)进程间通信对于用户是透明的
三、代理/存根 DLL
用户仅能访问当前进程的代码和数据,代理 DLL 提供了和对象相同的行为接口集合,代理将用户请求调度并通过信道穿越进程边界,目标进程的存根 DLL 则负责反调度,调用相关的对象,并在对象返回数据时,包装数据进行回传。
四、使用 IDL/MIDL
使用 IDL 接口定义语言来描述接口,并通过 MIDL 编译器生成代理和存根 DLL。
代理和存根 DLL 适用于标准调度,COM 另支持自定义的调度。
MIDL 编译器首先生成相应代理、存根 DLL 的 C 代码,对其进行编译和链接后可得到相应的代理和存根 DLL。
五、优先使用 IDL 描述接口而不是手工实现
IDL 同时支持类型库的建立。
六、IDL 的语法规则
1)使用 [] 方括号对来限制 IDL 的区块。
2)关键字:
object 标识一个 COM 接口,未包含该关键字的接口则属于 DCE RPC 接口。
[
object,
uuid(string-uuid),
helpstring("Lines 1.0 Type Library")
pointer_default(unique)
]
interface interface-name : base-interface
{
…
}
uuid 定义接口的 IID。
helpstring 涉及到类型库,和实现进程外服务器无直接关系。
pointer_default 关键字告知 MIDL 编译器指针参数的默认处理规则,该关键字具有三个不同选项:
1、ref 指明非空引用指针,且指向地址不能更改。
2、unique 指针可以为空,地址不能更改
3、ptr 常规指针
3)出参和入参
MIDL 可以使用 in 和 out 参数属性。
1、[in] 入参仅需被调度,无需返回
2、[out] 出参仅需被返回,无需调度,必须为指针
3、[in, out] 同时为出参和入参
4)字符串描述
使用 [string] 表示该参数为字符串类型,通过查找末尾的空字符串决定复制的数据长度。按约定,COM 使用 wchar_t 宽字符来存储字符串。
5)import 引入其他 .idl 文件且没有重复定义的问题。
6)运行时的参数数据长度指定 size_is
HRESULT Next(
[in] ULONG celt,
[out, size_is(celt), length_is(*pceltFetched)]
LPOLEVERB rgelt,
[out] ULONG *pceltFetched);
上述 IDL 代码中 Next 方法的第二个参数借助前一个参数设定其存储长度。
7)IDL 中的结构
IDL 文件可定义 C/C++ 风格的结构。
IDL 需要知道指针指向的数据类型,所以不允许通过 void* 进行传递。
对于一般性的接口指针,可以通过 iid_is(riid) 加于标示。或者显式指出接口类型,这等同于常规结构类型的指定。