TN065: Interface dual suporte para servidores de automação OLE

Observação:

A seguinte nota técnica não foi atualizada desde que foi incluída pela primeira vez na documentação online.sistema autônomo resultado, alguns procedimentos e tópicos podem estar desatualizado ou incorreto.Para obter informações mais recentes, é recomendável que você procurar o tópico de interesse no índice de documentação online.

Esta nota aborda como adicionar suporte dual interface a um aplicativo de servidor com base em MFC automação OLE.The ACDUAL exemplo ilustra dual interface suporte e o código de exemplo esta nota é obtido do ACDUAL.sistema autônomo macros descritas nesta anotação, sistema autônomo DECLARE_DUAL_ERRORINFO, DUAL_ERRORINFO_PART, e IMPLEMENT_DUAL_ERRORINFO, fazem parte do exemplo ACDUAL e podem ser encontradas no MFCDUAL.H.

Interfaces duplos

Embora a automação OLE permite que você implemente um IDispatch interface, uma interface VTBL ou uma interface dupla (que compreende ambos), Microsoft recomenda que você implementar duas interfaces para todos expostos objetos de automação OLE. Duas interfaces têm vantagens significativas em IDispatch-somente ou apenas VTBL interfaces:

  • Vinculação pode ocorrer em time de compilar por meio da interface VTBL ou em time de execução por meio de IDispatch.

  • Controladores de automação OLE que podem usar a interface VTBL podem se beneficiar de melhor desempenho.

  • Controladores de automação OLE existentes que usam o IDispatch interface continuarão a funcionar.

  • A interface VTBL é mais fácil chamar a partir do C++.

  • Duas interfaces são necessárias para compatibilidade com os recursos de suporte do objeto do Visual Basic.

Adicionando suporte à interface dupla em uma classe com CCmdTarget

Uma interface dupla é realmente apenas uma interface personalizada derivada de IDispatch. A maneira mais simples de implementar interface dual suporte em um CCmdTarget-baseado em classe é implementar primeiro despacho normal da interface em sua classe usando o MFC e ClassWizard e, em seguida, adicione a interface personalizada posteriormente. Em sua maioria, sua implementação de interface personalizada simplesmente delegará volta para o MFC IDispatch implementação.

Primeiro, modifique o arquivo ODL para seu servidor definir duas interfaces para os objetos.Para definir uma interface dupla, você deve usar uma demonstrativo de interface, em vez do DISPINTERFACE demonstrativo que geram os assistentes do Visual C++. Em vez de remover o existenteDISPINTERFACE demonstrativo, adicione uma demonstrativo de interface de novo. Mantendo o DISPINTERFACE formulário, você pode continuar a usar ClassWizard para adicionar propriedades e métodos ao seu objeto, mas você deve adicionar equivalentes de propriedades e métodos para sua demonstrativo de interface.

Uma demonstrativo de interface para uma interface dupla deve ter o OLEAUTOMATION and DUAL atributos e a interface devem ser derivado de IDispatch. Você pode usar o GUIDGEN exemplo para criar um IID para a interface dupla:

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

Depois da demonstrativo de interface no lugar, começar a adicionar entradas para os métodos e propriedades.Para interfaces duas, é necessário reorganizar listas de parâmetros para que seus métodos e propriedade acessador funções na interface dupla retornam um HRESULT e coloque o valores de retorno sistema autônomo parâmetros com sistema autônomo atributos [retval,out]. Lembre-se de que propriedades, você precisa adicionar uma leitura (propget) e gravar ()propput) acessar função com o mesmo id. Por exemplo:

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

Após os métodos e propriedades são definidas, você precisará adicionar uma referência para a demonstrativo de interface em sua demonstrativo coclass.Por exemplo:

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

Assim que seu arquivo ODL estiver atualizado, use o mecanismo de MAP de interface do MFC para definem uma classe de implementação para a interface dupla em sua classe de objeto e fazer as entradas correspondentes do MFC QueryInterface mecanismo. Você precisa de uma entrada no INTERFACE_PART bloco para cada entrada na demonstrativo de interface do ODL, mais as entradas para uma interface dispatch. Cada entrada ODL com o propput atributo precisa de uma função chamada put_propertyname. Cada entrada com o propget atributo precisa de uma função chamada get_propertyname.

Para definir uma classe de implementação para a sua interface dupla, adicione um DUAL_INTERFACE_PART Bloquear à sua definição de classe de objeto. Por exemplo:

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)

Conectar a interface dupla do MFC QueryInterface mecanismo, adicione um INTERFACE_PART entrada para o MAP de interface:

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

Em seguida, você precisa preencher a implementação da interface.Na maior parte, você poderá delegado para o MFC existentes IDispatch implementação. Por exemplo:

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

Para métodos e funções de assessor de propriedade do objeto, você precisa preencher a implementação.As funções de método e propriedade geralmente podem delegado para os métodos gerados usando ClassWizard.No entanto, se você conjunto propriedades para acesso variáveis diretamente, você precisa escrever o código para get/colocar o valor na variável.Por exemplo:

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

Ponteiros de interface dupla de passagem

Não é simples, especialmente se você precisar passar o ponteiro de interface dupla telefonar CCmdTarget::FromIDispatch. FromIDispatch funciona somente em do MFC IDispatch ponteiros. Uma maneira para contornar este problema é a consulta para o original IDispatch ponteiro configurado pelo MFC e passar esse ponteiro para funções que precisam dele. Por exemplo:

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

Antes de passar um ponteiro de volta através do método de interface dupla, talvez seja necessário convertê-la do MFC IDispatch ponteiro para o ponteiro de interface dupla. Por exemplo:

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

Registrando biblioteca de tipos do aplicativo

AppWizard não gera código para registrar a biblioteca de tipos de um aplicativo de servidor de automação OLE com o sistema.Existem outras maneiras de registrar a biblioteca de tipos, é conveniente para que o aplicativo registrar a biblioteca de tipos quando ele atualiza suas informações de tipo OLE, ou seja, sempre que o aplicativo é executado independente.

Para registrar a biblioteca de tipos de aplicativo sempre que o aplicativo é executado autônomo:

  • Incluir AFXCTL.H no seu padrão inclui o arquivo de cabeçalho, STDAFX.H para acessar a definição do AfxOleRegisterTypeLib função.

  • No InitInstance função, localizar a telefonar para COleObjectFactory::UpdateRegistryAll. Seguindo essa telefonar, adicione uma telefonar para AfxOleRegisterTypeLib, especificando o LIBID correspondente à sua biblioteca de tipos, juntamente com o nome da sua biblioteca de tipos:

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

Modificando configurações de compilação do projeto para acomodar as alterações de biblioteca de tipo

Para modificar um projeto compilação configurações para que um cabeçalho de arquivo contendo UUID definições é gerado pelo MkTypLib sempre que a biblioteca de tipos é reconstruída:

  1. Sobre o Compilação menu, clicar Configurações e, em seguida, selecionar o arquivo ODL na lista de arquivos para cada configuração.

  2. clicar no Tipos OLE guia e especifique um nome de arquivo no Cabeçalho de saídacampo de nome de arquivo .Use um nome de arquivo que já não está sendo usado por seu projeto, porque MkTypLib irá substituir qualquer arquivo existente.clicar OK para fechar o compilação Configurações caixa de diálogo.

Para adicionar o UUID definições do arquivo de cabeçalho gerado MkTypLib para seu projeto:

  1. Incluir o gerados MkTypLib arquivo de cabeçalho no seu padrão inclui o arquivo de cabeçalho, STDAFX.H.

  2. Criar um novo arquivo, INITIIDS.CPP e adicioná-la para seu projeto.Nesse arquivo, inclua seu arquivo de cabeçalho gerado MkTypLib após incluindo 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. Sobre o Compilação menu, clicar Configurações e INITIIDS.CPP, em seguida, selecionar na lista de arquivos para cada configuração.

  4. clicar no C++ , clicar categoria Cabeçalhos pré-compiladose selecionar o Não usando o pré-compilado cabeçalhos botão de opção.clicar OK para fechar o Criar configurações caixa de diálogo.

Especificando o nome de classe de objeto correto na biblioteca de tipos do

Os assistentes que acompanha o Visual C++ incorretamente usar o nome de classe de implementação para especificar o coclass no arquivo ODL do servidor OLE creatable classes.Embora isso funcione, o nome de classe de implementação provavelmente não é o nome de classe que você deseja que os usuários do seu objeto usar.Para especificar o nome correto, abra o arquivo ODL, localizar cada demonstrativo coclass e substitua o nome de classe de implementação com o nome externo correto.

Observe que, quando a demonstrativo coclass for alterada, o variável nomes de CLSID s no arquivo de cabeçalho gerado MkTypLib mudará adequadamente.Você precisará atualizar seu código para usar os novos nomes de variáveis.

Tratamento de exceções e as interfaces de erro de automação

Métodos e funções de assessor de propriedade do objeto Automation podem lançar exceções.Se assim, você deve tratá-los na sua implementação de interface dupla e passar informações sobre a exceção de volta para a controladora através de automação OLE erro - manipulação de interface IerroInfo.Essa interface fornece para informações sobre o erro detalhado e contextuais através de ambos os IDispatch e VTBL interfaces. Para indicar que um erro manipulador está disponível, você deve implementar o ISupporterroInfo interface.

Para ilustrar o mecanismo de tratamento de erros, suponha que as funções geradas ClassWizard usadas para implementar o suporte de despacho padrão lançam exceções.Implementação do MFC de IDispatch:: Invoke normalmente captura essas exceções e converte-os em uma estrutura EXCEPTINFO retornado por meio de Invoke Chame. No entanto, quando VTBL interface é usado, você é responsável por capturar exceções você mesmo.sistema autônomo um exemplo de proteger seus métodos de interface dupla:

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 se encarrega de retornar o código de erro correta quando ocorre uma exceção. CATCH_ALL_DUAL Converte uma exceção de MFC em informações de manipulação de erros de automação OLE usando o ICreateErrorInfo interface.(Um exemplo CATCH_ALL_DUAL macro está no arquivo MFCDUAL.H na ACDUAL amostra.A função chama para lidar com exceções, DualHandleExceptionno arquivo MFCDUAL.CPP.) CATCH_ALL_DUAL Determina o código de erro retornar com base no tipo de exceção ocorreu:

  • COleDispatchException – Nesse caso, HRESULT é construída usando o seguinte código:

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

    Isso cria um HRESULT específico à interface que causou a exceção. O código de erro é compensado pelo 0x200 para evitar quaisquer conflitos com definidas pelo sistema HRESULTs para o padrão OLE interfaces.

  • CMemoryException – Nesse caso, E_OUTOFMEMORY será retornado.

  • Qualquer Outros exceção – nesse caso, E_UNEXPECTED será retornado.

Para indicar que o manipulador de erro de automação OLE é usado, você também deve implementar o ISupportErrorInfo interface.

Primeiro, adicione código à sua definição de classe de automação para mostrar que ele oferece suporte a ISupportErrorInfo.

Em segundo lugar, adicione código à interface MAP da sua classe de automação para associar o ISupportErrorInfo classe de implementação de com QueryInterface mecanismo. The INTERFACE_PART demonstrativo coincide com a classe definida para ISupportErrorInfo.

Por fim, implementar a classe definida para oferecer suporte a ISupportErrorInfo.

(The ACDUAL exemplo contém três macros para ajudar a fazer estas três etapas, DECLARE_DUAL_ERRORINFO, DUAL_ERRORINFO_PART, e IMPLEMENT_DUAL_ERRORINFOtodas contidas em MFCDUAL.H.)

O exemplo a seguir implementa uma classe definida para oferecer suporte a ISupportErrorInfo.CAutoClickDoc é o nome da sua classe de automação e IID_IDualAClick é o IID para a interface que é a fonte dos erros relatados por meio do objeto de erro de automação 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; 
}

Consulte também

Outros recursos

Notas técnicas por número

Notas técnicas por categoria