这有点复杂,但我已经工作了两天没有进展,所以我可以真的使用一些SO帮助。
我正在尝试编写一个COM服务器,这是我多次成功完成的事情。为了将所有COM对象必须实现的接口(具体地,IUnknown
)的实现移动到超类(我称之为Base
)中,我需要该超类从{{1 }}。这是因为COM对象将从超类继承,也从对象的公共接口继承,该对象也必须从IUnknown
继承。
再次,我有一个“钻石”继承结构:
IUnknown
实现COM服务器的一部分是创建一个“工厂”对象,它本身就是一个COM对象,因此必须实现 IUnknown
/ \
/ \
Base::virtual IUnknown IComObject::virtual IUnknown
\ /
\ /
CComObject::Base, IComObject
。唉,工厂对象还必须实现IUnknown
,它继承自IClassFactory
,但不是虚拟的。因此,工厂对象也必须实现IUnknown
,即使它继承自我的新超类IUnknown
。
现在,Base
实现必须做的事情之一是提供一种“自杀”方法,在适当的时候,它将删除COM对象。为了成功实现这一点,IUnknown
的析构函数必须是虚拟的。所以,首先,Base
的声明如下:
Base
这里没有声明#include <windows.h>
class Base : public virtual IUnknown
{
public:
virtual ~Base() {};
};
个方法,虽然它们将来某个时候,当我解决我现在正在处理的错误时。现在,唯一将从Base继承的类是我的工厂类,名为IUnknown
,必须(由于上述原因)实现CFactory5
本身。
以下是IUnknown
的声明和完整实现(如存根):
CFactory5
现在,所有COM服务器必须通过契约实现并导出两个函数class CFactory5 : public Base, public IClassFactory
{
public:
STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override { return E_FAIL; };
STDMETHODIMP_(ULONG) AddRef(void) override { return E_FAIL; };
STDMETHODIMP_(ULONG) Release(void) override { return E_FAIL; };
STDMETHODIMP CreateInstance(IUnknown *pUnk, REFIID riid, void** ppv) { return E_FAIL; };
STDMETHODIMP LockServer(BOOL fLock) { return E_FAIL; };
};
,它提供指向工厂对象的指针,DllGetClassObject
,它让COM知道整个服务器库可以卸载。这里,与类id一起,是那两个函数,简化为最低限度,至少可以返回指向DllCanUnloadNow
对象的指针:
CFactory5
对于所有这些存根,这个工厂对象不能很好地工作,但是导致永远不会有机会展示自己的问题,因为工厂对象的方法的 none 得到了调用(根据调试器)。相反,在#include <initguid.h>
DEFINE_GUID(CLSID_ICounter,
0x8b63c61, 0x467a, 0x46c7, 0x9e, 0xf5, 0x99, 0xe, 0x8, 0x54, 0x5b, 0xc2);
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void** ppv)
{
if (rclsid != CLSID_ICounter)
return CLASS_E_CLASSNOTAVAILABLE;
*ppv = new CFactory5;
return S_OK;
}
STDAPI DllCanUnloadNow()
{
return S_FALSE;
}
返回之后,在调用调试器的其他任何事情之前,我会收到此错误:
DllGetClassObject
以下是Exception thrown at 0x00000000 in Client5.exe: 0xC0000005:
Access violation executing location 0x00000000.
的代码:
Client5.exe
调用int main()
{
HRESULT hrtop = CoInitialize(NULL);
if (FAILED(hrtop))
{
printf("CoInitialize failed: 0x%08x\n", hrtop);
exit(hrtop);
}
void* pCF = NULL;
CoGetClassObject(CLSID_ICounter, CLSCTX_INPROC_SERVER, NULL, IID_IClassFactory, &pCF);
CoUninitialize();
return 0;
}
时,会调用CoGetClassObject
,我可以在调试器中捕获。单步执行,我可以看到创建了一个新的DllGetClassObject
对象,并且指向它的指针存储在CFactory5
中。在我“继续”超过*ppv
结束之前,没有任何问题。由于没有调用我的其他代码,我得到了上述异常。
现在,如果我从DllGetClassObject
的{{1}声明中删除CFactory5
关键字,我的virtual
的一个存根方法会被调用如果我从Base
的析构函数的声明中删除IUnknown
关键字,则将其作为基类或。无论在调用我的存根方法之前发生了什么,如果我做了其中任何一件事,就会停止爆炸。但是我需要这些东西都是虚拟的,原因如上所述。
所以,在这一点上,我很难过。我不能在不牺牲基本设计元素的情况下删除任何更多的代码,而且我似乎无法得到我运行的内容以向我显示我可以使用调试器检查的错误。我认为有些东西我仍然不了解虚拟继承和/或虚拟析构函数(尽管我对于为什么析构函数重要,因为它永远不会被调用而感到困惑)。
如果有人能向我解释这一点,我会非常感激。即使没有人能做到这一点,如果有人可以建议更多的方法来尝试调试它,我将非常感激。
我正在使用Visual Studio 2015社区(更新3)和Windows 10。
有什么想法吗?