Udostępnij za pośrednictwem


TN039: implementacja automatyzacji MFC/OLE

Uwaga

Następująca uwaga techniczna nie została zaktualizowana, ponieważ została po raz pierwszy uwzględniona w dokumentacji online. W związku z tym niektóre procedury i tematy mogą być nieaktualne lub nieprawidłowe. Aby uzyskać najnowsze informacje, zaleca się wyszukanie interesującego tematu w indeksie dokumentacji online.

Omówienie interfejsu OLE IDispatch

Interfejs IDispatch jest sposobem, za pomocą którego aplikacje uwidaczniają metody i właściwości, takie jak inne aplikacje, takie jak Visual BASIC lub inne języki, mogą korzystać z funkcji aplikacji. Najważniejszą częścią tego interfejsu IDispatch::Invoke jest funkcja . Usługa MFC używa funkcji "dispatch maps" do zaimplementowania IDispatch::Invokeelementu . Mapa wysyłania zawiera informacje o implementacji MFC w układzie lub "kształcie" CCmdTargetklas pochodnych, dzięki czemu może bezpośrednio manipulować właściwościami obiektu lub wywoływać funkcje składowe w obiekcie w celu spełnienia IDispatch::Invoke żądań.

W większości przypadków klasyWizard i MFC współpracują, aby ukryć większość szczegółów automatyzacji OLE od programisty aplikacji. Programista koncentruje się na rzeczywistych funkcjach, aby uwidocznić w aplikacji i nie musi martwić się o podstawową instalację wodną.

Istnieją jednak przypadki, w których konieczne jest zrozumienie, co MFC robi za kulisami. Ta uwaga dotyczy sposobu przypisywania identyfikatorów DISPIDdo funkcji i właściwości składowych. Znajomość algorytmu MFC używanego do przypisywania identyfikatorów DISPIDjest niezbędna tylko wtedy, gdy musisz znać identyfikatory, takie jak podczas tworzenia "biblioteki typów" dla obiektów aplikacji.

Przypisanie MFC DISPID

Mimo że użytkownik końcowy automatyzacji (na przykład użytkownik języka Visual Basic), widzi rzeczywiste nazwy właściwości i metod z włączoną automatyzacją w kodzie (np. obj). ShowWindow), implementacja nie otrzymuje rzeczywistych IDispatch::Invoke nazw. Ze względów optymalizacji otrzymuje on identyfikator DISPID, czyli 32-bitowy "magiczny plik cookie", który opisuje metodę lub właściwość, która ma być uzyskiwana. Te wartości DISPID są zwracane z implementacji IDispatch za pomocą innej metody o nazwie IDispatch::GetIDsOfNames. Aplikacja kliencka automatyzacji wywoła GetIDsOfNames raz dla każdego elementu członkowskiego lub właściwości, do których zamierza uzyskać dostęp, i buforuje je w celu późniejszego wywołania metody IDispatch::Invoke. W ten sposób kosztowne wyszukiwanie ciągów odbywa się tylko raz na użycie obiektu, a nie raz na IDispatch::Invoke wywołanie.

MFC określa identyfikatory DISPIDdla każdej metody i właściwości na podstawie dwóch elementów:

  • Odległość od góry mapy wysyłki (1 względna)

  • Odległość mapy wysyłki z najbardziej pochodnej klasy (względna 0)

Identyfikator DISPID jest podzielony na dwie części. LowORD identyfikatora DISPID zawiera pierwszy składnik, odległość od góry mapy wysyłki. HIWORD zawiera odległość od najbardziej pochodnej klasy. Przykład:

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()

Jak widać, istnieją dwie klasy, z których oba uwidaczniają interfejsy automatyzacji OLE. Jedna z tych klas pochodzi z drugiej i w ten sposób wykorzystuje funkcje klasy bazowej, w tym część automatyzacji OLE ("x" i "y" w tym przypadku).

MFC wygeneruje identyfikatory DISPIDdla klasy CDispPoint w następujący sposób:

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

Ponieważ właściwości nie znajdują się w klasie bazowej, HIWORD identyfikatora DISPID jest zawsze zerowy (odległość od najbardziej pochodnej klasy dla CDispPoint wynosi zero).

MFC wygeneruje identyfikatory DISPIDdla klasy CDisp3DPoint w następujący sposób:

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

Właściwość Z ma identyfikator DISPID z zerową wartością HIWORD , ponieważ jest zdefiniowana w klasie, która uwidacznia właściwości CDisp3DPoint. Ponieważ właściwości X i Y są zdefiniowane w klasie bazowej, HIWORD identyfikatora DISPID wynosi 1, ponieważ klasa, w której te właściwości są zdefiniowane, znajduje się w odległości jednej pochodnej od najbardziej pochodnej klasy.

Uwaga

Pozycja LOWORD jest zawsze określana przez położenie na mapie, nawet jeśli na mapie istnieją wpisy z jawnym identyfikatorem DISPID (zobacz następną sekcję, aby uzyskać informacje na temat _ID wersji DISP_PROPERTY makr i DISP_FUNCTION ).

Zaawansowane funkcje mapy wysyłania MFC

Istnieje wiele dodatkowych funkcji, których klasa ClassWizard nie obsługuje w tej wersji programu Visual C++. KlasaWizard obsługuje , DISP_FUNCTIONDISP_PROPERTYi DISP_PROPERTY_EX które definiują odpowiednio metodę, właściwość zmiennej składowej i właściwość funkcji składowej get/set. Te możliwości są zwykle potrzebne do utworzenia większości serwerów automatyzacji.

Następujące dodatkowe makra mogą być używane, gdy obsługiwane makra ClassWizard nie są odpowiednie: DISP_PROPERTY_NOTIFYi DISP_PROPERTY_PARAM.

DISP_PROPERTY_NOTIFY — opis makra

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

Parametry

theClass
Nazwa klasy.

pszName
Nazwa zewnętrzna właściwości.

Membername
Nazwa zmiennej składowej, w której jest przechowywana właściwość.

pfnAfterSet
Nazwa funkcji składowej, która ma być wywoływana, gdy właściwość zostanie zmieniona.

vtPropType
Wartość określająca typ właściwości.

Uwagi

To makro jest podobne do DISP_PROPERTY, z tą różnicą, że akceptuje dodatkowy argument. Dodatkowy argument pfnAfterSet powinien być funkcją składową, która nie zwraca żadnych parametrów i nie przyjmuje parametrów "void OnPropertyNotify()". Zostanie ona wywołana po zmodyfikowaniu zmiennej składowej.

DISP_PROPERTY_PARAM — opis makra

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

Parametry

theClass
Nazwa klasy.

pszName
Nazwa zewnętrzna właściwości.

element członkowskiPobierz
Nazwa funkcji składowej użytej do pobrania właściwości.

memberSet
Nazwa funkcji składowej używanej do ustawiania właściwości.

vtPropType
Wartość określająca typ właściwości.

vtsParams
Ciąg spacji oddzielony VTS_ dla każdego parametru.

Uwagi

Podobnie jak makro DISP_PROPERTY_EX, to makro definiuje właściwość dostępną z oddzielnymi funkcjami elementów członkowskich Get i Set. To makro umożliwia jednak określenie listy parametrów dla właściwości. Jest to przydatne w przypadku implementowania właściwości indeksowanych lub sparametryzowanych w inny sposób. Parametry będą zawsze umieszczane jako pierwsze, a następnie nowa wartość właściwości. Przykład:

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

odpowiadałoby funkcjom pobierania i ustawiania składowych:

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

DISP_XXXX_ID — opisy makr

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)

Parametry

theClass
Nazwa klasy.

pszName
Nazwa zewnętrzna właściwości.

Dispid
Stały identyfikator DISPID dla właściwości lub metody.

PfnGet
Nazwa funkcji składowej użytej do pobrania właściwości.

pfnSet
Nazwa funkcji składowej używanej do ustawiania właściwości.

Membername
Nazwa zmiennej składowej do mapowania na właściwość

vtPropType
Wartość określająca typ właściwości.

vtsParams
Ciąg spacji oddzielony VTS_ dla każdego parametru.

Uwagi

Te makra umożliwiają określenie identyfikatora DISPID zamiast automatycznego przypisywania przez MFC. Te zaawansowane makra mają takie same nazwy, z wyjątkiem tego, że identyfikator jest dołączany do nazwy makra (np. DISP_PROPERTY_ID), a identyfikator jest określany przez parametr określony tuż po parametrze pszName . Zobacz AFXDISP. H, aby uzyskać więcej informacji na temat tych makr. Na końcu mapy wysyłania należy umieścić wpisy _ID . Będą one miały wpływ na automatyczne generowanie DISPID w taki sam sposób, jak wersja innej niż _ID makra ( identyfikatory DISPIDsą określane według pozycji). Przykład:

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 wygeneruje identyfikatory DISPID dla klasy CDisp3DPoint w następujący sposób:

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

Określenie stałego identyfikatora DISPID jest przydatne do zachowania zgodności z poprzednimi wersjami istniejącego interfejsu wysyłania lub implementowania określonych metod lub właściwości zdefiniowanych przez system (zwykle wskazywanych przez ujemny identyfikator DISPID, taki jak kolekcja DISPID_NEWENUM ).

Pobieranie interfejsu IDispatch dla elementu COleClientItem

Wiele serwerów będzie obsługiwać automatyzację w swoich obiektach dokumentów wraz z funkcjonalnością serwera OLE. Aby uzyskać dostęp do tego interfejsu automatyzacji, należy bezpośrednio uzyskać dostęp do zmiennej składowej COleClientItem::m_lpObject . Poniższy kod pobierze IDispatch interfejs dla obiektu pochodzącego z COleClientItemklasy . Jeśli znajdziesz tę funkcję, możesz dołączyć poniższy kod w aplikacji:

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

Interfejs wysyłania zwrócony z tej funkcji może być następnie używany bezpośrednio lub dołączony do COleDispatchDriver elementu w celu uzyskania bezpiecznego dostępu. Jeśli używasz go bezpośrednio, upewnij się, że wywołujesz jego Release element członkowski, używając wskaźnika ( COleDispatchDriver destruktor domyślnie to robi).

Zobacz też

Uwagi techniczne według numerów
Uwagi techniczne według kategorii