Condividi tramite


TN065: Supporto per l'interfaccia duale per i server di automazione OLE

[!NOTA]

La seguente nota tecnica non è stata aggiornata dalla prima volta che viene inclusa nella documentazione online.Di conseguenza, alcune procedure e argomenti potrebbero non essere aggiornati o errati.Per le informazioni più recenti, è consigliabile cercare l'argomento di interesseindice della documentazione online.

Questa nota viene illustrato come aggiungere il supporto per le interfacce duali a un'applicazione server di automazione basata su MFC OLE.L'esempio di Nell'esempio ACDUAL viene illustrato il supporto per l'interfaccia duale e il codice di esempio in questa nota viene ricavato dall'esempio ACDUAL.Le macro descritte in questa nota, come DECLARE_DUAL_ERRORINFO, DUAL_ERRORINFO_PARTe IMPLEMENT_DUAL_ERRORINFO, fa parte dell'esempio ACDUAL e sono disponibili in MFCDUAL.H.

Interfacce duali

Sebbene automazione OLE consenta di implementare un'interfaccia di IDispatch , un'interfaccia di VTBL, o un'interfaccia duale (che include sia), Si consiglia consigliabile si implementano le interfacce duali per tutti gli oggetti esposti automazione OLE.Le interfacce duali presentano vantaggi significativi rispetto alle interfacce solo o solo VTBL di IDispatch:

  • L'associazione può aver luogo in fase di compilazione tramite l'interfaccia di VTBL, o in fase di esecuzione con IDispatch.

  • Controller di automazione OLE che possono utilizzare l'interfaccia di VTBL possono trarre vantaggio dalle prestazioni migliori.

  • i controller esistenti di automazione OLE che utilizzano l'interfaccia di IDispatch continueranno a funzionare.

  • L'interfaccia di VTBL è più semplice chiamare da C++.

  • Le interfacce duali sono necessarie per la compatibilità con le funzionalità di supporto dell'oggetto Visual Basic.

Supporto per le interfacce duali di aggiunta su una classe Basata CCmdTarget

Un'interfaccia duale è in realtà un'interfaccia personalizzata derivata da IDispatch.Il modo più semplice per implementare il supporto per l'interfaccia duale in una classe di base CCmdTargetconsiste nel implementa l'interfaccia dispatch normale nella classe utilizzando MFC e di ClassWizard, quindi aggiunge un'interfaccia più avanti.In genere, l'implementazione di interfaccia delegherà semplicemente nuovamente all'implementazione MFC IDispatch .

Innanzitutto, modificare il file ODL per il server per definire le interfacce duali per gli oggetti.Per definire un'interfaccia duale, è necessario utilizzare un'istruzione dell'interfaccia, anziché l'istruzione di DISPINTERFACE che le procedure guidate di Visual C++ generano.Anziché rimuovere l'istruzione esistente di DISPINTERFACE , aggiungere una nuova istruzione dell'interfaccia.Tenendo premuto il form di DISPINTERFACE , è possibile continuare a utilizzare ClassWizard per aggiungere proprietà e metodi all'oggetto, ma è necessario aggiungere proprietà e metodi equivalenti all'istruzione interface.

Un'istruzione dell'interfaccia per un'interfaccia duale deve disporre degli attributi di DUAL e di OLEAUTOMATION e l'interfaccia deve essere derivata da IDispatch.È possibile utilizzare l'esempio di FINESTRE per creare IID per l'interfaccia duale:

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

Dopo aver l'istruzione dell'interfaccia, voci iniziale di aggiunta di metodi e proprietà.Per le interfacce duali, è necessario ridisporre gli elenchi di parametri in modo che i metodi e funzioni di accesso della proprietà nell'interfaccia duale restituiscano HRESULT e passeranno i valori restituiti come parametri con attributi [retval,out].Tenere presente che per le proprietà, sarà necessario aggiungere sia lettura (propget) che scrivere (propput) la funzione di accesso con lo stesso ID.Di seguito è riportato un esempio:

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

Dopo i metodi e proprietà viene definito, è necessario aggiungere un riferimento all'istruzione dell'interfaccia nell'istruzione della coclasse.Di seguito è riportato un esempio:

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

Dopo che il file ODL è stato aggiornato, utilizzare il meccanismo di mapping dell'interfaccia di MFC per definire una classe di implementazione per l'interfaccia duale nella classe di oggetto ed eseguire le voci corrispondenti nel meccanismo di QueryInterface di MFC.È necessaria una voce nel blocco di INTERFACE_PART per ogni voce nell'istruzione dell'interfaccia di ODL, più voci per un'interfaccia dispatch.Ogni voce ODL con l'attributo di propput necessita di una funzione denominata put_propertyname.Ogni voce con l'attributo di propget necessita di una funzione denominata get_propertyname.

Per definire una classe di implementazione per l'interfaccia duale, aggiungere un blocco di DUAL_INTERFACE_PART alla definizione di classe di oggetto.Di seguito è riportato un esempio:

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)

Per connettere l'interfaccia duale al meccanismo di QueryInterface di MFC, aggiungere una voce di INTERFACE_PART il mapping dell'interfaccia:

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

A questo punto, è necessario inserire l'implementazione dell'interfaccia.In genere, sarà possibile delegare l'implementazione esistente di MFC IDispatch .Di seguito è riportato un esempio:

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

Per i metodi dell'oggetto e le funzioni di accesso della proprietà, è necessario inserire l'implementazione.Il metodo e funzioni di proprietà possono delegare in genere ai metodi generati utilizzando ClassWizard.Tuttavia, se si impostano le proprietà per accedere direttamente alle variabili, è necessario scrivere codice per ottenere/inserire il valore della variabile.Di seguito è riportato un esempio:

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

Passaggio di puntatori a interfaccia duale

Passare il puntatore a interfaccia duale non è chiaro, specialmente se è necessario chiamare CCmdTarget::FromIDispatch.Funzionamento diFromIDispatch solo sui puntatori di IDispatch di MFC.Un modo per risolvere il problema consiste di eseguire una query per l'impostazione del puntatore di IDispatch di originale da MFC e di passare tale puntatore a funzioni che lo richiedono.Di seguito è riportato un esempio:

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

Prima di passare un puntatore all'indietro con il metodo interfaccia duale, potrebbe essere necessario convertirlo del puntatore MFC IDispatch al puntatore a interfaccia duale.Di seguito è riportato un esempio:

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

Registrare la libreria dei tipi dell'applicazione

AppWizard non genera codice per registrare la libreria dei tipi di un'applicazione server di automazione OLE con il sistema.Mentre esistono altri modi per registrare la libreria dei tipi, è utile disporre del log dell'applicazione la libreria dei tipi quando viene aggiornato le informazioni sul tipo di OLE, ovvero, ogni volta che l'applicazione viene eseguito autonomamente.

Per registrare la libreria dei tipi dell'applicazione ogni volta che l'applicazione viene supporto eseguito da solo:

  • Importare AFXCTL.H nello standard include il file di intestazione, STDAFX.H, per accedere alla definizione della funzione di AfxOleRegisterTypeLib .

  • Nella funzione di InitInstance dell'applicazione, individuare la chiamata a COleObjectFactory::UpdateRegistryAll.Attenendosi a questa chiamata, aggiungere una chiamata a AfxOleRegisterTypeLib, specificando LIBID che corrisponde alla libreria dei tipi, con il nome della libreria dei tipi:

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

Modificare le impostazioni di compilazione del progetto in base alle modifiche della libreria dei tipi

Per modificare le impostazioni di compilazione di un progetto in modo che un file di intestazione contenente le definizioni di UUID venga generato da MkTypLib ogni volta che la libreria dei tipi viene ricompilata:

  1. Scegliere dal menu Compilazione , fare clic su Impostazioniquindi selezionare il file ODL dall'elenco dei file per ogni configurazione.

  2. Fare clic sulla scheda di OLE Types e specificare un nome file nel campo del nome file di Output header .Utilizzare un nome file che non sia già utilizzato dal progetto, perché MkTypLib sovrascriverà il file esistente.Scegliere OK per chiudere la finestra di dialogo di Build Settings .

Per aggiungere le definizioni di UUID dal file di intestazione MkTypLib-generato al progetto:

  1. Includere il file di intestazione MkTypLib-generato nello standard include il file di intestazione, STDAFX.H.

  2. Creare un nuovo file, INITIIDS.CPP e aggiungerlo al progetto.In questo file, includere dopo il file di intestazione MkTypLib-generato inclusi OLE2.H e 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. Scegliere dal menu Compilazione , fare clic su Impostazioniquindi selezionare INITIIDS.CPP dall'elenco dei file per ogni configurazione.

  4. Fare clic sulla scheda di C++ , fare clic sulla categoria Intestazioni precompilatee selezionare il pulsante di opzione di Not using precompiled headers .Scegliere OK per chiudere la finestra di dialogo di Build Settings .

Specificare il nome di classe corretto dell'oggetto nella libreria dei tipi

Le procedure guidate disponibili in Visual C++ utilizzano in modo errato il nome della classe di implementazione per specificare la coclasse nel file ODL del server per le classi OLE-generabili.Sebbene questo verrà eseguito, il nome della classe di implementazione non è probabilmente il nome della classe desiderata gli utenti dell'oggetto da utilizzare.Per specificare il nome corretto, aprire il file ODL, individuare ogni istruzione della coclasse e sostituire il nome della classe di implementazione con il nome esterno corretto.

Si noti che quando l'istruzione della coclasse viene modificata, i nomi delle variabili di CLSIDoggetti nel file di intestazione MkTypLib-generato cambiano di conseguenza.Sarà necessario aggiornare il codice per utilizzare i nuovi nomi di variabile.

Gestione delle eccezioni e le interfacce degli errori di automazione

I metodi dell'oggetto ActiveX e le funzioni di accesso della proprietà possono generare eccezioni.In questo caso, è necessario gestirle nell'implementazione interfaccia duale e comunicare le informazioni sull'eccezione al controller tramite l'interfaccia di gestione degli errori di automazione OLE, IErrorInfo.Questa interfaccia è previsto informazioni dettagliate sugli errori e contestuali tramite sia IDispatch che le interfacce di VTBL.Per indicare che un gestore errori è disponibile, è necessario implementare l'interfaccia di ISupportErrorInfo .

Per illustrare il meccanismo di gestione degli errori, si supponga che le funzioni ClassWizard-generate utilizzate per implementare le eccezioni standard throw di supporto di invio.L'implementazione di MFC di IDispatch::Invoke in genere intercetta queste eccezioni e li converte in una struttura di EXCEPTINFO restituita dalla chiamata di Invoke .Tuttavia, quando l'interfaccia di VTBL viene utilizzata, è responsabile per intercettare eccezioni manualmente.Come esempio della sicurezza dei metodi interfaccia duale:

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 consente infatti di restituire il codice di errore corretto quando si verifica un'eccezione.CATCH_ALL_DUAL converte un'eccezione MFC nelle informazioni di gestione degli errori di automazione OLE tramite l'interfaccia di ICreateErrorInfo .(Una macro di CATCH_ALL_DUAL di esempio è il file MFCDUAL.H nell'esempio di Nell'esempio ACDUAL .La funzione che chiama per gestire le eccezioni, DualHandleException, si trova nel file MFCDUAL.CPP.) CATCH_ALL_DUAL determina il codice di errore da restituire in base al tipo di eccezione che si è verificato:

  • COleDispatchException (in questo caso, HRESULT viene costruito utilizzando il codice seguente:

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

    Verrà creata HRESULT specifico all'interfaccia che ha causato l'eccezione.Il codice di errore è offset da 0x200 è per evitare eventuali conflitti con HRESULTdefinito dal sistema oggetti per le interfacce standard OLE.

  • CMemoryException (in questo caso, E_OUTOFMEMORY viene restituito.

  • Qualsiasi altra eccezione (in questo caso, E_UNEXPECTED viene restituito.

Per indicare che il gestore degli errori di automazione OLE viene utilizzato, è necessario implementare anche l'interfaccia di ISupportErrorInfo .

Innanzitutto, aggiungere il codice alla definizione della classe di automazione per visualizzarlo che supporta ISupportErrorInfo.

In secondo luogo, aggiungere codice al mapping dell'interfaccia della classe di automazione per associare la classe di implementazione di ISupportErrorInfo con il meccanismo di QueryInterface di MFC.L'istruzione di INTERFACE_PART corrisponde alla classe definita per ISupportErrorInfo.

Infine, implementare una classe definita per supportare ISupportErrorInfo.

Nell'esempio di Nell'esempio ACDUAL contiene tre macro per consentire a questi tre passaggi, DECLARE_DUAL_ERRORINFO, DUAL_ERRORINFO_PARTe IMPLEMENT_DUAL_ERRORINFO, interamente contenuto in MFCDUAL.H).

Nell'esempio seguente viene implementata una classe definita per supportare ISupportErrorInfo.CAutoClickDoc è il nome della classe di automazione e IID_IDualAClick è IID per l'interfaccia che è l'origine degli errori indicati tramite l'oggetto gli errori di automazione 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; 
}

Vedere anche

Altre risorse

Note tecniche del numero

Note tecniche per categoria