什么是COM?
COM全称为ComponentObjectModel,是微软为了计算机工业的软件生产更加符合人类的行为方式开发的一种新的软件开发技术。在COM构架下,人们可以开发出各种各样的功能专一的组件,然后将它们按照需要组合起来,构成复杂的应用系统。由此带来的好处是多方面的:
可以将系统中的组件用新的替换掉,以便随时进行系统的升级和定制可以在多个应用系统中重复利用同一个组件可以方便的将应用系统扩展到网络环境下COM与语言,平台无关的特性使所有的程序员均可充分发挥自己的才智与专长编写组件模块等等
通达信 接口 分析,接口的结构与描述
COM把通达信 接口 分析,接口与实现分离开的动机主要有两个:
升级。把对象内部的工作细节对客户隐藏起来,使得实现类内部的数据成员的数量、类型以及内部的方法都可以发生变化,而客户程序无需重新编译。客户在运行时询问对象,以便发现对象的扩展功能编译器独立性。
COM规范规定:通达信 接口 分析,接口是包含了一组函数的数据结构,通过这组数据结构,客户可以调用组件对象的功能。通达信 接口 分析,接口的结构如下所示:
使用C++/C/C#描述通达信 接口 分析,接口
假设我们要做一个简单的计算器,提供以下函数:
使用C++的类来描述通达信 接口 分析,接口的结构
class ICalculator
{
public:
virtual int __stdcall add(int num1, int num2) = 0;
virtual int __stdcall sub(int subtrahend, int minuend) = 0;
};
类ICalculator的内存结构与COM通达信 接口 分析,接口规范要求的完全一致。见下:
使用C语言描述通达信 接口 分析,接口的例子
struct ICalculator { //通达信 接口 分析,接口包含一个指针,指针指向一个虚表结构
struct ICalculatorVtbl *pVtbl;
};
struct ICalculatorVtbl { //虚表由函数指针构成
int (STDMETHODCALLTYPE *add)(ICalculator* this, int num1, int num2);
int (STDMETHODCALLTYPE *sub)(ICalculator* this, int subtrahend, int minuend);
};
实际上其他的语言,只要能描述虚表和虚表指针的结构就能够描述COM通达信 接口 分析,接口。
使用C#语言对通达信 接口 分析,接口的描述
[ComVisible(true)]
[Guid('1DE6EE9F-DBEE-411F-BFEF-532A86E84645')]
public interface ICalculator
{
int add(int num1, int num2);
int sub(int subtrahend, int minuend);
}
COM最终的目标是建立二进制级的组件模型。COM规范只定义了通达信 接口 分析,接口的特征,它没有规定编译器,也没有约束语言的使用。我们不仅需要需要编译器独立性,还需要语言的独立性。可以把C++定义的通达信 接口 分析,接口翻译到其他的编程语言中。因为COM通达信 接口 分析,接口的二进制本质上就是一组vptr/vtbl虚表指针和虚表,很多语言都可以做到。
但如果为所有已知的语言对所有的通达信 接口 分析,接口都产生映射版本,那么
工作量巨大由于C++语言非常复杂,且很容易产生歧义,它所描述的通达信 接口 分析,接口不一定能映射到其他语言上。
因此COM提供了这样一种语言,它只用到基本的C语法,同时加入了一些能消除歧义的特征,用来描述通达信 接口 分析,接口。称为通达信 接口 分析,接口定义语言IDL。它的意义在于以语言中性的方式准确地描述通达信 接口 分析,接口的类型,并且在IDL与其他语言之间建立映射,从而作为客户端与服务器端的通达信 接口 分析,接口描述标准,使得各方在遵循IDL标准的基础上自由地选择编程语言。
import “unknwn.idl”; // 类似于 include,引入其他的 idl 文档
[ object, // 表明该通达信 接口 分析,接口是一个 COM 通达信 接口 分析,接口而不是一个 RPC 通达信 接口 分析,接口
uuid(54BF6568-1007-11D1-B0AA-444553540000), // 全球唯一标志符
] // [ ] 表示属性
interface ICalculator : IUnknown // interface 关键字表明通达信 接口 分析,接口定义的开始
{
HRESULT add( [in] long num1, [in] long num2, [out, retval] long result );
HRESULT sub( [in] long subtrahend, [in] long minuend, [out, retval] long result );
};
通达信 接口 分析,接口的标识
逻辑名称与实质名称两个开发人员可能选择同一个通达信 接口 分析,接口名字ICalculator,然而有不同的成员函数,除了名字相同以外,两个通达信 接口 分析,接口是不兼容的。两个COM组件和各自的客户程序有可能在同一台机器上。冲突的可能性时刻存在。为了消除名字冲突,所有的通达信 接口 分析,接口在设计时分配一个二进制的名字,也就是实质名字,它使用GUID来标识通达信 接口 分析,接口。GUID是一个128位长的数,能在概率意义上保证不重复。
当GUID用来命名通达信 接口 分析,接口时,它被称为通达信 接口 分析,接口ID(InterfaceIdentifier,II,COM的实现也使用GUID来标识,这时它被称为类ID(ClassIdentifier,CLSI,用文本来表示,往往是以下的格式:
{CEBB3FBA-17F5-44c4-987C-631FAE5B80AC} // 32 个 16 进制的数字
因为很少编译器支持128位整数,COM定义了一个结构来表示GUID的128位值:
typedef struct _GUID {
DWORD Data1;
WORD Data2;
WORD Data3;
BYTE Data4[8];
} GUID;
typedef GUID IID; typedef GUID CLSID; // 为通达信 接口 分析,接口和实现类 ID 提供了别称。
COM还提供了等价性的测试函数IsEqualGUID并为GUID引用类型重载了==和!=操作符。
COM数据类型
为了支持语言独立性和平台独立性,COMIDL提供了一组内置的数据类型,从这些数据类型到C、C++、Java、VB等语言之间可以建立一个映射。
COM使用双字节字符,用OLECHAR来表示。为了编写在VB或C#中使用的COM对象,应使用BSTR格式的字符串。BSTR是带有长度前缀的UniCode编码字符串。COM提供了一组函数用于BSTR的操作,如SysAllocString、SysFreeString等。COM与IDL支持联合类型。COM定义了一个通用的联合的类型VARIANT,并且提供了一组函数操作VARIANT,如VariantInit、VariantClear、VariantCopy和VariantChangeType等。IDL支持指针类型,并使用C指针语法。函数的每一个参数必须指明是输入、输出或输入输出,以便编译器在生成的代码中进行列集和散集处理。几乎每个COM方法的返回值都是HRESULT,它是一个32位的整数,可以向调用者的运行环境提供关于发生了什么类型的错误的信息,比如网络错误、服务器失败等等。HRESULT分为3部分:严重程度位、操作码、信息码。MAKE_HRESULT宏用来生成自定义的HRESULT值,SUCCEEDED宏用来判断HRESULT值是否成功,FAILED宏用来判断值是否失败。
MIDL编译器
MIDexe是Win32SDK提供的工具,实现从IDL到C/C++的映射。它能编译idl文档以产生以下代码:
calculator.h 通达信 接口 分析,接口说明的头文件calculator_p.c 实现了通达信 接口 分析,接口的代理和存根calculator_c 定义了IDL中的GUID、IIDdlldatc 代理存根的入口函数以及其他数据结构calculator.tlb 类型库文件,可以供C#、Java等编译器使用
IUnknown通达信 接口 分析,接口
IUnknown是一个通达信 接口 分析,接口,所有COM通达信 接口 分析,接口都继承于IUnknown。
// IUnknown 的定义
interface IUnknown
{
virtual HRESULT __stdcall QueryInterface( const IID& iid, void **ppv ) = 0;
virtual ULONG __stdcall AddRef() = 0;
virtual ULONG __stdcall Release() = 0;
}
COM定义的每一个通达信 接口 分析,接口都必须从IUnknown继承过来,其原因在于IUnknown通达信 接口 分析,接口提供了两个非常重要的特性:生存周期控制和通达信 接口 分析,接口查询。客户程序只能通过通达信 接口 分析,接口与COM对象进行通信,虽然客户程序可以不管对象内部的实现细节,但它要控制对象的存在与否。
通达信 接口 分析,接口继承
通达信 接口 分析,接口的继承使得子类通达信 接口 分析,接口具有基类通达信 接口 分析,接口的功能。特别地,所有的通达信 接口 分析,接口都是从IUnknown通达信 接口 分析,接口派生而来,它们都具有通达信 接口 分析,接口的转换和引用计数的功能。通达信 接口 分析,接口本身不能多重继承。这并不妨碍一个对象继承多个通达信 接口 分析,接口,如:
可以使用一个来表示COM对象和它所支持的通达信 接口 分析,接口:
通达信 接口 分析,接口查询规则
按照COM规范,COM对象可以支持多通达信 接口 分析,接口,而且对通达信 接口 分析,接口的获取是动态的,因此非常便于COM对象的升级和更新,这是体现COM生命力的地方。客户程序在运行时对COM对象的通达信 接口 分析,接口进行询问,如果它实现了该通达信 接口 分析,接口,则客户可以调用它的服务。在COM规范中,这是通过IUnknown的成员函数QueryInterface实现的。
QueryInterface使用通达信 接口 分析,接口的GUID而不是字符串逻辑名称来区分通达信 接口 分析,接口。当客户创建了COM对象之后,创建函数会给客户返回一个通达信 接口 分析,接口指针,由于所有的通达信 接口 分析,接口都派生自IUnknown,它们都有QueryInterface成员函数,客户可以使用它来查询对象支持的其他通达信 接口 分析,接口。查询时,客户指定通达信 接口 分析,接口的IID号,查询函数把查询结果保存在通达信 接口 分析,接口指针ppv中。
QueryInterface的实现要求:
从同一对象的不同通达信 接口 分析,接口出发查询到的IUnknown通达信 接口 分析,接口完全相同也即得到相同的IUnknown子对象。遵循这一规则能确定两个通达信 接口 分析,接口是否指向同一组件。但从不同的路径查询到的其他通达信 接口 分析,接口不必完全相同。这可以允许有的通达信 接口 分析,接口是动态地生成的,比如tear-off通达信 接口 分析,接口等。对称性:A→B成功,则B→A成功意味着客户不必关心先获得哪个通达信 接口 分析,接口指针。两种不同类型的通达信 接口 分析,接口指针可以以任意的次序获得。传递性:A→B,B→C,则A→C意味着客户不必以任何特定的顺序来获得某个通达信 接口 分析,接口。如果任何两个通达信 接口 分析,接口之间不能直接转换,那么也不能通过第三方来完成。或者说,我们总是能够简单地从一个通达信 接口 分析,接口出发一步到位地到达其他的通达信 接口 分析,接口。自反性:A→A总是成功的这实际是传递性的一个特例,对应于起点和终点相同的情形。以上几条意味者所有的通达信 接口 分析,接口处于平等的地位。一个对象的所有通达信 接口 分析,接口构成一个双向连接:
文章为作者独立观点,不代表 股票程序化软件自动交易接口观点