Я пытаюсь встроить элемент управления браузера в свое приложение (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 эскиз того, что я хочу, будет примерно таким:
(Нажмите для увеличения)
http://img827.imageshack.us/img827/3269/lsactivedesktopumlsmall.png
Как видно из uml, я хочу предоставить минимальные реализации всех интерфейсов, поэтому мне нужно только переопределить эти методы в интерфейсах, которые я действительно хочу сделать что-то существенное в TWebf.
Возможна ли моя "реализация множественного наследования"? Это хорошая идея? Это лучшее решение?
EDIT:
Для дальнейшего обсуждения, вот текущая реализация 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;
}
РЕДАКТИРОВАТЬ 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 правильно отметил, этот указатель должен быть приведен до того, как он назначен для * ppv, и требуется особая осторожность, когда запрашивается IUnknown. Прочитайте Почему именно мне нужно явное преобразование при реализации QueryInterface в объекте с множественным наследованием для превосходного объяснения этого.
Почему именно я получил эту конкретную ошибку времени выполнения, которую я до сих пор не знаю.