В настоящей записке рассматривается как добавить поддержку сдвоенных интерфейсов в приложение сервера на основе MFC OLE-автоматизации. Образец ACDUAL иллюстрирует поддержку сдвоенных интерфейсов, и пример кода в настоящей записке берется из ACDUAL. Макросы, описанные в настоящей записке, такие как DECLARE_DUAL_ERRORINFO , DUAL_ERRORINFO_PART , и IMPLEMENT_DUAL_ERRORINFO , являются частью в образце ACDual ПОКАЗАНЫ и можно найти в MFCDUAL.H.
Сдвоенные интерфейсы
Хотя OLE-автоматизации позволяет реализовать интерфейс IDispatch , интерфейс VTBL или сдвоенный интерфейс (который охватывает оба), корпорация Майкрософт настоятельно рекомендует использовать сдвоенные интерфейсы для всех доступных объектов OLE-автоматизации. Сдвоенные интерфейсы имеют существенные преимущества перед IDispatch-только VTBL только или интерфейсы:
Добавление поддержки двойного интерфейса в класс на базе CCmdTarget
Сдвоенный интерфейс является производным от IDispatchдействительно только пользовательский интерфейс. Наиболее простой способ реализации поддержку сдвоенных интерфейсов в CCmdTarget-на основе класса является первая реализация нормальной диспетчеризации интерфейса на вашем классе с использованием MFC и ClassWizard, а затем добавьте пользовательский интерфейс позднее. В основном реализация пользовательского интерфейса будет просто делегируют обратно в реализации MFC IDispatch.
Во-первых измените файл ODL для вашего сервера для определения сдвоенные интерфейсы для ваших объектов. Для определения сдвоенный интерфейс, вы должны использовать оператор interface вместо DISPINTERFACE заявление, что мастера Visual C++ создают. Вместо того, чтобы удаление существующих DISPINTERFACE заявление, добавьте оператор нового интерфейса. Сохраняя DISPINTERFACE формы, вы можете продолжать использовать ClassWizard для добавления свойств и методов для вашего объекта, но необходимо добавить эквивалентные свойства и методы интерфейса заявление.
Оператор interface для сдвоенный интерфейс должен иметь двойной атрибуты и OLEAUTOMATION , и интерфейс должен быть производным от IDispatch. Вы можете использовать образец GUIDGEN для создания IID для сдвоенного интерфейса
[uuid(0BDD0E81-0DD7-11cf-BBA8-444553540000), / / IID_IDualAClick
автоматизированный OLE,
двойной
]
интерфейс IDualAClick: IDispatch
{
}
Как только у вас есть оператор interface, начните добавлять записи для методов и свойств. Для сдвоенных интерфейсов, вам необходимо изменить списки параметров так, чтобы ваши методы и функции метода доступа к свойству в два интерфейса возвращают HRESULT и передавать их возвращаемые значения в качестве параметров с атрибутами [retval,out] . Помните, что для свойств, необходимо будет добавить как чтение ( propget ) и записи ( propput ) получить доступ к функции с таким же идентификатором. Например:
[propput, ID(1) принадлежности к группам] HRESULT текст ([в] BSTR newText);
[propget, ID(1) принадлежности к группам] HRESULT текст ([out, retval] BSTR * retval)
После того, как определяются свойства и методы, необходимо добавить ссылку на оператор interface в Вашем заявлении coclass. Например:
[uuid(4B115281-32F0-11cf-AC85-444553540000)]
Компонентный класс документа
{
диспетчерский IAClick;
[по умолчанию] интерфейс IDualAClick;
}
После того, как ваш ODL файл был обновлен, используйте MFC в интерфейс карты механизм для определения класса реализации для сдвоенного интерфейса в вашем классе объекта и сделать соответствующие записи в библиотеке MFC QueryInterface механизм. Вам нужен один вход в INTERFACE_PART блок для каждой записи в оператор interface ODL, а также записи для интерфейса диспетчеризации. Каждая запись ODL с помощью атрибута propput нуждается функция с именем put_propertyname . Каждая запись с помощью атрибута propget требуется функция с именемget_propertyname.
Для определения класса реализации для ваших два интерфейса, добавьте DUAL_INTERFACE_PART блок к определению класса объекта. Например:
BEGIN_DUAL_INTERFACE_PART (DualAClick, IDualAClick)
STDMETHOD(put_text) (авторскими BSTR newText);
STDMETHOD(get_text) (авторскими BSTR FAR * retval);
STDMETHOD(put_x) (авторскими короткие newX);
STDMETHOD(get_x) (авторскими короткие FAR * retval);
STDMETHOD(put_y) (авторскими короткие newY);
STDMETHOD(get_y) (авторскими короткие FAR * retval);
STDMETHOD(put_Position) (авторскими IDualAutoClickPoint FAR * newPosition);
STDMETHOD(get_Position) (авторскими IDualAutoClickPoint Дальний Дальний ** retval);
STDMETHOD(RefreshWindow)(this);
STDMETHOD(SetAllProps) (короткие x авторскими, короткие y, текст BSTR);
STDMETHOD(ShowWindow)(this);
END_DUAL_INTERFACE_PART(DualAClick)
Для подключения сдвоенный интерфейс к библиотеке MFC механизм QueryInterface , добавьте INTERFACE_PART Вход в интерфейс карты
BEGIN_INTERFACE_MAP (CAutoClickDoc, CDocument)
INTERFACE_PART (CAutoClickDoc, DIID_IAClick, направление)
INTERFACE_PART (CAutoClickDoc, IID_IDualAClick, DualAClick)
END_INTERFACE_MAP()
Затем вам нужно заполнить в реализации интерфейса. В основном вы сможете делегировать существующую реализацию IDispatch MFC. Например:
STDMETHODIMP_(ULONG) CAutoClickDoc::XDualAClick::AddRef()
{
METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
return pThis->ExternalAddRef();
}
STDMETHODIMP_(ULONG) CAutoClickDoc::XDualAClick::Release()
{
METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
return pThis->ExternalRelease();
}
STDMETHODIMP CAutoClickDoc::XDualAClick::QueryInterface(
REFIID iid, LPVOID* ppvObj)
{
METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
return pThis->ExternalQueryInterface(&iid, ppvObj);
}
STDMETHODIMP CAutoClickDoc::XDualAClick::GetTypeInfoCount(
UINT FAR* pctinfo)
{
METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
LPDISPATCH lpDispatch = pThis->GetIDispatch(FALSE);
ASSERT(lpDispatch != NULL);
return lpDispatch->GetTypeInfoCount(pctinfo);
}
STDMETHODIMP CAutoClickDoc::XDualAClick::GetTypeInfo(
UINT itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo)
{
METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
LPDISPATCH lpDispatch = pThis->GetIDispatch(FALSE);
ASSERT(lpDispatch != NULL);
return lpDispatch->GetTypeInfo(itinfo, lcid, pptinfo);
}
STDMETHODIMP CAutoClickDoc::XDualAClick::GetIDsOfNames(
REFIID riid, OLECHAR FAR* FAR* rgszNames, UINT cNames,
LCID lcid, DISPID FAR* rgdispid)
{
METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
LPDISPATCH lpDispatch = pThis->GetIDispatch(FALSE);
ASSERT(lpDispatch != NULL);
return lpDispatch->GetIDsOfNames(riid, rgszNames, cNames,
lcid, rgdispid);
}
STDMETHODIMP CAutoClickDoc::XDualAClick::Invoke(
DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags,
DISPPARAMS FAR* pdispparams, VARIANT FAR* pvarResult,
EXCEPINFO FAR* pexcepinfo, UINT FAR* puArgErr)
{
METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
LPDISPATCH lpDispatch = pThis->GetIDispatch(FALSE);
ASSERT(lpDispatch != NULL);
return lpDispatch->Invoke(dispidMember, riid, lcid,
wFlags, pdispparams, pvarResult,
pexcepinfo, puArgErr);
}
Для вашего объекта методы и свойства функций методов доступа вам необходимо заполнить в осуществлении. Обычно метод и свойство функции можно делегировать обратно в методы, сформированные с помощью ClassWizard. Однако если вы настроили свойства прямой доступ к переменным, необходимо написать код для get/поместить значение в переменную. Например:
STDMETHODIMP CAutoClickDoc::XDualAClick::put_text(BSTR newText)
{
nbsp; METHOD_PROLOGUE (CAutoClickDoc, DualAClick)
/ / MFC автоматически преобразует из Unicode BSTR с / / Ansi CString, в случае необходимости...
pThis - > m_str = newText;
возвращение NOERROR;
}
STDMETHODIMP CAutoClickDoc::XDualAClick::get_text(BSTR* retval)
{
METHOD_PROLOGUE (CAutoClickDoc, DualAClick)
/ / MFC автоматически преобразует из CString Ansi для / / Unicode BSTR, в случае необходимости...
pThis - > m_str.SetSysString(retval);
возвращение NOERROR;
}
Передача двойного интерфейса указатели
Передавая указатель двойного интерфейса не является простым, особенно, если вам нужно позвонить CCmdTarget::FromIDispatch. FromIDispatch работает только на MFC IDispatch указатели. Одним из способов обхода этого заключается в том, чтобы запросить оригинальный набор указатель IDispatch вверх от MFC и передать этому указателю функции, которые в ней нуждаются. Например
(CAutoClickDoc::XDualAClick::put_Position) STDMETHODIMP
nbsp; IDualAutoClickPoint Дальний * newPosition)
{
METHOD_PROLOGUE (CAutoClickDoc, DualAClick)
LPDISPATCH lpDisp = NULL;
newPosition - > QueryInterface (IID_IDispatch, (LPVOID *) & lpDisp);
pThis - > SetPosition(lpDisp);
lpDisp - > Release();
возвращение NOERROR;
}
Перед передачей указатель обратно через два интерфейса метода, может потребоваться преобразовать его из MFC IDispatch указателя в указатель двойного интерфейса. Например:
(CAutoClickDoc::XDualAClick::get_Position) STDMETHODIMP
nbsp; IDualAutoClickPoint Дальний ** FAR retval)
{
METHOD_PROLOGUE (CAutoClickDoc, DualAClick)
LPDISPATCH lpDisp;
lpDisp = pThis - > GetPosition();
lpDisp - > QueryInterface (IID_IDualAutoClickPoint, (LPVOID *) retval);
возвращение NOERROR;
}
Регистрация библиотеки типов приложения
AppWizard не создает код для регистрации в системе приложения сервера OLE-автоматизации библиотеки типов. Хотя существуют другие способы для регистрации библиотеки типов, это удобно иметь зарегистрировать библиотеку типов, когда он обновляет свои сведения о типе OLE, то есть, каждый раз, когда приложение запускается автономных приложений.
Зарегистрировать библиотеку типов приложения при каждом запуске отдельного приложения:
InitInstance функции, найдите вызов COleObjectFactory::UpdateRegistryAll. После вызова этого метода добавьте вызов AfxOleRegisterTypeLib, указав LIBID , соответствующий вашей библиотеки типов, вместе с именем библиотеки типов/ / Когда серверное приложение запускается отдельный, это хорошая идея
/ / для обновления системного реестра в случае, если он поврежден.
m_server.UpdateRegistry(OAT_DISPATCH_OBJECT);
COleObjectFactory::UpdateRegistryAll();
/ / DUAL_SUPPORT_START
/ / Библиотека типов зарегистрирована или сдвоенный интерфейс не будет работать.
AfxOleRegisterTypeLib(AfxGetInstanceHandle(), LIBID_ACDual, _T("AutoClik.TLB"));
/ / DUAL_SUPPORT_END
Изменение параметров построения проекта для размещения изменения библиотеки типов
Чтобы изменить параметры построения проекта таким образом, чтобы созданный заголовочный файл, содержащий определения UUID MkTypLib всякий раз, когда библиотека типов перестраивается
Добавление определений UUID из MkTypLib созданных заголовочного файла в проект:
/ / initIIDs.c: Определяет идентификаторы IID для сдвоенных интерфейсов
/ / Это не должны быть построены с предкомпилированного заголовка.
# include lt;ole2.h >
# include <initguid.h>
# include «acdual.h»
Указание правильного имени класса объекта в библиотеке типов
Мастера, поставляются с Visual C++ неправильно используют имя класса реализации для указания класса coclass в файл в каталоге сервера ODL для OLE-создаваемым классов. В то время как это будет работать, имя класса реализации не вероятно имя класса, пользователям вашего объекта для использования. Чтобы указать правильное имя, откройте файл ODL, разместить каждого компонентного класса и заменить имя класса реализации с правильным именем внешнего.
Обратите внимание, что при изменении оператора класса coclass, имена переменных s CLSIDв файле заголовка, генерируемые MkTypLib соответственно изменится. Вам нужно будет обновить код, чтобы использовать новые имена переменных.
Обработка исключений и интерфейсы ошибок автоматизации
Методы и функции метода доступа к свойству объекта автоматизации могут создавать исключения. Если это так, следует обрабатывать их в реализации двойного интерфейса и передавать информацию об исключении контроллер через OLE-автоматизации обработки ошибок интерфейс IErrorInfo. Этот интерфейс обеспечивает подробные, контекстуальные сообщения об ошибках через интерфейсы IDispatch и VTBL. Чтобы указать, что обработчик ошибок доступен, следует реализовать интерфейс ISupportErrorInfo.
Чтобы проиллюстрировать механизм обработки ошибок, предполагается, что генерируемые ClassWizard функции, используемые для реализации поддержки стандартное направление создавать исключения. MFC в осуществление IDispatch::Invoke обычно перехватывает эти исключения и преобразует их в EXCEPTINFO структура, вернулся через вызов Invoke . Однако когда используется интерфейс VTBL, вы несете ответственность за перехват исключений. В качестве примера защитить ваши методы, два интерфейса
STDMETHODIMP CAutoClickDoc::XDualAClick::put_text(BSTR newText)
{
nbsp; METHOD_PROLOGUE (CAutoClickDoc, DualAClick)
TRY_DUAL(IID_IDualAClick)
{
/ / MFC автоматически преобразует из Unicode BSTR с / / Ansi CString, в случае необходимости...
pThis - > m_str = newText;
возвращение NOERROR;
}
CATCH_ALL_DUAL
}
CATCH_ALL_DUALзаботится о возвращении правильный код ошибки при возникновении исключения. CATCH_ALL_DUALпреобразует исключение MFC в OLE-автоматизации обработки ошибок информацию с помощью интерфейса ICreateErrorInfo . (Пример CATCH_ALL_DUAL макрос содержится в файле MFCDUAL.H в образец ACDUAL . Функция он вызывает для обработки исключений, DualHandleException , находится в файле MFCDUAL.НПК.) CATCH_ALL_DUALопределяет код ошибки для возвращения типа исключения, которое произошло
hR = MAKE_HRESULT (SEVERITY_ERROR, FACILITY_ITF, nbsp; (e - > m_wCode + 0x200))
Это создает значение HRESULT для интерфейса, который вызвал исключение. Код ошибки компенсируется 0x200 для избежания любых коллизий с системой s HRESULTдля стандартных интерфейсов OLE.
E_OUTOFMEMORY возвращается.E_UNEXPECTED возвращается.Чтобы указать, что используется обработчик ошибок OLE-автоматизации, следует реализовать интерфейс ISupportErrorInfo.
Во-первых добавьте код в определение класса автоматизации, чтобы показать, что он поддерживает ISupportErrorInfo.
Во-вторых добавьте код для карты интерфейса класса автоматизации для связывания ISupportErrorInfo класса реализации с механизмом QueryInterface MFC. INTERFACE_PARTЗаявление соответствует класс, определенный для ISupportErrorInfo.
Наконец Реализуйте класс, определенный для поддержки ISupportErrorInfo.
( Образец ACDUAL содержит три макросы, чтобы помочь сделать эти три шага, DECLARE_DUAL_ERRORINFO , DUAL_ERRORINFO_PART , и IMPLEMENT_DUAL_ERRORINFO , все содержащиеся в MFCDUAL.Х.)
В следующем примере реализуется класс для поддержки ISupportErrorInfo. CAutoClickDoc— это имя класса автоматизации и IID_IDualAClick IID интерфейса, который является источником ошибки сообщается через объект ошибки OLE-автоматизации:
STDMETHODIMP_(ulong) CAutoClickDoc::XSupportErrorInfo::AddRef() {}
nbsp; METHOD_PROLOGUE (CAutoClickDoc, SupportErrorInfo) возвращение pThis - > ExternalAddRef();
} STDMETHODIMP_(ULONG) CAutoClickDoc::XSupportErrorInfo::Release() {METHOD_PROLOGUE (CAutoClickDoc, SupportErrorInfo) возвращение pThis - > ExternalRelease();
} CAutoClickDoc::XSupportErrorInfo::QueryInterface (REFIID iid, LPVOID * ppvObj) STDMETHODIMP {METHOD_PROLOGUE (CAutoClickDoc, SupportErrorInfo) возвращение pThis - > ExternalQueryInterface (& iid, ppvObj);
} CAutoClickDoc::XSupportErrorInfo::InterfaceSupportsErrorInfo (REFIID iid) STDMETHODIMP {METHOD_PROLOGUE (CAutoClickDoc, SupportErrorInfo) возврата (iid == IID_IDualAClick)? S_OK: S_FALSE;
}
Технические примечания по номеру |nbsp; Технические примечания по категориям