IDL 文件剖析

这些 IDL 示例文件展示了接口定义的基本结构。 内存分配、自定义封送和异步消息传递只是自定义 COM 接口可以实现的部分功能。 MIDL 属性用于定义 COM 接口。 有关实现接口和类型库(包括 MIDL 属性摘要)的详细信息,请参阅《MIDL 程序员指南和参考》中的接口定义和类型库。 有关所有 MIDL 属性、关键字和指令的完整参考信息,请参阅 MIDL 语言参考

Example.idl

下面的 IDL 文件示例定义了两个 COM 接口。 通过该 IDL 文件,Midl.exe 将生成代理/存根和封送代码以及头文件。 示例之后是逐行剖析。

//
// Example.idl 
//
import "mydefs.h","unknwn.idl"; 
[
object,
uuid(a03d1420-b1ec-11d0-8c3a-00c04fc31d2f),
] interface IFace1 : IUnknown
{
HRESULT MethodA([in] short Bread, [out] BKFST * pBToast);
HRESULT MethodB([in, out] BKFST * pBPoptart);
};
 
[
object,
uuid(a03d1421-b1ec-11d0-8c3a-00c04fc31d2f),
pointer_default(unique)
] interface IFace2 : IUnknown
{
HRESULT MethodC([in] long Max,
                [in, max_is(Max)] BkfstStuff[ ],
                [out] long * pSize,
                [out, size_is( , *pSize)] BKFST ** ppBKFST);
}; 
 

这里使用 IDL import 语句引入了包含用户定义类型的头文件 Mydefs.h 和包含 IUnknown 定义的 Unknwn.idl,IFace1 和 IFace2 即从中派生。

object 属性将接口标识为对象接口,并告诉 MIDL 编译器生成代理/存根代码,而不是 RPC 客户端和服务器存根。 对象接口方法的返回类型必须是 HRESULT,以便基础 RPC 机制能够报告因网络问题而无法完成调用的错误。

uuid 属性指定接口标识符 (IID)。 每个接口、类和类型库都必须使用自己的唯一标识符来进行标识。 使用 Uuidgen.exe 实用工具为接口和其他组件生成一组唯一 ID。

interface 关键字定义接口名称。 所有对象接口都必须直接或间接派生自 IUnknown

in 方向参数指定了一个仅由调用方设置的参数。 out 参数指定传回调用方的数据。 在一个参数上同时使用两个方向属性,表示该参数既用于向方法发送数据,也用于将数据传回调用方。

pointer_default 属性为所有指针指定默认指针类型(uniquerefptr),参数列表中包含的指针除外。 如果没有指定默认类型,MIDL 会假定单指针是唯一的。 但是,如果有多级指针,即使希望默认类型为唯一的也必须明确指定默认指针类型。

在上例中,数组 BkfstStuff[ ] 是一个一致数组,其大小在运行时确定。 max_is属性指定了包含数组索引最大值的变量。

size_is 属性还可用于指定数组的大小,或如上例中的多级指针。 在此示例中,调用时可以事先不知道会返回多少数据。

Example2.idl

下面的 IDL 示例(重复使用了前面 IDL 示例中描述的接口)展示了生成接口类型库信息的各种方法。

//
// Example2.idl
//

import "example.idl","oaidl.idl"; 

[
uuid(a03d1422-b1ec-11d0-8c3a-00c04fc31d2f),
helpstring("IFace3 interface"),
pointer_default(unique);
dual,
oleautomation
] 
interface IFace3 : IDispatch
{
   HRESULT MethodD([in] BSTR OrderIn,
                   [out, retval] * pTakeOut);
}; //end IFace3 def

[
uuid(a03d1423-b1ec-11d0-8c3a-00c04fc31d2f),
version(1.0),
helpstring("Example Type Library"),
] library ExampleLib
{
  importlib("stdole32.tlb");
  interface IFace3;
  [
  uuid(a03d1424-b1ec-11d0-8c3a-00c04fc31d2f),
  helpstring("Breakfast Component Class")
  ] coclass BkfstComponent
    {
    [default]interface IFace1;
    interfaceIFace2
    }; //end coclass def

[
uuid(a03d1424-b1ec-11d0-8c3a-00c04fc31d2f),
helpstring("IFace4 interface"),
pointer_default(unique);
dual,
oleautomation
] 
interface IFace4 : IDispatch
{
[propput] HRESULT MethodD([in] BSTR OrderIn);
[propget] HRESULT MethodE([out, retval] * pTakeOut);
}; //end IFace4 def
 
}; //end library def
 

helpstring 属性是可选的,用于简要描述对象或提供状态行。 这些帮助字符串可通过对象浏览器(如 Microsoft Visual Basic 提供的帮助字符串)进行读取。

IFace3 上的 dual 属性创建了一个既是调度接口又是 COM 接口的接口。 因为它是从 IDispatch 派生的,所以双接口支持自动化,这也是 oleautomation 属性所指定的。 IFace3 导入 Oaidl.idl 以获得 IDispatch 的定义。

library 语句定义了 ExampleLib 类型库,它有自己的 uuidhelpstringversion 属性。

在类型库定义中,importlib 指令会引入已编译的类型库。 所有类型库定义都应引入 Stdole32.tlb 中定义的基础类型库。

此类型库定义演示了在类型库中包含接口的三种不同方法。 只需在库语句中引用 IFace3 即可将它包括在内。

coclass 语句定义了一个全新的组件类 BkfstComponent,其中包括两个先前定义的接口 IFace1 和 IFace2。 默认属性将 IFace1 指定为默认接口。

IFace4 在库语句中进行了介绍。 MethodD 上的 propput 属性表示该方法对同名属性执行设置操作。 propget 属性表示该方法从与该方法同名的属性中获取信息。 MethodD 中的 retval 属性指定了一个输出参数,其中包含函数的返回值。