TN065: Поддержка Двойн- Интерфейса для сервера OLE-автоматизации
Примечание |
---|
Следующая техническая заметка не была обновлена со времени сначала была включена в подключенной документации.В результате некоторые процедуры и разделы могут оказаться устаревшей или неверны.Последние новости, рекомендуется поиск раздела процента в подключенном индексу документации. |
Эта заметка содержит сведения о том, как добавить поддержку сдвоенных интерфейсов в приложение MFC-основанному сервер OLE-автоматизации.Пример В образце ACDUAL демонстрирует поддержку сдвоенных интерфейсов и код примера в этом заметку взят из ACDUAL.Макросы, описанные в этом DECLARE_DUAL_ERRORINFO заметку, например, DUAL_ERRORINFO_PART и IMPLEMENT_DUAL_ERRORINFO, является частью образца ACDUAL и могут быть расположены в MFCDUAL.H.
Сдвоенные интерфейсы
Хотя ole-автоматизации позволяет реализовать интерфейс IDispatch интерфейс VTBL или сдвоенный интерфейс (который включает оба Майкрософт настоятельно рекомендует), реализуемые сдвоенные интерфейсы для всех доступных объектов ole-автоматизации.Сдвоенные интерфейсы имеют значительные преимущества по сравнению IDispatch- только или интерфейсы VTBL-only:
Привязка может происходить во время компиляции через интерфейс VTBL или во время выполнения через IDispatch.
Контроллер OLE-автоматизации, которые могут использовать интерфейс VTBL могут могут использовать улучшенная производительность.
Существующие контроллер OLE-автоматизации, используют интерфейс IDispatch не перестанут работать.
Интерфейс VTBL проще вызвать из C++.
Сдвоенные интерфейсы, необходимые для обеспечения совместимости с функциями поддержки объекта Visual Basic.
Добавить поддержку сдвоенных интерфейсов в CCmdTarget-Основанному класс
Сдвоенный интерфейс действительно только пользовательский интерфейс, производный от IDispatch.Самый простой способ реализации поддержки сдвоенных интерфейсов в CCmdTarget- на основе класс, чтобы сначала реализует обычный интерфейс диспетчера в классе с помощью MFC и ClassWizard и добавляет пользовательский интерфейс далее.В большинстве случаев пользовательская реализация интерфейса просто делегатов обратно в реализации MFC IDispatch.
Во-первых, измените файл ODL сервера для определения сдвоенные интерфейсы для объектов.Чтобы определить сдвоенный интерфейс, необходимо использовать выписку интерфейса вместо выписки DISPINTERFACE мастера Visual C++, создающих.Вместо удаление существующей оператор DISPINTERFACE добавьте новую выписка интерфейса.Путем сохранения форма DISPINTERFACE можно продолжать использовать ClassWizard для добавления свойств и методов к конкретному объекту, однако необходимо добавить соответствующие свойства и методы для выписке интерфейса.
Выписка интерфейса для двойного интерфейса должна иметь атрибуты OLEAUTOMATION и DUAL и интерфейс должен быть производным от IDispatch.Можно использовать образец GUIDGEN для создания IID для двойного интерфейса:
[ uuid(0BDD0E81-0DD7-11cf-BBA8-444553540000), // IID_IDualAClick
oleautomation,
dual
]
interface IDualAClick : IDispatch
{
};
После создания выписку интерфейса, запустите добавление записей для методов и свойств.Сдвоенных интерфейсов, необходимые для изменения порядка списки параметров для этих методов и функции метода доступа свойства возвращают HRESULT двойном интерфейсе и передает их возвращаемые значения как параметры с атрибутами [retval,out].Помните, что для свойства, считанное нужно добавить и (propget) и функция доступа (propput) с таким же идентификатором.Примеры.
[propput, id(1)] HRESULT text([in] BSTR newText);
[propget, id(1)] HRESULT text([out, retval] BSTR* retval);
После своих методов и свойств укажите, необходимо добавить ссылку на выписке интерфейса в вашей выписке компонентного класса.Примеры.
[ uuid(4B115281-32F0-11cf-AC85-444553540000) ]
coclass Document
{
dispinterface IAClick;
[default] interface IDualAClick;
};
После того как файл ODL был обновлен, используется механизм сопоставления интерфейсов MFC для идентификации класса реализации двойного интерфейса в классе объектов и выполнить соответствующие записи в механизме QueryInterface MFC.Необходима одна запись в блоке INTERFACE_PART для каждой записи при выписке интерфейса ODL, а также записи для интерфейса диспетчеризации.Каждой записи ODL с атрибутом propput необходима функция с именем put_propertyname.Каждой записи с атрибутом propget необходима функция с именем get_propertyname.
Для идентификации класса для реализации двойного интерфейса, добавьте блок DUAL_INTERFACE_PART в определение класса объектов.Примеры.
BEGIN_DUAL_INTERFACE_PART(DualAClick, IDualAClick)
STDMETHOD(put_text)(THIS_ BSTR newText);
STDMETHOD(get_text)(THIS_ BSTR FAR* retval);
STDMETHOD(put_x)(THIS_ short newX);
STDMETHOD(get_x)(THIS_ short FAR* retval);
STDMETHOD(put_y)(THIS_ short newY);
STDMETHOD(get_y)(THIS_ short FAR* retval);
STDMETHOD(put_Position)(THIS_ IDualAutoClickPoint FAR* newPosition);
STDMETHOD(get_Position)(THIS_ IDualAutoClickPoint FAR* FAR* retval);
STDMETHOD(RefreshWindow)(THIS);
STDMETHOD(SetAllProps)(THIS_ short x, short y, BSTR text);
STDMETHOD(ShowWindow)(THIS);
END_DUAL_INTERFACE_PART(DualAClick)
Чтобы подключиться к механизму QueryInterface сдвоенный интерфейс MFC добавьте запись INTERFACE_PART к сопоставлению интерфейса:
BEGIN_INTERFACE_MAP(CAutoClickDoc, CDocument)
INTERFACE_PART(CAutoClickDoc, DIID_IAClick, Dispatch)
INTERFACE_PART(CAutoClickDoc, IID_IDualAClick, DualAClick)
END_INTERFACE_MAP()
Теперь необходимо заполнить реализацию интерфейса.В большинстве случаев следует делегировать к существующей реализации MFC IDispatch.Примеры.
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.Однако при настройке свойства для доступа к переменным непосредственно, то необходимо написать код для доступа и поместить значение в переменную.Примеры.
STDMETHODIMP CAutoClickDoc::XDualAClick::put_text(BSTR newText)
{
METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
// MFC automatically converts from Unicode BSTR to
// Ansi CString, if necessary...
pThis->m_str = newText;
return NOERROR;
}
STDMETHODIMP CAutoClickDoc::XDualAClick::get_text(BSTR* retval)
{
METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
// MFC automatically converts from Ansi CString to
// Unicode BSTR, if necessary...
pThis->m_str.SetSysString(retval);
return NOERROR;
}
Передача указателей сдвоенных интерфейсов
Передача собственный указатель сдвоенных интерфейсов не является прямолинейным, особенно если необходимо вызвать CCmdTarget::FromIDispatch.FromIDispatch работает только над указателями IDispatch MFC.Один из способов обхода этого запроса при установке указателя IDispatch оригинального MFC и передать этот указатель к функциям, которым он необходим.Примеры.
STDMETHODIMP CAutoClickDoc::XDualAClick::put_Position(
IDualAutoClickPoint FAR* newPosition)
{
METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
LPDISPATCH lpDisp = NULL;
newPosition->QueryInterface(IID_IDispatch, (LPVOID*)&lpDisp);
pThis->SetPosition(lpDisp);
lpDisp->Release();
return NOERROR;
}
Прежде чем передать указатель назад с помощью метода сдвоенных интерфейсов, можно преобразовать его из указателя MFC IDispatch к указателю сдвоенных интерфейсов.Примеры.
STDMETHODIMP CAutoClickDoc::XDualAClick::get_Position(
IDualAutoClickPoint FAR* FAR* retval)
{
METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
LPDISPATCH lpDisp;
lpDisp = pThis->GetPosition();
lpDisp->QueryInterface(IID_IDualAutoClickPoint, (LPVOID*)retval);
return NOERROR;
}
Регистрация библиотеки типов приложений
AppWizard не создает код, чтобы зарегистрировать библиотеку типов приложений сервер OLE-автоматизации с системой.Пока другие способы регистрация библиотеки типов, удобно иметь регистр приложения библиотеку типов, когда она обновляет его ЯВЛЯЕТСЯ сведения о типе, то есть когда приложение выполняется изолированное.
Регистрация библиотеки типов приложения, когда приложение выполняемая в изолированном режиме.
Включите AFXCTL.H в стандарте включает файл заголовка, STDAFX.H, чтобы обращаться к определению функции AfxOleRegisterTypeLib.
В функции InitInstance приложения найдите вызов COleObjectFactory::UpdateRegistryAll.После этого вызова добавьте вызов AfxOleRegisterTypeLib, указав LIBID, соответствующей библиотеки типов, а также имя библиотеки типов:
// When a server application is launched stand-alone, it is a good idea // to update the system registry in case it has been damaged. m_server.UpdateRegistry(OAT_DISPATCH_OBJECT); COleObjectFactory::UpdateRegistryAll(); // DUAL_SUPPORT_START // Make sure the type library is registered or dual interface won't work. AfxOleRegisterTypeLib(AfxGetInstanceHandle(), LIBID_ACDual, _T("AutoClik.TLB")); // DUAL_SUPPORT_END
Изменение параметров построения проекта, чтобы вместить изменения библиотеки типов
Изменение параметров построения проекта, чтобы файл заголовка, содержащая определения UUID будет сформирован MkTypLib, когда библиотека типов перестроена.
В меню Построение, нажмите кнопку Параметры, а затем выберите файл ODL из списка файлов для каждой конфигурации.
Перейдите на вкладку Типы OLE и укажите имя файла в поле имя файла Заголовок вывода.Используйте имя файла, который еще не используется проектом, поскольку MkTypLib перезаписать любой существующий файл.Нажмите кнопку ОК, чтобы закрыть диалоговое окно Параметры построения.
Добавление определения UUID из MkTypLib-произведенного файла заголовка в проект:
Включите MkTypLib-произведенный файл заголовка в стандарте включает файл заголовка, STDAFX.H.
Создайте новый файл INITIIDS.CPP и добавьте его к проекту.В этом файле, включите в MkTypLib-произведенный файл заголовка после включая OLE2.H и INITGUID.H:
// initIIDs.c: defines IIDs for dual interfaces // This must not be built with precompiled header. #include <ole2.h> #include <initguid.h> #include "acdual.h"
В меню Построение, нажмите кнопку Параметры, а затем выберите INITIIDS.CPP из списка файлов для каждой конфигурации.
Перейдите на вкладку C++ выберите категорию Предварительно откомпилированные заголовки и выберите переключатель Не использовать предварительно скомпилированные заголовки.Нажмите кнопку ОК, чтобы закрыть диалоговое окно Параметры построения.
Указание правильного имени класса объектов в библиотеку типов
Мастеры поставленных вместе с Visual C++ неправильно используют имя класса реализации, чтобы определить компонентный класс в файле ODL сервера для классов OLE-creatable.Будет работать, пока это имя класса реализации не нужно, чтобы пользователи возможно, имя класса объекта.Чтобы определить правильное имя, ODL открытие файла найдите каждый оператор компонентного класса и замените имя класса реализации с правильным внешним именем.
Обратите внимание, что если оператор будет изменена компонентный класс имена переменных CLSID в файле заголовка MkTypLib-произведенном изменяются соответствующим образом.Потребуется обновить код для использования новых имен переменной.
Обработка исключений и интерфейсы ошибок автоматизации
Методы объекта автоматизации и функции метода доступа свойства могут создавать исключения.В этом случае следует обработать их реализации сдвоенных интерфейсов и передавать сведения об исключении назад к контроллеру ole-автоматизации с помощью интерфейса обработки ошибок, IErrorInfo.Этот интерфейс предоставляет подробный, контекстуального сведения об ошибке, как с помощью IDispatch, так и через интерфейс VTBL.Для указания того, что обработчик ошибок доступен, необходимо реализовать интерфейс ISupportErrorInfo.
Для демонстрации механизм обработки ошибок, предположим, что ClassWizard-произведенные функции, используемые для реализации стандартных исключений поддержки диспетчера.Реализация MFC IDispatch::Invoke обычно перехватывает эти исключения и преобразует их в структуру EXCEPTINFO, которая возвращается посредством вызова Invoke.Однако при использовании интерфейс VTBL самостоятельно отвечают за перехват исключения.В качестве примера защитить методы сдвоенных интерфейсов:
STDMETHODIMP CAutoClickDoc::XDualAClick::put_text(BSTR newText)
{
METHOD_PROLOGUE(CAutoClickDoc, DualAClick)
TRY_DUAL(IID_IDualAClick)
{
// MFC automatically converts from Unicode BSTR to
// Ansi CString, if necessary...
pThis->m_str = newText;
return NOERROR;
}
CATCH_ALL_DUAL
}
CATCH_ALL_DUAL позаботится о возвращение правильный код ошибки при возникновении исключения.Преобразование CATCH_ALL_DUAL исключение MFC в данные обработки исключений с помощью интерфейса ICreateErrorInfo ole-автоматизации.(Макрос CATCH_ALL_DUAL примера в файле MFCDUAL.H в образце В образце ACDUAL.Функция он вызывает для обработки исключений, DualHandleException в файле MFCDUAL.CPP.) CATCH_ALL_DUAL указывает код ошибки, чтобы вернуться на основе типа исключения, который запустил:
COleDispatchException – в этом случае HRESULT построено с помощью следующего кода:
hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, (e->m_wCode + 0x200));
Это создает HRESULT, относящиеся к интерфейсу, который вызвал исключение.Код ошибки возмещен 0x200, чтобы избежать любые конфликты с система-определенным HRESULT s для стандартных интерфейсов OLE.
CMemoryException – в этом случае E_OUTOFMEMORY возвращается.
Любое другое исключение – в этом случае E_UNEXPECTED возвращается.
Для указания того, что обработчик ошибок ole-автоматизации используется, необходимо также реализовать интерфейс ISupportErrorInfo.
Сначала добавьте код в определение класса автоматизации для отображения поддерживает ISupportErrorInfo.
Затем добавьте код к сопоставлению интерфейса типа автоматизации, чтобы связать класс реализации ISupportErrorInfo с механизмом QueryInterface MFC.Оператор INTERFACE_PART соответствует классу, указанному для ISupportErrorInfo.
Наконец, реализуйте класс, определенный для поддержки ISupportErrorInfo.
(Образец В образце ACDUAL содержит 3 макроса для облегчения выполняет эти 3 шага, DECLARE_DUAL_ERRORINFO, DUAL_ERRORINFO_PART и IMPLEMENT_DUAL_ERRORINFO, все, содержащихся в MFCDUAL.H).
В следующем примере реализуется класс, определенный для поддержки ISupportErrorInfo.CAutoClickDoc имя класса автоматизации и IID_IDualAClickIID для интерфейса, какой из источника ошибок с помощью объекта ошибки ole-автоматизации.
STDMETHODIMP_(ULONG) CAutoClickDoc::XSupportErrorInfo::AddRef()
{
METHOD_PROLOGUE(CAutoClickDoc, SupportErrorInfo)
return pThis->ExternalAddRef();
}
STDMETHODIMP_(ULONG) CAutoClickDoc::XSupportErrorInfo::Release()
{
METHOD_PROLOGUE(CAutoClickDoc, SupportErrorInfo)
return pThis->ExternalRelease();
}
STDMETHODIMP CAutoClickDoc::XSupportErrorInfo::QueryInterface(
REFIID iid, LPVOID* ppvObj)
{
METHOD_PROLOGUE(CAutoClickDoc, SupportErrorInfo)
return pThis->ExternalQueryInterface(&iid, ppvObj);
}
STDMETHODIMP CAutoClickDoc::XSupportErrorInfo::InterfaceSupportsErrorInfo(
REFIID iid)
{
METHOD_PROLOGUE(CAutoClickDoc, SupportErrorInfo)
return (iid == IID_IDualAClick) ? S_OK : S_FALSE;
}