之前的笔记搬运
设计COM插件应该需要注意的规范
前四条是为了保证每个编译器编译出来的虚表是一样的
不能有虚析构 (别的编译器虚析构在虚表里可能会有两项)
不允许重载 (重载可能会打乱成员函数在虚表里的顺序)
不允许虚继承 (会改变虚表指针位置)
不允许多重继承 (会有多个虚表)
结构的成员函数必须为__stdcall, 不能是默认的thiscall.
(保证所有编译器传this指针都是通过栈来传递)不允许使用int型变量, 全部用long代替, 因为不同编译器对Int解释的字节大小不同,long都是统一的
语言不同导致字符串格式不同, 为了统一多语言字符串格式,
BSTR
微软设计了一个新字符串格式: BSTR
字符串大小 | 字符串内容 | /0 |
---|---|---|
BSTR使用的编码格式是UNICODE
可以操作BSTR字符串的类: _bstr_t, CString(MFC), CComBstr
零碎笔记
- 因为COM要兼容每种语言, 而使用插件的话就得用头文件, 所以微软定义了格式为
.TLB一个二进制头文件的概念, 编译器可以解析其中的二进制来获取函数名, 返回值,调用约定, 参数等等, 来生成一个对应自己的语言的头文件,而生成TLB文件用到了新的语言 IDL, 编译器为MIDL (VS自带)
- IDL中的retval关键字可以用来修饰返回值参数, 当使用时就可以不填写该函数,
而是当返回值使用, 不过该参数必须是最后一个, 并且在使用时,必须使用TLB,不能使用.H声明, 其原理就是内部会调用一边函数, 并检查返回值,如果成功就返回参数, 不成功就抛异常
OUT关键字只能修饰指针或引用类型
使用ATL框架编写的COM插件.TLB文件会内嵌在DLL里
IDispatch替换IUnkown, 可以支持脚本语言, 被称为自动化接口ActiveX 插件就是带界面的COM插件
“连接点”的作用是用于COM插件和主窗口通信, 主窗口可以响应编写出来的控件的消息,原理是利用预先留好虚表的位置, 让用户自己填写并实现函数
而IDL文件中的Myxxx类是用于DDX的, 在使用时会DDX会绑定Myxxx类,
可以使用里面的接口函数
- 在DLL导出C++写的函数时名称粉碎机制会影响动态调用时的使用, 而加上extern
C的关键时, C调用约定可以避免名称粉碎,而如果函数是stdcall的话,还是会有名称粉碎, 所以这时候需要用def进行导出函数,就可以避免名称粉碎;
- 在设计接口一般都是在虚基类里定义纯虚函数来规范接口, 而接口也会面临跟新,
这时候加新接口, 也就是加新的纯虚函数, 而位置必须是从最后面加,这样以前的接口函数在虚表的偏移也不会有变化,在新版本接口的环境也可以使用旧版本插件里的函数, 这就是向下兼容的一些技巧;
在比较两个同样类型结构体是否相同的时候, 而结构体就是多个变量组成一块内存,可以使用memcmp比较内存是否一样, 可以避免一个个比较结构体中的成员是否相同;
在一个模块中new出的内容, 一定要在该模块中delete, 因为每个模块中都有一个表,来存放和管理从自身模块中申请出堆内存的指针, 而当你在自身模块new出内容,在其他模块delete的时候, 就会因为其他模块的表中没有该内存的申请信息,而导致不能释放内存, 报错, 或其他的一些错误;
注册表中的class_root就是存放Com插件信息的地方;
com标准的一些注册, 卸载函数, 在导出时候需要用private关键字修饰的原因,
就是为了防止静态调用;