4

我正在尝试在我的应用程序 (IWebBrowser2) 中嵌入浏览器控件。我需要实现 IDispatch、IDocHostShowUI、IDocHostUIHandler 等来完成这项工作。我在纯 C++/Win32 api 中执行此操作。我没有使用 ATL、MFC 或任何其他框架。

我有一个名为 TWebf 的主类,它创建一个 Win32 窗口以将浏览器控件放入其中,并使所有需要的 OLE 调用使其工作。它还用于控制浏览器控件,使用 Refresh()、Back()、Forward() 等方法。

现在这是通过组合实现的。TWebf 具有将所有不同接口(IDispatch、IDocHostShowUI ...)实现为(堆栈分配的)成员的类。TWebf 在其构造函数中所做的第一件事是给所有这些成员一个指向自身的指针(dispatch.webf = this;等等)。QueryInterface、AddRef 和 Release 被实现为对 TWebf 中所有接口实现的这些方法的调用return webf->QueryInterface(riid, ppv);(例如通过调用)

我不喜欢 TWebf 和实现接口的类之间的这种循环依赖。TWebf 有一个 TDispatch 成员,该成员有一个 TWebf 成员,该成员有一个......

所以我正在考虑用多重继承来解决这个问题。这也将简化 QueryInterface 以始终能够返回this

我想要的 UMLish 草图是这样的:(点击查看大图)

从 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:

我尝试只为几个接口实现这个。让 TWebf 从 IUnknown 和 TOleClientSite 继承似乎工作正常,但是当我将 TDispatch 添加到继承列表时它停止工作。

除了warning C4584: 'TWebf' : base-class 'IUnknown' is already a base-class of 'TDispatch'警告之外,我还会遇到运行时错误。运行时错误是“访问冲突读取位置 0x00000000”

由于某种原因,运行时错误发生在处理 IOleClientSite 而不是 IDispatch 的行上。我不知道为什么会这样,或者它是否真的与多重继承有关。任何线索任何人?

编辑 3:

QueryInterface 的错误实现似乎是运行时异常的原因。正如Mark Ransom正确指出的,在将 this 指针分配给 *ppv 之前需要对其进行强制转换,并且在请求 IUnknown 时需要特别小心。阅读为什么在具有多重继承的对象中实现 QueryInterface 时我需要显式向上转换,以获得很好的解释。

为什么我仍然不知道那个特定的运行时错误。

4

3 回答 3

2

多重继承是实现 COM 接口的一种非常常见的方式,所以是的,这是可能的。

但是 QueryInterface 仍然必须为每个接口转换指针。多重继承的一个有趣特性是指针可以针对每种类类型进行调整——指向 IDispatch 的指针与指向 IDocHostUIHandler 的指针的值不同,即使它们都指向同一个对象。还要确保 IUnknown 的 QueryInterface 总是返回相同的指针;由于所有接口都派生自 IUnknown,如果您尝试直接转换为 IUnknown,您将得到一个模棱两可的转换,但这也意味着您可以将任何接口用作 IUnknown,只需选择父列表中的第一个。

于 2010-08-31T22:00:10.260 回答
0

多重继承有几个限制

  1. 如果两个接口要求实现具有相同名称/签名的函数,则不可能使用多重继承提供两种不同的行为。在某些情况下,您需要相同的实现,但在其他情况下,您不需要。

  2. 您的类的虚拟表上将有多个 IUnknown 接口,这可能会增加额外的内存使用量。他们确实共享相同的实现,这很好。

于 2010-08-31T22:06:11.813 回答
0

保持组合会更容易。MI有很多陷阱,比如虚拟继承,并且在可维护性方面受到很大影响。如果您必须将其作为数据成员传递给组合类,那么您做错了。如果他们需要访问其他提供的方法,您应该做的是在方法调用中传递这个。由于您控制对组合对象的所有方法调用,因此插入额外的指针应该没有问题。这使维护和其他操作的生活变得更加容易。

于 2010-08-31T22:32:40.903 回答