用C/C++实现COM,首先要解决几个问题:
1. C/C++代码在编译的时候,编译器会将函数包括导出函数进行重命名,而且不同编译器处理的方式还不一样,所以就需要用一些方法告诉编译器应该怎么处理导出函数以规范导出函数的命名。可选方法有:
- 用extern “C”, 告诉编译器按照C语言的方式对导出函数做重命名-实际上就是保持原来的名字,不做任何重命名;
- 用DEF 文件和IDL 定义接口。这种方式导出来的函数名字就是def里定义的名字
2. 接口与实现分离,在接口类里面不出现任何变量。而且,只包含一个指针:虚表指针。这就要求所有的函数都是虚函数。这些接口类应该被定义成纯抽象类, 所有的函数都是纯虚函数。如果不定义成纯虚函数,那边编译的时候编译器会尝试去找到这些函数的实现方式而实际上找不到,会提示连接错误。
3. 析构函数的处理。一般情况下,析构函数应该是虚函数,但是因为不同的编译器和平台对析构函数的处理是不一样的:虚析构函数的函数指针可能出现在virtual table 的任何位置, 这会导致COM接口的兼容性问题。所以,COM 接口类不把析构函数定义成虚函数。那么,如何保证在调用delete pInterface的时候能调用到正确的析构函数正确的析构函数而不会造成内存泄漏呢? 可以在COM 接口里提供一个删除对象的接口,让COM对象的使用者显示的调用这个接口以争取的释放内存。
4. C++语言规范里面规定了RTTI, 但是对于RTTI的实现,各个编译器厂商实现方法都是不一样的。为了使得接口独立于编译器(与编译器的具体实现无关), 接口类应该暴露一个函数用来模拟RTTI,也即应该实现一个dynamic_cast函数,返回正确的对象类型指针。或者这样理解,COM的本意是想在二进制的层次,也就是内存布局的层面,定义好一个接口, 让客户使用。客户不应该去修改这个接口的布局或者扩充这个接口。如果需要扩充,也应该是通过这个接口里面已经定义好了的接口(函数)去调用扩充的功能;而不应该对接口做任何的类型转换。