IDL was originally part of the Open Software Foundation’s Distributed Computing Environment (DCE). It described function interfaces for Remote Procedure Calls (RPCs), so that a compiler could generate proxy and stub code that marshaled parameters between machines. MIDL is Microsoft’s IDL compiler. In addition, Microsoft developed its own Object Definition Language (ODL), which included the dispinterface keyword for specifying IDispatch’s logical interfaces. ODL scripts could be compiled by MKTYPLIB into type libraries (.TLB files), which could be used by Automation clients for early binding.
With Windows NT 4.0, Microsoft released MIDL 3.0, which merged the two languages by extending IDL (now called COM IDL) to include ODL’s features. This enabled the MIDL compiler to generate type libraries as well as proxy and stub code for marshaling. For a discussion of the differences between the older ODL and the MIDL 3.0 COM IDL, see “Introducing Distributed COM and the New OLE Features in Windows NT 4.0” by Don Box (MSJ, May 1996). For a discussion of marshaling, IDL, and the tradeoffs between custom, dispatch, and dual interfaces, I highly recommend Don Box’s December 1995 OLE Q&A column. Just remember that the article was written before Microsoft merged MIDL and MKTYPLIB.
So let’s look at what MIDL does. By default, if you declare an interface in your IDL file, MIDL will take the declared functions and generate all of the files for an RPC interface. This includes a client proxy, a server stub, and a header file. Now, if you specify the [object] attribute in your interface attribute list, MIDL will instead generate all of the proxy and stub files for a COM interface. This includes the interface proxy file (name_p.c), the type definition header file (name.h), the interface GUID file (name_i.c), and the proxy DLL file (dlldata.c). And finally, for each library block that you define within the IDL file, MIDL will generate a type library (see Figure 1).
midl.exe 对于我们给定的my.idl文件生成了5个输出文件.
my.h 类型定义头文件(如果是c/c++, 包这个头文件,就可以动态调用COMDLL的方法了)
my.tlb 类型库文件(给c/c++之外的语言用的, e.g. VB, vb是不包含头文件的,引入COMDLL的类型库就可以使用COMDLL中的方法)
my_i.c 接口GUID文件(定义了接口需要的所有GUID, 在my.h中声明为extern X_IID来使用)
my_p.c COM 接口代理实现文件
dlldata.c 代理DLL文件
如果是使用RPC调用的COMDll, 还会生成 my_c.c my_s.c
IDL文件的编写偏向于C++ coder.
除了要使用通用的数据类型外,将C++定义转成IDL定义需要以下几个规则.
cpp_quote(“#define UNICODE”)
would appear in the generated code like this:
#define UNICODE
import “moose.idl”
would be generated in the source code as:
#include “moose.h”
用[in]和[out]来描述.
void fx([in] long l1, [out] long *pl2, [in, out] long *pl3);
// pval cannot be null
long fx([in, ref] const long *pval);
Pointers that can be null are called unique pointers and are indicated by the [unique] attribute.
// pval can be null
long fx([in, unique] const long *pval);
// 完全的指针, full指针指向同一个内存地址.
long fx([in, full] long *pval1,
[in, full] long *pval2);
如果是字符串指针,要使用OLECHAR*
void MyFunction([in, string] const OLECHAR *pwszName);
传递数组, 要指定数组的长度
void Fx([in] long cItems,
[in, size_is(cItems)] short aItems[]);
void Fx([in] long cMax,
[in, out] long *pcUsed,
[in,out,size_is(cMax),
length_is(*pcUsed)] long *aValues);
COM组件被注册好后,可以用oleview.exe 来看COM组件类型库信息
oleview.exe在Microsoft SDK\Bin\和\Windows Kits\8.1\bin\x86(x64)中.
import "unknwn.idl";
[
object,
uuid(747DCA16-D34B-4527-8E47-5A4C0FF41B19)
]
interface IFastString : IUnknown
{
[helpstring("Fast Find")]
HRESULT Find([in]BSTR pszFind, [out,retval]long *pResult);
[helpstring("Fast Length")]
HRESULT Length([out,retval]long *pResult);
[helpstring("Fast Set")]
HRESULT Set([in]BSTR pszFind);
[helpstring("Fast Get")]
HRESULT Get([out,retval]BSTR* pszFind);
};
[
uuid(4BB15D85-2223-4884-B777-90E52A5AA456),
version(1.0)
]
library FastStringLib
{
importlib("stdole2.tlb");
[
uuid(EE75421D-0AC9-4f27-955D-3F2B344A8402),
helpstring("字符串处理")
]
coclass MyFastString
{
interface IFastString;
};
};
//------------------------------------------------------------------------------
// 说明
//------------------------------------------------------------------------------
/// @file MyComDll.IDL
/// @brief 定义IDL接口的例子
/**
IDL中写注释,可以用c/c++注释风格
*/
//------------------------------------------------------------------------------
// 头文件
//------------------------------------------------------------------------------
import "unknwn.idl"; ///< for HRESULT
//------------------------------------------------------------------------------
// 接口IID定义, 可以定义多个接口
//------------------------------------------------------------------------------
/// COMDLL是COM组件(接口类)的容器, 一个COMDLL中,可以有多个接口类
/**
// {FABA0150-7A61-4B90-BE31-367959D6017D}
static const GUID <> =
{ 0xfaba0150, 0x7a61, 0x4b90, { 0xbe, 0x31, 0x36, 0x79, 0x59, 0xd6, 0x1, 0x7d } };
*/
[object, uuid(FABA0150-7A61-4B90-BE31-367959D6017D)] interface ITest : IUnknown {
[helpstring("test function")] HRESULT fnTest();
};
/**
// {FCAB304F-1C2A-406C-B81B-514C2531C5D7}
static const GUID <> =
{ 0xfcab304f, 0x1c2a, 0x406c, { 0xb8, 0x1b, 0x51, 0x4c, 0x25, 0x31, 0xc5, 0xd7 } };
*/
[object, uuid(FCAB304F-1C2A-406C-B81B-514C2531C5D7)] interface IMyString : IUnknown {
[helpstring("拷贝入参到MyString")] HRESULT CopyTo(BSTR* bstrSrc);
[helpstring("得到MyString长度")] HRESULT GetLength(LONG* pLength);
[helpstring("根据GetLength, 分配足够长度的BSTR作为入参, 得到MyString")] HRESULT GetString(BSTR* bstrSrc);
};
//------------------------------------------------------------------------------
// 类型库(.tlb)定义
// 如果不定义, 是不能产生tlb文件的
//------------------------------------------------------------------------------
/**
// {10BE6B22-9030-4573-8C60-F7AD3BFA3A0B}
static const GUID <> =
{ 0x10be6b22, 0x9030, 0x4573, { 0x8c, 0x60, 0xf7, 0xad, 0x3b, 0xfa, 0x3a, 0xb } };
*/
/// 版本只能是x.y, x和y分别最大为65535
/// 这里的版本定义x = 年, y = 发布时,从年初到现在的build次数
/// 一个IDL中只能有一个库, 但是一个库中可以包含多个接口
[uuid(10BE6B22-9030-4573-8C60-F7AD3BFA3A0B), version(2015.1)] library LibTest {
//------------------------------------------------------------------------------
// 接口实现类CLSID定义
//------------------------------------------------------------------------------
/*
// {04CD56ED-6DE7-4859-8A60-3AAC1B9A2E1D}
static const GUID <> =
{ 0x4cd56ed, 0x6de7, 0x4859, { 0x8a, 0x60, 0x3a, 0xac, 0x1b, 0x9a, 0x2e, 0x1d } };
*/
[uuid(04CD56ED-6DE7-4859-8A60-3AAC1B9A2E1D), helpstring("接口类(测试类)实现")] coclass CTest {
interface ITest;
};
/*
// {955EA949-67CB-46CB-A376-9F299B394568}
static const GUID <> =
{ 0x955ea949, 0x67cb, 0x46cb, { 0xa3, 0x76, 0x9f, 0x29, 0x9b, 0x39, 0x45, 0x68 } };
*/
[uuid(955EA949-67CB-46CB-A376-9F299B394568), helpstring("接口类(字符串类)实现")] coclass CMyString {
interface IMyString;
};
};
在msdn的MIDL帮助中,对IDL关键字做了进一步的解释
在msdn的IDL帮助中,可以对IDL关键字和写法做进一步的试验