为什么虚拟继承和虚拟析构函数会导致COM工厂生成异常?
这将是一个有点复杂,但我已经工作了两天没有任何进展,所以我真的需要一些帮助 我正在尝试编写一个COM服务器,我已经成功地完成了几次。为了将所有COM对象都必须实现的接口的实现(特别是,为什么虚拟继承和虚拟析构函数会导致COM工厂生成异常?,com,virtual-inheritance,virtual-destructor,Com,Virtual Inheritance,Virtual Destructor,这将是一个有点复杂,但我已经工作了两天没有任何进展,所以我真的需要一些帮助 我正在尝试编写一个COM服务器,我已经成功地完成了几次。为了将所有COM对象都必须实现的接口的实现(特别是,IUnknown)移动到一个超类(我正在调用Base)中,我需要该超类从IUnknown虚拟继承。这是因为COM对象将从超类继承,也将从对象的公共接口继承,该接口还必须从IUnknown继承 再一次,我有一个“钻石”继承结构: IUnknown
IUnknown
)移动到一个超类(我正在调用Base
)中,我需要该超类从IUnknown
虚拟继承。这是因为COM对象将从超类继承,也将从对象的公共接口继承,该接口还必须从IUnknown
继承
再一次,我有一个“钻石”继承结构:
IUnknown
/ \
/ \
Base::virtual IUnknown IComObject::virtual IUnknown
\ /
\ /
CComObject::Base, IComObject
实现COM服务器的一部分是创建一个“工厂”对象,它本身就是一个COM对象,因此必须实现IUnknown
。遗憾的是,工厂对象还必须实现IClassFactory
,它继承自IUnknown
,但不是虚拟的。因此,工厂对象还必须实现IUnknown
,即使它继承了我的新超类Base
现在,IUnknown
的实现必须做的一件事是提供一个“自杀”方法,该方法将在适当的时候删除COM对象。为了成功地做到这一点,Base
的析构函数必须是虚拟的。因此,首先,Base
的声明如下所示:
#包括
类库:公共虚拟IUnknown
{
公众:
虚~Base(){};
};
这里没有声明任何IUnknown
方法,尽管它们将在将来某个时候,当我解决我现在正在处理的bug时。现在,将从基继承的唯一类是我的工厂类,名为CFactory5
,它必须(出于上述原因)实现IUnknown
本身
以下是CFactory5
的声明和完整实现(作为存根):
class CFactory5:公共基、公共IClassFactory
{
公众:
STDMETHODIMP QueryInterface(refid riid,void**ppv)重写{return E_FAIL;};
STDMETHODIMP(ULONG)AddRef(void)override{return E_FAIL;};
STDMETHODIMP(ULONG)Release(void)override{return E_FAIL;};
STDMETHODIMP CreateInstance(IUnknown*pUnk,refid-riid,void**ppv){return E_FAIL;};
STDMETHODIMP LockServer(BOOL fLock){返回E_FAIL;};
};
现在,所有COM服务器都必须按照约定实现并导出两个函数,DllGetClassObject
,它提供指向工厂对象的指针;以及DllCanUnloadNow
,它让COM知道是否可以卸载整个服务器库。在这里,与类id一起的是这两个函数,这两个函数被简化为最小值,至少可以返回指向CFactory5
对象的指针:
#包括
定义\u GUID(CLSID\u i计数器,
0x8b63c61、0x467a、0x46c7、0x9e、0xf5、0x99、0xe、0x8、0x54、0x5b、0xc2);
STDAPI DllGetClassObject(REFCLSID rclsid、refid riid、void**ppv)
{
if(rclsid!=CLSID\u i计数器)
返回类不可用;
*ppv=新的CFactory5;
返回S_OK;
}
STDAPI DllCanUnloadNow()
{
返回S_FALSE;
}
对于所有这些存根,这个工厂对象不会很好地工作,但是会导致的问题永远不会有机会显示出来,因为工厂对象的任何方法都不会被调用(根据调试器)。相反,在DllGetClassObject
返回之后,在调用调试器捕获的任何其他内容之前,我会得到以下错误:
Exception thrown at 0x00000000 in Client5.exe: 0xC0000005:
Access violation executing location 0x00000000.
以下是Client5.exe
的代码:
intmain()
{
HRESULT hrtop=CoInitialize(NULL);
如果(失败(hrtop))
{
printf(“协同初始化失败:0x%08x\n”,hrtop);
出口(hrtop);
}
void*pCF=NULL;
CoGetClassObject(CLSID\u ICounter、CLSCTX\u INPROC\u服务器、NULL、IID\u IClassFactory和pCF);
coninitialize();
返回0;
}
当调用CoGetClassObject
时,会导致调用DllGetClassObject
,我可以在调试器中捕捉到它。向前走,我可以看到一个新的CFactory5
对象被创建,指向它的指针存储在*ppv
中。在我“继续”通过DllGetClassObject
的末尾之前,不会出错。由于没有调用我的其他代码,我得到了上面描述的异常
现在,如果我从Base
的IUnknown
声明中删除virtual
关键字作为其基类,或者如果我从Base
的析构函数声明中删除virtual
关键字,则会调用CFactory5
的一个存根方法。在调用我的存根方法之前发生的任何事情,如果我做了这些事情中的任何一件,都会停止。但我需要这些东西都是虚拟的,原因如上所述
所以,在这一点上,我被难住了。我不能在不牺牲一个基本设计元素的情况下删除更多的代码,而且我似乎无法得到我运行的代码来显示我可以使用调试器检查的错误。我推测,关于虚拟继承和/或虚拟析构函数,我仍然有一些不明白的地方(尽管我对析构函数的重要性倍感困惑,因为它从未被调用过)
如果有人能向我解释,我将不胜感激。即使没有人能做到这一点,如果有人能提出更多的方法来尝试调试它,我将非常感激
我正在使用Visual Studio 2015社区(更新3)和Windows 10
有什么想法吗?COM对其接口的特定二进制布局非常挑剔。虚拟继承导致编译器生成与COM不兼容的二进制布局。在您的示例中,
icombject
不是COM接口。我建议你看看-