TN065: Suporte de Interface dupla para servidores de automação OLE

ObservaçãoObservação

A seguinte nota técnica não foi atualizada desde que foi incluída pela primeira vez na documentação online.Como resultado, alguns procedimentos e tópicos podem estar desatualizado ou incorreto.As informações mais recentes, é recomendável que você procure o tópico de interesse no índice de documentação on-line.

Esta nota descreve como adicionar suporte de interface dupla para um aplicativo de servidor com base em MFC automação de OLE.O ACDUAL exemplo ilustra o suporte de interface dupla e o código de exemplo nesta nota é tirado de ACDUAL.As macros descritas nesta nota, como DECLARE_DUAL_ERRORINFO, DUAL_ERRORINFO_PART, e IMPLEMENT_DUAL_ERRORINFO, fazem parte do exemplo de ACDUAL e pode ser encontrado em MFCDUAL.H.

Interfaces duplas

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

  • Ligação pode ocorrer em tempo de compilação através da interface VTBL ou em tempo de execução por meio de IDispatch.

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

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

  • Interface VTBL é mais fácil chamar de C++.

  • Interfaces duas são necessários para compatibilidade com os recursos de suporte do objeto Visual Basic.

Adicionando suporte de Interface dupla para uma classe de CCmdTarget

Uma interface dupla é realmente apenas uma interface personalizada derivada de IDispatch.A maneira mais simples para implementar suporte de interface dupla em um CCmdTarget-baseado em classe é implementar primeiro despacho normal de interface em sua classe usando o MFC e ClassWizard, em seguida, adicionar a interface personalizada posteriormente.Na maior parte do tempo, sua implementação de interface personalizada simplesmente delega para o MFC IDispatch implementação.

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

Uma instrução de interface para uma interface dupla deve ter a OLEAUTOMATION e 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
  {
  };

Uma vez que a declaração de interface no lugar, começar a adicionar entradas para os métodos e propriedades.Para interfaces duplas, você precisa reorganizar as listas de parâmetro para que seus métodos e funções do acessador de propriedade na interface dupla retornam um HRESULT e passar valores de retorno como parâmetros com os atributos [retval,out].Lembre-se de que propriedades, você precisará adicionar uma leitura (propget) e write (propput) função com a mesma identificação de acesso.Por exemplo:

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

Depois de seus métodos e propriedades são definidas, você precisará adicionar uma referência para a declaração de interface na instrução coclass.Por exemplo:

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

Depois que o arquivo ODL foi atualizado, usar mecanismo de mapa de interface do MFC para definir 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 de INTERFACE_PART bloco para cada entrada na declaração de interface da ODL mais entradas para uma interface dispatch.Cada entrada ODL com o propput atributo precisa uma função chamada put_propertyname.Cada entrada com o propget atributo precisa uma função chamada get_propertyname.

Para definir uma classe de implementação para a interface dupla, adicionar um DUAL_INTERFACE_PART bloco de sua definição de classe do 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)

Para se conectar a interface dupla a do MFC QueryInterface mecanismo, adicionar um INTERFACE_PART entrada de mapa 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ê precisará preencher a implementação da interface.Na maior parte do tempo, você poderá delegar a MFC existente 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 do acessador de propriedade do objeto, você precisa preencher a implementação.Funções de método e propriedade geralmente podem delegar para os métodos gerados usando ClassWizard.No entanto, se você definir propriedades para acessar variáveis diretamente, você precisa escrever o código para get/put 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;
}

Passar ponteiros de Interface dupla

Passar o ponteiro de interface dupla não é simples, especialmente se você precisar chamar CCmdTarget::FromIDispatch.FromIDispatchfunciona somente em do MFC IDispatch ponteiros.É uma maneira de contornar essa consulta para o original IDispatch conjunto ponteiro MFC para cima 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 através do método de interface dupla, talvez você precise converter o 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;
}

Registrar biblioteca de tipos do aplicativo

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

Registrar biblioteca de tipos do aplicativo sempre que executar o aplicativo autônomo:

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

  • Em seu aplicativo InitInstance funcionar, localize a chamada para COleObjectFactory::UpdateRegistryAll.Após essa chamada, adicionar uma chamada para AfxOleRegisterTypeLib, especificando o ID da biblioteca correspondente à sua biblioteca de tipo, juntamente com o nome da sua biblioteca de tipo:

    // 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 alterações de biblioteca de tipo

Para modificar configurações de construção de um projeto para que um arquivo de cabeçalho que contém UUID definições é gerado pelo compatível sempre que é reconstruída a biblioteca de tipos:

  1. Sobre o Construir menu, clique em configuraçõese selecione o arquivo ODL da lista de arquivos para cada configuração.

  2. Clique o OLE tipos guia e especificar um nome de arquivo de cabeçalho de saída campo filename.Use um nome de arquivo não está sendo usado por seu projeto, pois compatível irá substituir qualquer arquivo existente.Clique em OK para fechar o Configurações de compilação caixa de diálogo.

Para adicionar o UUID definições do arquivo de cabeçalho gerado compatível ao seu projeto:

  1. Incluir gerado pelo compatível inclui o arquivo de cabeçalho padrão do seu arquivo de cabeçalho STDAFX.H.

  2. Crie um novo arquivo, INITIIDS.CPP e adicioná-lo ao seu projeto.Nesse arquivo, inclua o seu arquivo de cabeçalho gerado compatível 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 Build menu, clique em configuraçõese selecione INITIIDS.CPP da lista de arquivos para cada configuração.

  4. Clique o C++ , clique em categoria Precompiled Headerse selecione o não usando cabeçalhos pré-compilados botão de rádio.Clique em OK para fechar a Configurações de compilação caixa de diálogo.

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

Os assistentes fornecidos com Visual C++ incorretamente usam o nome da classe de implementação para especificar o coclass no arquivo ODL do servidor OLE criado classes.Enquanto isso funcionar, o nome da classe de implementação é provavelmente não deseja que os usuários do seu objeto para usar o nome da classe.Para especificar o nome correto, abra o arquivo ODL, localize cada instrução coclass e substituir o nome da classe de implementação com o nome externo correto.

Observe que quando a instrução coclass é alterado, os nomes de variáveis de CLSIDs no arquivo de cabeçalho gerado compatível será alterada de acordo.Você precisará atualizar seu código para usar os novos nomes de variáveis.

Tratamento de exceções e Interfaces de erro de automação

Métodos e funções do acessador de propriedade do objeto de automação 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 ao controlador através da interface de manipulação de erros de automação OLE, IErrorInfo.Essa interface fornece informações de erro detalhadas e contextual tanto por meio IDispatch e interfaces VTBL.Para indicar que um manipulador de erro está disponível, você deve implementar a ISupportErrorInfo 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 que é retornada por meio de Invoke chamar.No entanto, quando interface VTBL é usado, você é responsável por capturar exceções sozinho.Como um exemplo da proteção de 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_DUALse encarrega de retornar o código de erro correta quando ocorre uma exceção.CATCH_ALL_DUALConverte uma exceção 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 exemplo.A função chama para lidar com exceções, DualHandleException, no arquivo MFCDUAL.CPP). CATCH_ALL_DUAL determina o código de erro para retornar com o tipo de exceção ocorreu:

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

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

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

  • CMemoryException – nesse caso, E_OUTOFMEMORY é retornado.

  • Outra exceção – nesse caso, E_UNEXPECTED é retornado.

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

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

Segundo, adicione código para o mapa de interface da classe de automação para associar o ISupportErrorInfo classe de implementação do MFC QueryInterface mecanismo.O INTERFACE_PART instrução corresponde a classe definida para ISupportErrorInfo.

Finalmente, implementar a classe definida para dar suporte a ISupportErrorInfo.

(O ACDUAL exemplo contém três macros para ajudar a fazer estas três etapas, DECLARE_DUAL_ERRORINFO, DUAL_ERRORINFO_PART, e IMPLEMENT_DUAL_ERRORINFO, todas contidas no MFCDUAL.H.)

O exemplo a seguir implementa uma classe definida para dar suporte a ISupportErrorInfo.CAutoClickDocé o nome da sua classe de automação e IID_IDualAClick é o IID para a interface que é a fonte de 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