我正在尝试在我的应用程序中嵌入浏览器控件(IWebBrowser2).我需要实现IDispatch,IDocHostShowUI,IDocHostUIHandler等来使这项工作.我在纯C / Win32 api中这样做.我没有使用ATL,MFC或任何其他框架.
我有一个名为TWebf的主类,它创建一个Win32窗口来将浏览器控件放入其中并进行所需的所有OLE调用以使其工作.它还用于控制浏览器控件,使用Refresh(),Back(),Forward()等方法.
现在,这是用组合实现的. TWebf具有实现所有不同接口(IDispatch,IDocHostShowUI …)作为(堆栈分配)成员的类. TWebf在其构造函数中做的第一件事是给所有成员一个指针回自己(dispatch.webf = this; etc). QueryInterface,AddRef和Release实现为对所有接口实现的TWebf中的那些方法的调用(通过调用return webf-> QueryInterface(riid,ppv);例如)
我不喜欢TWebf和实现接口的类之间的这种循环依赖. TWebf有一个TDispatch成员,其TWebf成员有……
所以我正在考虑用多重继承来解决这个问题.这也会简化QueryInterface,以便始终能够返回它.
我想要的UMLish草图将是这样的:
(点击查看大图)
http://img827.imageshack.us/img827/3269/lsactivedesktopumlsmall.png
从uml中可以看出,我想提供所有接口的最小实现,所以我只需要在接口中覆盖那些我实际想要在TWebf中做大量工作的方法.
我的“多重继承实施”可能吗?这是个好主意吗?这是最好的解决方案吗?
编辑:
对于将来的讨论,这是TWebf中QueryInterface的当前实现
HRESULT STDMETHODCALLTYPE TWebf::QueryInterface(REFIID riid, void **ppv)
{
*ppv = NULL;
if (riid == IID_IUnknown) {
*ppv = this;
} else if (riid == IID_IOleClientSite) {
*ppv = &clientsite;
} else if (riid == IID_IOleWindow || riid == IID_IOleInPlaceSite) {
*ppv = &site;
} else if (riid == IID_IOleInPlaceUIWindow || riid == IID_IOleInPlaceFrame) {
*ppv = &frame;
} else if (riid == IID_IDispatch) {
*ppv = &dispatch;
} else if (riid == IID_IDocHostUIHandler) {
*ppv = &uihandler;
}
if (*ppv != NULL) {
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
编辑2:
我尝试为几个接口实现这个.从IUnknown和TOleClientSite继承TWebf似乎工作正常,但当我将TDispatch添加到继承列表时它停止工作.
除了警告C4584:’TWebf’:基类’IUnknown’已经是’TDispatch’警告的基类,我也会遇到运行时错误.运行时错误是“访问冲突读取位置0x00000000”
由于某种原因,运行时错误发生在处理IOleClientSite的行上,而不是IDispatch.我不知道为什么会发生这种情况,或者它是否真的与多重继承有关.有人提出任何线索吗?
编辑3:
QueryInterface的错误实现似乎是运行时异常的原因.正如Mark Ransom正确指出的那样,该指针需要在分配给* ppv之前进行转换,并且在请求IUnknown时需要特别小心.阅读Why exactly do I need an explicit upcast when implementing QueryInterface in an object with multiple inheritance以获得对此的出色解释.
为什么我确实得到了我仍然不知道的特定运行时错误.
最佳答案 多继承是一种非常常见的COM接口方式,所以是的,这是可能的.
但是,QueryInterface仍然必须为每个接口强制转换指针.多重继承的一个有趣属性是指针可以针对每个类类型进行调整 – 指向IDispatch的指针与指向IDocHostUIHandler的指针的值不同,即使它们都指向同一个对象.还要确保IUnknown的QueryInterface始终返回相同的指针;因为所有接口都来自IUnknown,如果你只是想直接转换它,你会得到一个模糊的演员,但这也意味着你可以使用任何接口作为IUnknown,只需选择父列表中的第一个.