Поделиться через


TN039. Реализация автоматизации MFC/OLE

Примечание.

Следующее техническое примечание не было обновлено, поскольку сначала оно было включено в электронную документацию. В результате некоторые процедуры и разделы могут быть устаревшими или неверными. Для получения последних сведений рекомендуется выполнить поиск интересующей темы в алфавитном указателе документации в Интернете.

Обзор интерфейса OLE IDispatch

Интерфейс IDispatch — это средство, с помощью которого приложения предоставляют методы и свойства, такие как другие приложения, такие как Visual BASIC или другие языки, могут использовать функции приложения. Наиболее важной частью IDispatch::Invoke этого интерфейса является функция. MFC использует "карты отправки" для реализации IDispatch::Invoke. Карта отправки предоставляет сведения о реализации MFC по макету или "фигуре" производных CCmdTargetклассов, таким образом, что он может напрямую управлять свойствами объекта или вызывать функции-члены в объекте для удовлетворения IDispatch::Invoke запросов.

В большинстве случаев КлассWizard и MFC сотрудничают, чтобы скрыть большую часть деталей автоматизации OLE от программиста приложений. Программист сосредоточиться на фактических функциональных возможностях для предоставления в приложении и не нужно беспокоиться о базовой сантехнике.

Однако существуют случаи, когда необходимо понять, что MFC делает за кулисами. Эта заметка будет решать, как платформа назначает DISPIDфункциям и свойствам членов. Знание алгоритма MFC, используемого для назначения disPID, необходимо только в том случае, если необходимо знать идентификаторы, например при создании "библиотеки типов" для объектов приложения.

Назначение DISPID MFC

Хотя конечный пользователь службы автоматизации (например, пользователь Visual Basic), видит фактические имена включенных в автоматизации свойств и методов в коде (например, obj). ShowWindow), реализация IDispatch::Invoke не получает фактических имен. По соображениям оптимизации он получает DISPID, который представляет собой 32-разрядный "волшебный файл cookie", описывающий метод или свойство, к которому требуется получить доступ. Эти значения DISPID возвращаются из реализации с помощью другого IDispatch метода.IDispatch::GetIDsOfNames Клиентское приложение автоматизации будет вызывать GetIDsOfNames один раз для каждого члена или свойства, к нему планируется получить доступ, и кэшировать их для последующих вызовов IDispatch::Invoke. Таким образом, дорогостоящий поиск строк выполняется только один раз для каждого использования объекта, а не один раз для каждого IDispatch::Invoke вызова.

MFC определяет disPIDдля каждого метода и свойства на основе двух вещей:

  • Расстояние от верхней части карты отправки (1 относительно)

  • Расстояние карты отправки от наиболее производного класса (0 относительно)

DISPID делится на две части. LoWORD DISPID содержит первый компонент, расстояние от верхней части карты отправки. HIWORD содержит расстояние от самого производного класса. Например:

class CDispPoint : public CCmdTarget
{
public:
    short m_x, m_y;
    // ...
    DECLARE_DISPATCH_MAP()
    // ...
};

class CDisp3DPoint : public CDispPoint
{
public:
    short m_z;
    // ...
    DECLARE_DISPATCH_MAP()
    // ...
};

BEGIN_DISPATCH_MAP(CDispPoint, CCmdTarget)
    DISP_PROPERTY(CDispPoint, "x", m_x, VT_I2)
    DISP_PROPERTY(CDispPoint, "y", m_y, VT_I2)
END_DISPATCH_MAP()

BEGIN_DISPATCH_MAP(CDisp3DPoint, CDispPoint)
    DISP_PROPERTY(CDisp3DPoint, "z", m_z, VT_I2)
END_DISPATCH_MAP()

Как видно, есть два класса, оба из которых предоставляют интерфейсы автоматизации OLE. Один из этих классов является производным от другого, поэтому использует функциональные возможности базового класса, включая часть автоматизации OLE ("x" и "y" в данном случае).

MFC создаст DISPIDдля класса CDispPoint следующим образом:

property X    (DISPID)0x00000001
property Y    (DISPID)0x00000002

Так как свойства не находятся в базовом классе, HIWORDDISPID всегда равен нулю (расстояние от самого производного класса для CDispPoint равно нулю).

MFC создаст DISPIDдля класса CDisp3DPoint следующим образом:

property Z    (DISPID)0x00000001
property X    (DISPID)0x00010001
property Y    (DISPID)0x00010002

Свойство Z присваивается DISPID с нулевым HIWORD , так как оно определено в классе, который предоставляет свойства CDisp3DPoint. Так как свойства X и Y определены в базовом классе, HIWORD DISPID равно 1, так как класс, в котором эти свойства определены, находится на расстоянии одного производных от самого производного класса.

Примечание.

LoWORD всегда определяется позицией в карте, даже если в карте существуют записи с явным DISPID (см. следующий раздел для получения сведений о _ID версиях DISP_PROPERTY и DISP_FUNCTION макросах).

Расширенные функции карты диспетчеризации MFC

Существует ряд дополнительных функций, которые ClassWizard не поддерживает с этим выпуском Visual C++. ClassWizard поддерживает DISP_FUNCTION, DISP_PROPERTYа DISP_PROPERTY_EX также определяет метод, свойство переменной члена и свойство функции элемента get/set соответственно. Обычно эти возможности необходимы для создания большинства серверов автоматизации.

Следующие дополнительные макросы можно использовать, если поддерживаемые макросы ClassWizard не достаточно: DISP_PROPERTY_NOTIFYи DISP_PROPERTY_PARAM.

DISP_PROPERTY_NOTIFY — описание макроса

DISP_PROPERTY_NOTIFY(
    theClass,
    pszName,
    memberName,
    pfnAfterSet,
    vtPropType)

Параметры

TheClass
Имя класса.

pszName
Внешнее имя свойства.

Membername
Имя переменной-члена, в которой хранится свойство.

pfnAfterSet
Имя функции-члена для вызова при изменении свойства.

vtPropType
Значение, указывающее тип свойства.

Замечания

Этот макрос очень похож на DISP_PROPERTY, за исключением того, что он принимает дополнительный аргумент. Дополнительный аргумент pfnAfterSet должен быть функцией-членом, которая не возвращает ничего и не принимает параметров " void OnPropertyNotify()". Он будет вызываться после изменения переменной-члена.

DISP_PROPERTY_PARAM — описание макроса

DISP_PROPERTY_PARAM(
    theClass,
    pszName,
    pfnGet,
    pfnSet,
    vtPropType,
    vtsParams)

Параметры

TheClass
Имя класса.

pszName
Внешнее имя свойства.

memberGet
Имя функции-члена, используемой для получения свойства.

memberSet
Имя функции-члена, используемой для задания свойства.

vtPropType
Значение, указывающее тип свойства.

vtsParams
Строка пробелов, разделенных VTS_ для каждого параметра.

Замечания

Как и макрос DISP_PROPERTY_EX, этот макрос определяет свойство, доступ к которым обращается с помощью отдельных функций элемента Get и Set. Однако этот макрос позволяет указать список параметров для свойства. Это полезно для реализации свойств, индексированных или параметризованных другим способом. Параметры всегда будут помещены в первую очередь, а затем новое значение свойства. Например:

DISP_PROPERTY_PARAM(CMyObject, "item", GetItem, SetItem, VT_DISPATCH, VTS_I2 VTS_I2)

будет соответствовать функциям получения и задания элементов:

LPDISPATCH CMyObject::GetItem(short row, short col)
void CMyObject::SetItem(short row, short col, LPDISPATCH newValue)

DISP_XXXX_ID — описания макросов

DISP_FUNCTION_ID(
    theClass,
    pszName,
    dispid,
    pfnMember,
    vtRetVal,
    vtsParams)
DISP_PROPERTY_ID(
    theClass,
    pszName,
    dispid,
    memberName,
    vtPropType)
DISP_PROPERTY_NOTIFY_ID(
    theClass,
    pszName,
    dispid,
    memberName,
    pfnAfterSet,
    vtPropType)
DISP_PROPERTY_EX_ID(
    theClass,
    pszName,
    dispid,
    pfnGet,
    pfnSet,
    vtPropType)
DISP_PROPERTY_PARAM_ID(
    theClass,
    pszName,
    dispid,
    pfnGet,
    pfnSet,
    vtPropType,
    vtsParams)

Параметры

TheClass
Имя класса.

pszName
Внешнее имя свойства.

Dispid
Фиксированный DISPID для свойства или метода.

pfnGet
Имя функции-члена, используемой для получения свойства.

pfnSet
Имя функции-члена, используемой для задания свойства.

Membername
Имя переменной-члена, сопоставленной с свойством

vtPropType
Значение, указывающее тип свойства.

vtsParams
Строка пробелов, разделенных VTS_ для каждого параметра.

Замечания

Эти макросы позволяют указать DISPID вместо автоматического назначения MFC. Эти расширенные макросы имеют те же имена, за исключением того, что идентификатор добавляется к имени макроса (например , DISP_PROPERTY_ID) и идентификатор определяется параметром, указанным сразу после параметра pszName . См. AFXDISP. H для получения дополнительных сведений об этих макросах. Записи _ID должны размещаться в конце карты отправки. Они будут влиять на автоматическое поколение DISPID таким же образом, как и версия макроса, не _ID (disPID s определяются позицией). Например:

BEGIN_DISPATCH_MAP(CDisp3DPoint, CCmdTarget)
    DISP_PROPERTY(CDisp3DPoint, "y", m_y, VT_I2)
    DISP_PROPERTY(CDisp3DPoint, "z", m_z, VT_I2)
    DISP_PROPERTY_ID(CDisp3DPoint, "x", 0x00020003, m_x, VT_I2)
END_DISPATCH_MAP()

MFC создаст DISPID для класса CDisp3DPoint следующим образом:

property X    (DISPID)0x00020003
property Y    (DISPID)0x00000002
property Z    (DISPID)0x00000001

Указание фиксированного DISPID полезно для обеспечения обратной совместимости с ранее существующим интерфейсом диспетчера или реализации определенных системных методов или свойств (обычно указывается отрицательным DISPID, например коллекцией DISPID_NEWENUM).

Получение интерфейса IDispatch для COleClientItem

Многие серверы поддерживают автоматизацию в своих объектах документов вместе с функциональностью сервера OLE. Чтобы получить доступ к этому интерфейсу автоматизации, необходимо напрямую получить доступ к переменной-члену COleClientItem::m_lpObject . Приведенный ниже код извлекает интерфейс для объекта, производного IDispatch от COleClientItem. Вы можете включить приведенный ниже код в приложение, если вы найдете эту функциональность:

LPDISPATCH CMyClientItem::GetIDispatch()
{
    ASSERT_VALID(this);
    ASSERT(m_lpObject != NULL);

    LPUNKNOWN lpUnk = m_lpObject;

    Run();      // must be running

    LPOLELINK lpOleLink = NULL;
    if (m_lpObject->QueryInterface(IID_IOleLink,
        (LPVOID FAR*)&lpOleLink) == NOERROR)
    {
        ASSERT(lpOleLink != NULL);
        lpUnk = NULL;
        if (lpOleLink->GetBoundSource(&lpUnk) != NOERROR)
        {
            TRACE0("Warning: Link is not connected!\n");
            lpOleLink->Release();
            return NULL;
        }
        ASSERT(lpUnk != NULL);
    }

    LPDISPATCH lpDispatch = NULL;
    if (lpUnk->QueryInterface(IID_IDispatch, &lpDispatch) != NOERROR)
    {
        TRACE0("Warning: does not support IDispatch!\n");
        return NULL;
    }

    ASSERT(lpDispatch != NULL);
    return lpDispatch;
}

Затем интерфейс диспетчера, возвращаемый этой функцией, может использоваться непосредственно или присоединен к типобезопасного COleDispatchDriver доступа. Если вы используете его напрямую, убедитесь, что его член вызывается Release при использовании указателя ( COleDispatchDriver деструктор выполняет это по умолчанию).

См. также

Технические примечания по номеру
Технические примечания по категории