Share via


TN065: Unterstützung für duale Schnittstellen für OLE-Automatisierungsserver

Hinweis

Der folgende technische Hinweis wurde seit dem ersten Erscheinen in der Onlinedokumentation nicht aktualisiert. Daher können einige Verfahren und Themen veraltet oder falsch sein. Um aktuelle Informationen zu erhalten, wird empfohlen, das gewünschte Thema im Index der Onlinedokumentation zu suchen.

In diesem Hinweis wird erläutert, wie Sie einer MFC-basierten OLE-Automatisierungsserveranwendung duale Schnittstellenunterstützung hinzufügen. Das ACDUAL-Beispiel veranschaulicht die Unterstützung der dualen Schnittstelle, und der Beispielcode in dieser Notiz stammt aus ACDUAL. Die in diesem Hinweis beschriebenen Makros, z. B. DECLARE_DUAL_ERRORINFO, DUAL_ERRORINFO_PART und IMPLEMENT_DUAL_ERRORINFO, sind Teil des ACDUAL-Beispiels und finden Sie in MFCDUAL.H.

Duale Schnittstellen

Obwohl die OLE-Automatisierung Ihnen die Implementierung einer IDispatch Schnittstelle, einer VTBL-Schnittstelle oder einer dualen Schnittstelle (die beide umfasst) ermöglicht, empfiehlt Microsoft dringend, zwei Schnittstellen für alle verfügbar gemachten OLE-Automatisierungsobjekte zu implementieren. Duale Schnittstellen haben gegenüber -only- oder VTBL-only-Schnittstellen erhebliche Vorteile IDispatch:

  • Die Bindung kann zur Kompilierungszeit über die VTBL-Schnittstelle oder zur Laufzeit über IDispatchdie VTBL-Schnittstelle erfolgen.

  • OLE-Automatisierungscontroller, die die VTBL-Schnittstelle verwenden können, können von einer verbesserten Leistung profitieren.

  • Vorhandene OLE-Automatisierungscontroller, die die IDispatch Schnittstelle verwenden, funktionieren weiterhin.

  • Die VTBL-Schnittstelle kann von C++ einfacher aufgerufen werden.

  • Duale Schnittstellen sind für die Kompatibilität mit Visual Basic-Objektunterstützungsfeatures erforderlich.

Hinzufügen von Dual-Interface-Unterstützung zu einer CCmdTarget-basierten Klasse

Eine duale Schnittstelle ist wirklich nur eine benutzerdefinierte Schnittstelle, die von IDispatch. Die einfachste Möglichkeit zum Implementieren der Unterstützung der dualen Schnittstelle in einer CCmdTarget-basierten Klasse besteht darin, zuerst die normale Dispatch-Schnittstelle in Ihrer Klasse mithilfe von MFC und ClassWizard zu implementieren und die benutzerdefinierte Schnittstelle später hinzuzufügen. In den meisten Fällen delegiert Ihre benutzerdefinierte Schnittstellenimplementierung einfach wieder an die MFC-Implementierung IDispatch .

Ändern Sie zunächst die ODL-Datei für Ihren Server, um duale Schnittstellen für Ihre Objekte zu definieren. Um eine duale Schnittstelle zu definieren, müssen Sie eine Schnittstellen-Anweisung anstelle der DISPINTERFACE Anweisung verwenden, die von den Visual C++-Assistenten generiert wird. Anstatt die vorhandene DISPINTERFACE Anweisung zu entfernen, fügen Sie eine neue Schnittstellen-Anweisung hinzu. Wenn Sie das DISPINTERFACE Formular beibehalten, können Sie classWizard weiterhin verwenden, um Ihrem Objekt Eigenschaften und Methoden hinzuzufügen, aber Sie müssen der Schnittstellen-Anweisung die entsprechenden Eigenschaften und Methoden hinzufügen.

Eine Schnittstellen-Anweisung für eine duale Schnittstelle muss über die OLEAUTOMATION - und DUAL-Attribute verfügen, und die Schnittstelle muss von IDispatch. Sie können das GUIDGEN-Beispiel verwenden, um eine IID für die duale Schnittstelle zu erstellen:

[ uuid(0BDD0E81-0DD7-11cf-BBA8-444553540000), // IID_IDualAClick
    oleautomation,
    dual
]
interface IDualAClick : IDispatch
    {
    };

Nachdem Sie die Schnittstellen-Anweisung eingerichtet haben, beginnen Sie mit dem Hinzufügen von Einträgen für die Methoden und Eigenschaften. Für duale Schnittstellen müssen Sie die Parameterlisten neu anordnen, damit Ihre Methoden und Eigenschaftenaccessorfunktionen in der dualen Schnittstelle ein HRESULT zurückgeben und ihre Rückgabewerte als Parameter mit den Attributen [retval,out]übergeben. Denken Sie daran, dass Sie für Eigenschaften sowohl eine Lese-(propget) als auch eine Lese-/Schreibzugriffsfunktion (propput) mit derselben ID hinzufügen müssen. Zum Beispiel:

[propput, id(1)] HRESULT text([in] BSTR newText);
[propget, id(1)] HRESULT text([out, retval] BSTR* retval);

Nachdem Ihre Methoden und Eigenschaften definiert wurden, müssen Sie einen Verweis auf die Schnittstellen-Anweisung in Ihrer Coclass-Anweisung hinzufügen. Beispiel:

[ uuid(4B115281-32F0-11cf-AC85-444553540000) ]
coclass Document
{
    dispinterface IAClick;
    [default] interface IDualAClick;
};

Nachdem Die ODL-Datei aktualisiert wurde, verwenden Sie den Schnittstellenzuordnungsmechanismus von MFC, um eine Implementierungsklasse für die duale Schnittstelle in Ihrer Objektklasse zu definieren und die entsprechenden Einträge im MFC-Mechanismus QueryInterface vorzunehmen. Sie benötigen einen Eintrag im INTERFACE_PART Block für jeden Eintrag in der Schnittstellen-Anweisung der ODL sowie die Einträge für eine Dispatch-Schnittstelle. Jeder ODL-Eintrag mit dem Propput-Attribut benötigt eine Funktion mit dem Namen put_propertyname. Jeder Eintrag mit dem Propget-Attribut benötigt eine Funktion mit dem Namen get_propertyname.

Um eine Implementierungsklasse für die duale Schnittstelle zu definieren, fügen Sie ihrer Objektklassendefinition einen DUAL_INTERFACE_PART Block hinzu. Beispiel:

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)

Um die duale Schnittstelle mit dem QueryInterface-Mechanismus von MFC zu verbinden, fügen Sie einen INTERFACE_PART Eintrag zur Schnittstellenzuordnung hinzu:

BEGIN_INTERFACE_MAP(CAutoClickDoc, CDocument)
    INTERFACE_PART(CAutoClickDoc, DIID_IAClick, Dispatch)
    INTERFACE_PART(CAutoClickDoc, IID_IDualAClick, DualAClick)
END_INTERFACE_MAP()

Als Nächstes müssen Sie die Implementierung der Schnittstelle ausfüllen. In den meisten Fällen können Sie die vorhandene MFC-Implementierung IDispatch delegieren. Beispiel:

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

Für die Methoden und Eigenschaftenaccessorfunktionen ihres Objekts müssen Sie die Implementierung ausfüllen. Ihre Methoden- und Eigenschaftsfunktionen können in der Regel mit ClassWizard generierte Methoden zurück delegieren. Wenn Sie jedoch Eigenschaften für den direkten Zugriff auf Variablen einrichten, müssen Sie den Code schreiben, um den Wert in die Variable abzurufen/einzufügen. Beispiel:

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

Übergeben von Dual-Interface-Zeigern

Das Übergeben des Zeigers mit dualer Schnittstelle ist nicht einfach, insbesondere, wenn Sie aufrufen CCmdTarget::FromIDispatchmüssen. FromIDispatch funktioniert nur auf MFC-Zeigern IDispatch . Eine Möglichkeit, dies zu umgehen, besteht darin, den von MFC eingerichteten ursprünglichen IDispatch Zeiger abzufragen und diesen Zeiger an Funktionen zu übergeben, die ihn benötigen. Beispiel:

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

Bevor Sie einen Zeiger zurück über die Dual-Interface-Methode übergeben, müssen Sie ihn möglicherweise vom MFC-Zeiger IDispatch in den Zeiger der dualen Schnittstelle konvertieren. Beispiel:

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

Registrieren der Typbibliothek der Anwendung

AppWizard generiert keinen Code, um die Typbibliothek einer OLE-Automatisierungsserveranwendung beim System zu registrieren. Während es andere Möglichkeiten gibt, die Typbibliothek zu registrieren, ist es praktisch, dass die Anwendung die Typbibliothek beim Aktualisieren der OLE-Typinformationen registriert, d. h., wenn die Anwendung eigenständig ausgeführt wird.

So registrieren Sie die Typbibliothek der Anwendung, wenn die Anwendung eigenständig ausgeführt wird:

  • Schließen Sie AFXCTL ein. H in Ihrem Standard enthält Headerdatei, STDAFX. H, um auf die Definition der AfxOleRegisterTypeLib Funktion zuzugreifen.

  • Suchen Sie in der Funktion Ihrer Anwendung InitInstance den Aufruf von COleObjectFactory::UpdateRegistryAll. Fügen Sie nach diesem Aufruf einen Aufruf AfxOleRegisterTypeLibhinzu, der die LIBID angibt, die Ihrer Typbibliothek entspricht, zusammen mit dem Namen Ihrer Typbibliothek:

    // 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
    

Ändern von Projektbuild-Einstellungen zur Aufnahme von Typbibliotheksänderungen

So ändern Sie die Buildeinstellungen eines Projekts so, dass eine Headerdatei, die UUID-Definitionen enthält, von MkTypLib generiert wird, wenn die Typbibliothek neu erstellt wird:

  1. Klicken Sie im Menü "Erstellen" auf Einstellungen, und wählen Sie dann die ODL-Datei aus der Dateiliste für jede Konfiguration aus.

  2. Klicken Sie auf die Registerkarte "OLE-Typen ", und geben Sie einen Dateinamen im Feld "Ausgabekopfdateiname " an. Verwenden Sie einen Dateinamen, der noch nicht von Ihrem Projekt verwendet wird, da MkTypLib vorhandene Dateien überschreibt. Klicken Sie auf "OK", um das Dialogfeld "Einstellungen erstellen" zu schließen.

So fügen Sie die UUID-Definitionen aus der vom MkTypLib generierten Headerdatei zu Ihrem Projekt hinzu:

  1. Fügen Sie die vom MkTypLib generierte Headerdatei in Ihre Standarddatei ein, einschließlich Headerdatei stdafx.h.

  2. Erstellen Sie eine neue Datei, INITIIDS. CPP, und fügen Sie es zu Ihrem Projekt hinzu. Fügen Sie in dieser Datei ihre mkTypLib-generierte Headerdatei nach dem Einschließen von OLE2 ein. H und 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"
    
  3. Klicken Sie im Menü "Erstellen" auf Einstellungen, und wählen Sie dann "INITIIDS" aus. CPP aus der Dateiliste für jede Konfiguration.

  4. Klicken Sie auf die Registerkarte "C++", klicken Sie auf die Kategorie "Vorkompilierte Kopfzeilen", und wählen Sie das Optionsfeld "Nicht mit vorkompilierten Kopfzeilen" aus. Klicken Sie auf "OK", um das Dialogfeld "Einstellungen erstellen" zu schließen.

Angeben des richtigen Objektklassennamens in der Typbibliothek

Die mit Visual C++ ausgelieferten Assistenten verwenden den Implementierungsklassennamen fälschlicherweise, um die Coclass in der ODL-Datei des Servers für OLE-Creatable-Klassen anzugeben. Dies funktioniert zwar, aber der Name der Implementierungsklasse ist wahrscheinlich nicht der Klassenname, den Benutzer Ihres Objekts verwenden sollen. Um den richtigen Namen anzugeben, öffnen Sie die ODL-Datei, suchen Sie nach jeder Coclass-Anweisung, und ersetzen Sie den Implementierungsklassennamen durch den richtigen externen Namen.

Beachten Sie, dass die Variablennamen von CLSIDs in der vom MkTypLib generierten Headerdatei entsprechend geändert werden, wenn die Coclass-Anweisung geändert wird. Sie müssen Ihren Code aktualisieren, um die neuen Variablennamen zu verwenden.

Behandeln von Ausnahmen und automatisierungsfehlerschnittstellen

Die Methoden und Eigenschaftenaccessorfunktionen Ihres Automatisierungsobjekts können Ausnahmen auslösen. In diesem Fall sollten Sie sie in der Implementierung der dualen Schnittstelle behandeln und Informationen zur Ausnahme über die FEHLERbehandlungsschnittstelle IErrorInfoder OLE-Automatisierung an den Controller übergeben. Diese Schnittstelle bietet detaillierte kontextbezogene Fehlerinformationen sowohl über VTBL-Schnittstellen als auch IDispatch über VTBL-Schnittstellen. Um anzugeben, dass ein Fehlerhandler verfügbar ist, sollten Sie die ISupportErrorInfo Schnittstelle implementieren.

Um den Fehlerbehandlungsmechanismus zu veranschaulichen, gehen Sie davon aus, dass die von ClassWizard generierten Funktionen zum Implementieren der Standardverteilerunterstützung Ausnahmen auslösen. Die Implementierung von MFC erfasst IDispatch::Invoke diese Ausnahmen in der Regel und konvertiert sie in eine EXCEPTINFO-Struktur, die über den Invoke Aufruf zurückgegeben wird. Wenn jedoch die VTBL-Schnittstelle verwendet wird, sind Sie dafür verantwortlich, die Ausnahmen selbst abzufangen. Beispiel für den Schutz Ihrer Dual-Interface-Methoden:

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 übernimmt die Rückgabe des richtigen Fehlercodes, wenn eine Ausnahme auftritt. CATCH_ALL_DUAL wandelt eine MFC-Ausnahme mithilfe der Schnittstelle in Fehlerbehandlungsinformationen der ICreateErrorInfo OLE-Automatisierung um. (Ein Beispielmakro CATCH_ALL_DUAL befindet sich in der Datei MFCDUAL. H im ACDUAL-Beispiel . Die Funktion, die zum Behandeln von Ausnahmen aufgerufen wird, DualHandleExceptionbefindet sich in der Datei MFCDUAL. CPP.) CATCH_ALL_DUAL bestimmt den fehlercode, der basierend auf dem aufgetretenen Ausnahmetyp zurückgegeben werden soll:

  • COleDispatchException - In diesem Fall HRESULT wird mithilfe des folgenden Codes erstellt:

    hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, (e->m_wCode + 0x200));
    

    Dadurch wird eine HRESULT spezifische Schnittstelle erstellt, die die Ausnahme verursacht hat. Der Fehlercode wird durch 0x200 versetzt, um Konflikte mit systemdefinierten HRESULTS für Standard-OLE-Schnittstellen zu vermeiden.

  • CMemoryException - In diesem Fall E_OUTOFMEMORY wird zurückgegeben.

  • Jede andere Ausnahme - in diesem Fall E_UNEXPECTED wird zurückgegeben.

Um anzugeben, dass der OLE-Automatisierungsfehlerhandler verwendet wird, sollten Sie auch die ISupportErrorInfo Schnittstelle implementieren.

Fügen Sie zunächst Ihrer Automatisierungsklassendefinition Code hinzu, um sie anzuzeigen ISupportErrorInfo.

Fügen Sie zweitens Code zur Schnittstellenzuordnung Ihrer Automatisierungsklasse hinzu, um die ISupportErrorInfo Implementierungsklasse dem Mechanismus von QueryInterface MFC zuzuordnen. Die INTERFACE_PART Anweisung entspricht der klasse, die für ISupportErrorInfo.

Implementieren Sie schließlich die klasse, die für die Unterstützung ISupportErrorInfodefiniert ist.

(Die ACDUAL-Beispiel enthält drei Makros, die ihnen dabei helfen sollen, diese drei Schritte, DECLARE_DUAL_ERRORINFOund DUAL_ERRORINFO_PARTIMPLEMENT_DUAL_ERRORINFOalle in MFCDUAL.H enthaltenen Schritte auszuführen.

Im folgenden Beispiel wird eine Klasse implementiert, die für die Unterstützung ISupportErrorInfodefiniert ist. CAutoClickDoc ist der Name Ihrer Automatisierungsklasse und IID_IDualAClick ist der IID für die Schnittstelle, die die Quelle von Fehlern ist, die über das OLE-Automatisierungsfehlerobjekt gemeldet werden:

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

Siehe auch

Technische Hinweise – nach Nummern geordnet
Technische Hinweise – nach Kategorien geordnet