C ++ COM设计。组合与多重继承 [英] C++ COM design. Composition vs multiple inheritance

查看:165
本文介绍了C ++ COM设计。组合与多重继承的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图在我的应用程序(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); / p>

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



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



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





从uml可以看出,我想提供所有接口的最小实现,所以我只需要重写那些方法在接口我实际上想做一些重要的TWebf。



我的多继承实现可能吗?这是个好主意吗?这是最好的解决方案吗?



编辑



当前实现的QueryInterface在TWebf

  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;
}

EDIT 2:
$ b

我试图实现这只是一些几个接口。有TWebf继承自IUnknown和TOleClientSite似乎工作正常,但是当我添加TDispatch到继承列表它停止工作。



除了警告C4584:'TWebf':base-class'IUnknown'已经是'TDispatch'的基类警告我也得到运行时错误。运行时错误是访问冲突读取位置0x00000000



运行时错误发生在处理IOleClientSite的行上,而不是IDispatch由于某种原因。我不知道为什么这是发生,或者如果它真的与多重继承或没有。



QueryInterface的一个坏的实现似乎没有任何线索?一直是运行时异常的原因。由于 Mark Ransom 正确注意到此指针需要在分配给* ppv之前进行强制转换,因此需要特别小心当请求IUnknown时。阅读为什么我需要在一个具有多重继承的对象中实现QueryInterface的显式上传。对此的一个很好的解释。



为什么我得到那个特定的运行时错误知道。

解决方案

多继承是一个很常见的COM接口,所以是可能的。



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


I'm trying to embed a browser control in my application (IWebBrowser2). I need to implement IDispatch, IDocHostShowUI, IDocHostUIHandler etc to make this work. I am doing this in pure C++/Win32 api. I'm not using ATL, MFC or any other framework.

I have a main class, called TWebf, that creates a Win32 window to put the browser control in and makes all the OLE calls needed to make it work. It's also used for controlling the browser control, with methods like Refresh(), Back(), Forward() etc.

Right now this is implemented with composition. TWebf has classes implementing all the different interfaces (IDispatch, IDocHostShowUI...) as (stack allocated) members. First thing TWebf does in its constructor is give all those members a pointer back to itself (dispatch.webf = this; etc). QueryInterface, AddRef and Release are implemented as calls to those methods in TWebf for all interface implementations (by calling return webf->QueryInterface(riid, ppv); for example)

I don't like this circular dependency between TWebf and the classes implementing the interfaces. TWebf has a TDispatch member that has a TWebf member that has a...

So I was thinking about solving this with multiple inheritance instead. That would also simplify QueryInterface to always be able to just return this.

A UMLish sketch of what I want would be something like this: (Click for a bigger view)

As can be seen in the uml I want to provide bare-minimum implementations of all the interfaces so I only have to override those methods in the interfaces I actually want to do something substantial in TWebf.

Is my "multiple inheritance implementation" possible? Is it a good idea? Is it the best solution?

EDIT:

For future discussion, here's the current implementation of QueryInterface in TWebf

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;
}

EDIT 2:

I tried implementing this for just a couple of the interfaces. Having TWebf inherit from IUnknown and TOleClientSite seems to work fine, but when I added TDispatch to the inheritance list it stopped working.

Apart from the warning C4584: 'TWebf' : base-class 'IUnknown' is already a base-class of 'TDispatch' warning I also get runtime errors. The runtime error is "Access violation reading location 0x00000000"

The runtime error happens on a line dealing with IOleClientSite, not IDispatch for some reason. I don't know why this is happening, or if it really has to do with the multiple inheritance or not. Any clues anyone?

EDIT 3:

A bad implementation of QueryInterface seems to have been the reason for the runtime exception. As Mark Ransom correctly noted the this pointer needs to be casted before it's assigned to *ppv, and special care is needed when IUnknown is requested. Read Why exactly do I need an explicit upcast when implementing QueryInterface in an object with multiple inheritance for an excellent explanation of that.

Why exactly I got that specific runtime error I still do not know.

解决方案

Multiple inheritance is a very common way to do COM interfaces, so yes it's possible.

However QueryInterface must still cast the pointer for each interface. One interesting property of multiple inheritance is that the pointer may get adjusted for each class type - a pointer to IDispatch won't have the same value as a pointer to IDocHostUIHandler, even though they both point to the same object. Also make sure that a QueryInterface for IUnknown always returns the same pointer; since all the interfaces derive from IUnknown you'll get an ambiguous cast if you try just to cast directly to it, but that also means you can use any interface as an IUnknown, just pick the first one in the parent list.

这篇关于C ++ COM设计。组合与多重继承的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆