- 因为在注册插件的时候,是要传一个名为
IDispatch*
这样子的一个参数,所以我们可以看到插件在注册的时候默认就是要求这是一个实现了自动化的接口(IDispatch,如下图所示,是后面将要展示的代码,先拎出来讲一下),可以看到参数(IDispatch * EventSink)这实际上就是注册插件时需要提供的自动化的接口IDispatch
的地址指针,所以我们在继承了插件结构体时还要实现自动化接口(IDispatch).
- 而自动化接口
IDispatch
接口不过就是未知接口IUnknown
接口的扩展 - 首先大略说一下关于未知接口IUnknown,因为主程序CDR在运行过程中突然要加载我们写的插件程序,然而CDR主程序并不知道我们这些后来人写的是些啥程序,所以他根本不知道调用我们会运行出现什么,所以这个就是这个接口名字含义的由来,他只是用来呼唤我们用的,至于呼唤出来的是个什么东西它完全不知道,所以叫作未知接口,英文IUnknown更直接:“我不知道”…
- 拆分
IUnknown
里面就是三个函数,分别是QueryInterface
,AddRef
和Release
,第一个函数QueryInterface是查询接口,也是IUnknown的第一个函数,这是固定的,任何其它的IUnknown接口,第一个函数都是QueryInterface,它是用来确定查询接口用的,第二个AddRef增加和第三个Release减少分别是计数用的,用以确认这个接口的寿命,是实现自动化的一部分. - 而
IDispatch
就是在IUnknown的基础上再增加四个函数,分别是GetTypeInfoCount
,GetTypeInfo
,GetIDsOfNames
和Invoke
,这四个个函数就排在IUnknown三个函数后面形成一个自动化接口,有了这四个函数这个接口就不再是未知接口了,而是能够获取当前的函数个数,函数名称,ID和具体实现了一些什么功能等等.
- 接下来我们就开始创建结构体
struct CongLingKaiShi :public VGCore::IVGAppPlugin {
public:virtual HRESULT __stdcall raw_OnLoad(VGCore::IVGApplication* Application);virtual HRESULT __stdcall raw_StartSession();virtual HRESULT __stdcall raw_StopSession();virtual HRESULT __stdcall raw_OnUnload();
public:virtual HRESULT __stdcall QueryInterface(REFIID riid, void** ppvObject);virtual ULONG __stdcall AddRef(void) {return 1;}virtual ULONG __stdcall Release(void) {return 1;}
public:virtual HRESULT __stdcall GetTypeInfoCount(UINT* pctinfo) { return E_NOTIMPL; }virtual HRESULT __stdcall GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo) { return E_NOTIMPL; }virtual HRESULT __stdcall GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId) { return E_NOTIMPL; }virtual HRESULT __stdcall Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr);
};
- 首先我们看到这是一个结构体
struct
也就是结构体对象生成之前的模板 - 结构体的名字叫作
CongLingKaiShi
- 后面跟上的冒号
:public VGCore::IVGAppPlugin
就是说CongLingKaiShi这个结构体要继承冒号后面的VGCore::IVGAppPlugin
结构体, public
是描述继承形式为公有继承,也就是爷爷的财产(除私人物品)儿子和孙辈可以使用或分配给任何人- 顺带再说
private
私有继承,也就是爷爷的财产(除私人物品)儿子可以使用或分配给任何人,但孙子辈看不到 - 另一个
protected
保护继承,也就是爷爷的财产(除私人物品)只有子子孙孙及其挚友能自己使用,无权给其它外人 - 所以没有这些要求的话一般都是公有继承
public
,然后可以看到结构体里面还有四个public:
这些都定义为公有成员 - 然后我们可以看到一共有11个
virtual
开头的虚函数,这11个函数怎么来的呢,我们可以打开vgcoreauto.tlh
文件,查到定位到IVGAppPlugin
这个函数体,如下图所示
- 可以看到下方有四个
=0
为结尾的纯虚函数,然后可以看到IVGAppPlugin
后面紧跟着一个: IDispatch
,说明IVGAppPlugin还继承了IDispatch
这个结构体,而在IDispatch
中就包含了GetTypeInfoCount
和GetTypeInfo
和GetIDsOfNames
和Invoke
四这个纯虚函数,如下图
- 然后我们看到
IDispatch : public IUnknown
IDispatch 这个自动化接口结构体又继承了IUnknown
未知接口结构体中的三个纯虚函数QueryInterface
和AddRef
和Release
,如下图
- 以上就是11个纯虚函数的由来,所以当我们的结构体
CongLingKaiShi
继承了一个VGCore::IVGAppPlugin的时候,实际上我们就一次性继承了11个纯虚函数,而纯虚函数是必须被实例化的,因为纯虚函数在基类是没有实体的(基类纯虚函数在基类中免写实体并方便基类指针多态访问派生类要写实体的虚函数,这句话有点拗口,反正就是要把这些函数全都实现一遍就好了) virtual
是表示这个函数是虚函数,它一定是派生出来的结构体或者类当中的同名函数,是另外在程序的虚函数表中进行维护的HRESULT
是函数返回值,是一个无符号的长整数,跟ULONG类似的,它一般就是大于等于0的长整数long__stdcall
是标准呼叫,里面规定了出栈和入栈的顺序等等raw_OnLoad(VGCore::IVGApplication* Application)
第是第一个函数,也是最重要的一个函数,名称为raw_OnLoad,就是在CDR主程序加载成功的时候,主程序会启动这个函数,然后把最重要的VGCore::IVGApplication* Application
这个主程序的根结构体Application
传过来.然后我们就可以通过这个Application操作当前的CDR主程序了,因为所有的东西都是封装在这个Application里面.(Application在三十二章里面详细讲过了)- 然后
raw_StartSession
也差不多类似是内部启动完毕之后会加载的函数,启动时间在raw_OnLoad
之后 raw_StopSession
和raw_OnUnload
就是在关闭CDR的时候执行的函数了,可以把一些卸载类的功能放在这里面- 后面就是IDispatch的七个虚函数了,
AddRef
和Release
是生存周期用的,我们的插件程序生存周期和主程序同步,所以不需要管他,直接返回1 - 然后
GetTypeInfoCount
和GetTypeInfo
以及GetIDsOfNames
我们就直接返回E_NOTIMPL
没有实现该接口,因为这些都是给动态语言后绑定用的,我们并不需要 - 除了上面5个用不上用的,后面我们主要就是要实现
Invoke
等其余6个函数,一下节我们来实现它们的函数实体