Sdílet prostřednictvím


TN065: Podpora duálního rozhraní u automatizačních serverů OLE

[!POZNÁMKA]

Následující technická poznámka nebyla aktualizována, protože byla poprvé zahrnuta v dokumentaci online.V důsledku toho některé postupy a témata mohou být nesprávné nebo zastaralé.Pro nejnovější informace je vhodné vyhledat téma zájmu v dokumentaci online index.

Tato poznámka popisuje, jak přidat podporu dual rozhraní aplikace založené na knihovně MFC OLE Automation server.ACDUAL sample ukazuje, podpora dual rozhraní a příklady kódu v této poznámce je převzat z ACDUAL.Makra popsané v této poznámce, jako DECLARE_DUAL_ERRORINFO, DUAL_ERRORINFO_PART, a IMPLEMENT_DUAL_ERRORINFO, jsou součástí ACDUAL vzorku a lze nalézt v MFCDUAL.H.

Duální rozhraní

Ačkoli automatizace OLE umožňuje implementovat IDispatch rozhraní, rozhraní VTBL nebo duální rozhraní (což zahrnuje i), společnost Microsoft důrazně doporučuje implementovat duální rozhraní pro všechny vystavené objekty automatizace OLE.Duální rozhraní má značné výhody nad IDispatch-pouze nebo jen VTBL rozhraní:

  • Vazby mohou uskutečnit v době kompilace prostřednictvím VTBL rozhraní nebo za běhu pomocí IDispatch.

  • Zlepšení výkonu mohou využít řadiče automatizace OLE, které můžete použít rozhraní VTBL.

  • Existující řadiče automatizace OLE, které používají IDispatch rozhraní bude pokračovat v práci.

  • VTBL rozhraní je snazší volání z jazyka C++.

  • Duální rozhraní jsou požadovány pro kompatibilitu s Visual Basic objekt podporují funkce.

Přidání podpory duální rozhraní na základě třídy CCmdTarget třídu

Duální rozhraní je pouze vlastní rozhraní, odvozené z IDispatch.Nejsnazší způsob implementace podpory duální rozhraní v CCmdTarget-na základě třídy je první implementace rozhraní ve třídě pomocí MFC a ClassWizard normální odeslání a pak později přidat vlastní rozhraní.Z větší části bude implementace vlastního rozhraní jednoduše přenést zpět do knihovny MFC IDispatch provedení.

Nejprve upravte soubor Distanční definovat duální rozhraní pro objekty serveru.Chcete-li definovat duální rozhraní, musíte použít příkaz rozhraní místo DISPINTERFACE prohlášení, že průvodci Visual C++ generovat.Místo odebrání existující DISPINTERFACE prohlášení, přidejte příkaz nové rozhraní.Udržela DISPINTERFACE formuláře, můžete pokračovat pomocí ClassWizard přidat vlastnosti a metody do objektu, ale je nutné přidat odpovídající vlastnosti a metody na váš příkaz rozhraní.

Příkaz rozhraní pro duální rozhraní musí mít OLEAUTOMATION a dva atributy a rozhraní musí být odvozen od IDispatch.Lze použít cílem vzorek k vytvoření IID pro duální rozhraní:

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

Po příkazu rozhraní na místě začněte přidávat položky pro metody a vlastnosti.Pro duální rozhraní, je nutné uspořádat seznamy parametrů metody a vlastnosti přístupové funkce duální rozhraní, vrátí HRESULT a jejich vrácené hodnoty předat jako parametry s atributy [retval,out].Mějte na paměti, že u vlastností, je třeba přidat pro čtení (propget) a write (propput) přístup k funkci se stejným id.Příklad:

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

Po definování metod a vlastností, je třeba přidat odkaz na výpis rozhraní v příkazu coclass.Příklad:

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

Jakmile Distanční soubor byl aktualizován, použít mechanismus mapování rozhraní knihovny MFC definovat třídy implementace pro duální rozhraní ve třídě objektu a provést odpovídající položky v knihovně MFC's QueryInterface mechanismus.Potřebujete jednu položku v INTERFACE_PART blok pro jednotlivé položky ve výkazu rozhraní Distanční plus položky pro odesílající rozhraní.Každá položka Distanční s značkou propput atribut vyžaduje funkci s názvem put_propertyname.Každá položka se propget atribut vyžaduje funkci s názvem get_propertyname.

Chcete-li definovat třídu implementace pro duální rozhraní, přidejte DUAL_INTERFACE_PART blok do vaší definice třídy objektu.Příklad:

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)

Duální rozhraní připojit ke knihovně MFC's QueryInterface mechanismus, přidat INTERFACE_PART položku pro mapování rozhraní:

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

Dále je třeba vyplnit v implementaci rozhraní.Z větší části se budou moci delegovat na existující knihovny MFC IDispatch provedení.Příklad:

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

Pro váš objekt metody a vlastnosti přístupové funkce je nutné vyplnit v provedení.Vaše metody a vlastnosti funkcí můžete delegovat obecně zpět k metodám, které jsou generovány pomocí ClassWizard.Však pokud vlastnosti můžete nastavit přímý přístup k proměnné, musíte napsat kód get nebo PUT hodnoty do proměnné.Příklad:

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

Předání ukazatele rozhraní Dual

Předejte ukazatel rozhraní dual není jednoduché, zejména v případě, že je třeba volat CCmdTarget::FromIDispatch.FromIDispatchfunguje pouze v MFC IDispatch ukazatele.Jedním ze způsobů, jak tento problém vyřešit je dotaz pro původní IDispatch ukazatel set up knihovnou MFC a předat tento ukazatel funkce, které jej potřebují.Příklad:

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

Před předáním ukazatel zpět prostřednictvím metody duální rozhraní, můžete potřebovat převést z knihovny MFC IDispatch ukazatel na ukazatel rozhraní dual.Příklad:

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

Registrace knihovny typů aplikace

AppWizard negeneruje kód zaregistrovat knihovnu typů aplikace server OLE automatizace v systému.Pokud existují další způsoby, jak zaregistrovat knihovnu typů, je vhodné používat aplikaci registrovat knihovnu typu během aktualizace jeho informace o typu OLE, při každém spuštění samostatné aplikace.

Registrace knihovny typů aplikace při každém spuštění aplikace samostatně:

  • Patří AFXCTL.H ve své standardní zahrnuje záhlaví souboru STDAFX.H pro přístup k definici AfxOleRegisterTypeLib funkce.

  • Ve vaší aplikaci InitInstance funkci, vyhledejte volání COleObjectFactory::UpdateRegistryAll.Toto volání přidejte volání AfxOleRegisterTypeLib, zadání ID KNIHOVNY odpovídající typ knihovny, spolu s názvem knihovna typů:

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

Změna nastavení sestavení projektu k tomuto typu změny knihovny

Chcete-li upravit nastavení sestavení do projektu tak, aby soubor záhlaví obsahující UUID definice je generován MkTypLib vždy, když je znovu vytvořit knihovny typů:

  1. Na sestavení nabídky, klepněte na tlačítko Nastavenía pak vyberte Distanční soubor ze seznamu souborů pro každou konfiguraci.

  2. Klepněte Typy OLE kartu a zadejte název souboru v výstup záhlaví pole název souboru.Použijte název souboru, který již není používán projektu, protože MkTypLib přepíše všechny existující soubory.Klepněte na tlačítko OK zavřete Nastavení sestavení dialogové okno.

Chcete-li přidat UUID definice z generované MkTypLib záhlaví souboru do projektu:

  1. Zahrnout generovaná MkTypLib záhlaví souboru ve své standardní obsahuje záhlaví souboru STDAFX.H.

  2. Vytvořte nový soubor INITIIDS.CPP a přidat do projektu.V tomto souboru zahrňte soubor generovaný MkTypLib záhlaví po včetně OLE2.H a 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. Na sestavení nabídky, klepněte na tlačítko Nastavenía pak vyberte INITIIDS.CPP ze seznamu souborů pro jednotlivé konfigurace.

  4. Klepněte C++ karta, klepněte na kategorii Předkompilované hlavičkya vyberte Nepoužívat předkompilované hlavičky přepínací tlačítko.Klepněte na tlačítko OK zavřete Nastavení sestavení dialogové okno.

Určení názvu třídy správný objekt v knihovně typů

Průvodců nesprávně dodáno s Visual C++ pomocí názvu třídy implementace určuje coclass v souboru Distanční serveru OLE vytvořitelné tříd.Když to bude fungovat, název třídy implementace není pravděpodobně chcete uživatelům vašeho objektu použijte název třídy.Zadejte správný název, otevřete soubor Distanční, každý coclass příkaz Najít a nahradit správným názvem externí název třídy implementace.

Všimněte si, že při změně příkazu coclass, názvy proměnných z CLSIDs generované MkTypLib záhlaví souboru se odpovídajícím způsobem změní.Je třeba aktualizovat váš kód k použití nové názvy proměnných.

Zpracování výjimek a chyb rozhraní automatizace

Metody a vlastnosti přístupové funkce objektu automatizace může vyvolat výjimky.Pokud by tedy je zpracovávat ve vaší implementaci rozhraní dual a předat informace o výjimce zpět na zařízení prostřednictvím rozhraní automatizace OLE zpracování chyb IErrorInfo.Toto rozhraní poskytuje kontextové, podrobné chybové informace prostřednictvím obou IDispatch a rozhraní VTBL.K označení, že obslužná rutina chyb je k dispozici, měli byste ISupportErrorInfo rozhraní.

Pro ilustraci mechanismus zpracování chyb, se předpokládá, že vyvolávají výjimky, ClassWizard vygenerován funkce, které slouží k implementaci podpory pro standardní odesílání.Implementace MFC volání metody IDispatch::Invoke obvykle zachytí tyto výjimky a převádí na strukturu EXCEPTINFO, která je vrácena až Invoke volání.Však při použití rozhraní VTBL zodpovídáte za zachycení výjimek sami.Jako příklad chrání vaše rozhraní dvou metod:

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_DUALpečuje o vrácení správné chybový kód, pokud dojde k výjimce.CATCH_ALL_DUALPřevede automatizace OLE chyba zpracování informací pomocí MFC výjimka ICreateErrorInfo rozhraní. (Příklad CATCH_ALL_DUAL je makro v souboru MFCDUAL.H v ACDUAL vzorku.Funkce volá zpracování výjimek, DualHandleException, je v souboru MFCDUAL.CPP). CATCH_ALL_DUAL Určuje kód chyby vrátit podle typu výjimky, došlo k chybě:

  • COleDispatchException – v tomto případě HRESULT je vytvořen pomocí následujícího kódu:

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

    Tím se vytvoří HRESULT specifické pro rozhraní, která způsobila výjimku.Kód chyby je posunut o 0x200, aby se zabránilo případné konflikty s definované systémem HRESULTs pro standardní rozhraní OLE.

  • CMemoryException – v tomto případě E_OUTOFMEMORY je vrácena.

  • Jiná výjimka – v tomto případě E_UNEXPECTED je vrácena.

Chcete-li určit, zda je použita obslužná rutina Chyba automatizace OLE, byste měli implementovat ISupportErrorInfo rozhraní.

Nejprve přidejte kód do vaší definice třídy automatizace zobrazení podporuje ISupportErrorInfo.

Za druhé, přidat kód do mapy rozhraní automatizační třída přidružení ISupportErrorInfo implementace třídy s MFC QueryInterface mechanismus.INTERFACE_PART Prohlášení odpovídá třídy definované pro ISupportErrorInfo.

Nakonec implementovat třídu definovanou pro podporu ISupportErrorInfo.

( ACDUAL vzorek obsahuje tři makra můžete provést tyto tři kroky, DECLARE_DUAL_ERRORINFO, DUAL_ERRORINFO_PART, a IMPLEMENT_DUAL_ERRORINFO, všech obsažených v MFCDUAL.H.)

Následující příklad implementuje třídu definovanou pro podporu ISupportErrorInfo.CAutoClickDocje název vaší třídy automatizace a IID_IDualAClick je IID pro rozhraní, která je zdrojem chyby hlášené prostřednictvím objektu Chyba automatizace 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; 
}

Viz také

Další zdroje

Technické poznámky podle čísel

Technické poznámky podle kategorií