TN038: Implementação IUnknown MFC/OLE

No coração do OLE 2 é o "OLE Component Object Model", ou COM OCM. define um padrão para objetos como que colaboraram se comunicar entre si. Isso inclui os detalhes do que um "objeto" aparência, incluindo como métodos são expedidos em um objeto. COM também define uma classe base, de que derivam todas as classes compatíveis COM. Esta classe base é IUnknown. Embora a interface de IUnknown é referida como uma classe C++, COM não é específico para qualquer um idioma — ele pode ser implementado em C, PASCAL ou qualquer outra linguagem que pode suportar o layout binário de um objeto COM.

OLE refere-se a todas as classes derivadas de IUnknown como "interfaces". Esta é uma distinção importante, desde uma "interface", como IUnknown não traz consigo nenhuma implementação. Ela simplesmente define o protocolo pelo qual objetos se comunicam, não as especificidades do que fazem essas implementações. Isso é razoável para um sistema que permite a máxima flexibilidade. É o trabalho do MFC para implementar um comportamento padrão para MFC/C++ programas.

Para compreender a implementação do MFC de IUnknown você deve primeiro entender o que esta interface é. Uma versão simplificada do IUnknown é definida a seguir:

 classe IUnknown
{
público:
 nbsp;  virtual QueryInterface(REFIID iid, void** ppvObj) HRESULT = 0;
    virtual ULO&NG AddRef() = 0;
    virtual ULONG Release() = 0;
}

&Notanbsp;  Certos detalhes necessários de Convenção de chamada, tais como stdcall são deixados de fora para esta ilustração.

As funções de membro AddRef e versão controlam o gerenciamento de memória do objeto. COM usa um esquema de contagem de referência para manter o controle de objetos. Um objeto nunca é referenciado diretamente como em C++. Em vez disso, sempre são referências a objetos COM através de um ponteiro. Para versão o objeto quando o proprietário é feito usá-lo, o membro do objeto de lançamento é chamado (em oposição a usando o operador excluir, como seria feito para um objeto C++ tradicional). A referência contagem mecanismo permite várias referências a um único objeto para ser gerenciado. Uma implementação de AddRef e versão mantém uma contagem de referência no objeto — o objeto não será excluído até que sua contagem de referência chega a zero.

AddRef e versão são bastante simples de um ponto de vista de implementação. Aqui é uma implementação simples:

ULO&NG CMyObj::AddRef() {nbsp;  retornar + + m_dwRef; 
}

ULONG CMyObj::Release() {se (-m_dwRef = = 0) {
        Excluir este; 
        return 0;
    }
    retornar m_dwRef;
}

A função de membro de QueryInterface é um pouco mais interessante. Como você pode imaginar, não é muito interessante ter um objeto cujas funções de membro somente são AddRef e versão — que seria bom informar ao objeto para fazer mais coisas do que fornece IUnknown . É onde o Falha de QueryInterface é útil. Ele permite que você obter uma "interface" diferente no mesmo objeto. Essas interfaces são geralmente derivadas de IUnknown e adicionam a funcionalidade adicional, adicionando novas funções de membro. Interfaces COM nunca tem variáveis de membro declarados na interface, e todas as funções de membro são declaradas como puro virtual. Por exemplo,

classe IPri&ntInterface: IUnknown pública
{
público:
 nbsp;  virtual void PrintObject() = 0;
}

Para obter um IPrintInterface se você tiver apenas um IUnknown, chame IUnknown:: QueryInterface usando o IID do IPrintInterface. Um IID é um número de 128 bits que identifica com exclusividade a interface. Há um IID para cada interface que você ou OLE definem. Se pUnk é um ponteiro para um objeto de IUnknown , o código para recuperar uma IPrintInterface do pode ser

IPrintInterface * pPrint = NULL;
se (pUnk-gt;QueryInterface(IID_IPrintInterface, (void**) & pPrint) = = NOERROR)
{
    pPrint - > PrintObject();
    pPrint - > Release();   
        / / Versão ponteiro obtido através de QueryInterface
}

Isso parece bastante fácil, mas como você poderia implementar um objeto que oferece suporte a IPrintInterface e o IUnknown interface? Neste caso é simple, uma vez que o IPrintInterface é derivada diretamente de IUnknown — implementando IPrintInterface, IUnknown é suportada automaticamente. Por exemplo:

 classe CPrintObj: CPrintInterface pública
{
 nbsp;  virtual HRESULT QueryInterface(REFIID iid, void** ppvObj);
    virtual ULO&NG AddRef();
    virtual ULONG Release();
    virtual void PrintObject();
}

As implementações de AddRef e versão seria exatamente as mesmas que as aplicadas acima. CPrintObj::QueryInterface teria esta aparência

HRESULT CPrintObj::QueryInterface(REFIID iid, void FAR* FAR* ppvObj)
{
 nbsp;  se (iid = = IID_IUnknown | | iid = = IID_IPrintInterface)
    {
        * ppvObj = this;
        AddRef();
        retornar &NOERROR;
    }
    retornar ResultFromScode(E_NOINTERFACE);
}

Como você pode ver, se o identificador de interface (IID) é reconhecido, um ponteiro é retornado ao seu objeto; caso contrário, ocorrerá um erro. Observe também que um bem-sucedido QueryInterface resulta em uma implícita AddRef. Naturalmente, você também deve implementar CEditObj::Print. Isso é simple porque o IPrintInterface foi derivado diretamente a interface de IUnknown . No entanto, se você quiser oferecer suporte a duas interfaces diferentes, ambos derivado de IUnknown, considere o seguinte

classe IEditI&nterface: IUnkown pública
{
público:
 nbsp;  virtual void EditObject() = 0;
}

Embora haja um número de maneiras diferentes para implementar uma classe de suporte IEditInterface e IPrintInterface, incluindo o uso de C++ Múltiplo herança, esta observação irá concentrar-se sobre o uso de classes aninhadas para implementar essa funcionalidade.

classe CEditPrintObj
{
público:
 nbsp;  CEditPrintObj();

HRESULT QueryInterface(REFIID iid, void**);
    AddRef() ULO&NG;
    Release() ULONG;
    DWORD m_dwRef;

Classe CPrintObj: IPrintInterface pública
    {
    público:
        CEditPrintObj * m_pParent;
        virtual HRESULT QueryInterface(REFIID iid, void** ppvObj);
        virtual ULONG AddRef();
        virtual ULONG Release();
    } m_printObj;

Classe CEditObj: IEditInterface pública
    {
    público:
        CEditPrintObj * m_pParent;
        virtual ULONG QueryInterface(REFIID iid, void** ppvObj);
        virtual ULONG AddRef();
        virtual ULONG Release();
    } m_editObj;
}

A toda a implementação é incluída abaixo:

CEditPrintObj::CEditPrintObj()
{
 nbsp;  m_editObj.m_pParent = este;
    m_printObj.m_pParent = este;
}

ULONG CEditPrintObj::AddRef() {return + + m_dwRef;
}

CEditPrintObj::Release()
{
    se (-m_dwRef = = 0)
    {
        Excluir este;
        return 0;
    }
    retornar m_dwRef;
}

HRESULT CEditPrintObj::QueryInterface(REFIID iid, void** ppvObj)
{
    se (iid = = IID_IUnknown | | iid = = IID_IPrintInterface)
    {
        * ppvObj = & m_printObj;
        AddRef();
        retornar NOERROR;
    }
    else if (iid = = IID_IEditInterface)
    {
        * ppvObj = & m_editObj;
        AddRef();
        retornar NOERROR;
    }
    retornar ResultFromScode(E_NOINTERFACE);
}

ULONG CEditPrintObj::CEditObj::AddRef() {retorno m_pParent - > AddRef(); 
}

ULONG CEditPrintObj::CEditObj::Release() {retorno m_pParent - > Release(); 
}

HRESULT CEditPrintObj::CEditObj::QueryInterface (
    REFIID iid, vazio ** ppvObj) {retorno m_pParent - > falha de QueryInterface (iid, ppvObj); 
}

ULONG CEditPrintObj::CPrintObj::AddRef() {retorno m_pParent - > AddRef(); 
}

ULONG CEditPrintObj::CPrintObj::Release() {retorno m_pParent - > Release(); 
}

HRESULT CEditPrintObj::CPrintObj::QueryInterface (
    REFIID iid, vazio ** ppvObj) {retorno m_pParent - > falha de QueryInterface (iid, ppvObj); 
}

Observe que a maior parte da implementação IUnknown é colocado para a classe de CEditPrintObj , em vez de duplicar o código em CEditPrintObj::CEditObj e CEditPrintObj::CPrintObj. Isso reduz a quantidade de código e evita erros. O ponto-chave aqui é que a partir da interface IUnknown é possível chamar Falha de QueryInterface para recuperar qualquer interface que o objeto pode suportar, e de cada uma dessas interfaces é possível fazer o mesmo. Isso significa que todas as funções disponíveis de QueryInterface de cada interface devem se comportar exatamente da mesma maneira. Para que esses objetos incorporados ao chamar a implementação no objeto"externo", um ponteiro de costas é usado (m_pParent). O m_pParent ponteiro é inicializado durante o Construtor CEditPrintObj. Em seguida, você poderia implementar CEditPrintObj::CPrintObj::PrintObject e CEditPrintObj::CEditObj::EditObject também. Um pouco de código foi adicionado para adicionar um recurso — a capacidade de editar o objeto. Felizmente, é bastante incomum para interfaces ter apenas uma função membro único (embora isso aconteça) e nesse caso, EditObject e PrintObject normalmente iria ser combinadas em uma única interface.

Isso é um monte de explicação e um monte de código para esse cenário simple. As classes do MFC/OLE fornecem uma alternativa mais simples. A implementação do MFC usa uma técnica semelhante à maneira como mensagens do Windows são encapsuladas com mapas de mensagem. Este recurso é chamado de Mapas de Interface e é discutido na próxima seção.

Mapas de Interface do MFC

MFC/OLE inclui uma implementação de Interface mapas"" semelhante do MFC "Mapas de mensagem" e "Mapas de expedição" no conceito e execução. Os principais recursos do Interface mapas da MFC são os seguintes:

Além disso, mapas de interface suportam o seguinte recursos avançados:

Para obter mais informações sobre agregação, consulte o OLE Programmer Referência.

Suporte a mapa interface do MFC está enraizada na classe CCmdTarget . CCmdTarget "tem uma" referência contagem, bem como todas as funções de membro associadas à implementação de IUnknown (a contagem de referência por exemplo é no CCmdTarget). Para criar uma classe que ofereça suporte a OLE COM, você derivar uma classe de CCmdTarget e usar várias macros, bem como funções de membro de CCmdTarget para implementar as interfaces desejadas. Implementação do MFC usa classes aninhadas para definir cada implementação de interface muito semelhante ao exemplo acima. Isto é feito mais fácil com uma implementação padrão de IUnknown, bem como um número de macros que eliminam algum código repetitivo.

Informações básicas sobre mapa de interface

Para implementar uma classe usando a interface do MFC mapas siga estes passos:

  1. Derive uma classe direta ou indiretamente de CCmdTarget.

  2. Use a função DECLARE_INTERFACE_MAP na definição de classe derivada.

  3. Para cada interface que você deseja oferecer suporte, use o BEGIN_INTERFACE_PART e END_INTERFACE_PART macros na definição de classe.

  4. No arquivo de implementação, use o BEGIN_INTERFACE_MAP e END_INTERFACE_MAP macros para definir o mapa de interface da classe.

  5. Para cada IID suportada, use a macro INTERFACE_PART entre o BEGIN_INTERFACE_MAP e END_INTERFACE_MAP macros para mapear que IID para um determinado "parte" da sua classe.

  6. Implementar cada uma das classes aninhadas que representam as interfaces que você suporte.

  7. Use a macro METHOD_PROLOGUE para acessar o pai, CCmdTarget-derivado objeto.

  8. Falha de QueryInterface , AddRefe Releasepodem delegar a execução de CCmdTarget dessas funções (ExternalAddRef, ExternalReleasee ExternalQueryInterface).

O CPrintEditObj exemplo acima poderia ser implementado da seguinte forma:

classe CPrintEditObj: público CCmdTarget
{
público:
 nbsp;  / / dados de membro e funções de membro para CPrintEditObj ir aqui

/ / Interface Maps
protegido:
    DECLARE_I&NTERFACE_MAP()

BEGIN_INTERFACE_PART (EditObj, IEditInterface)
        STDMETHOD_ (vazio, EditObject)();
    END_INTERFACE_PART(EditObj)

BEGIN_INTERFACE_PART (PrintObj, IPrintInterface)
        STDMETHOD_ (vazio, PrintObject)();
    END_INTERFACE_PART(PrintObj)
}

A declaração acima cria uma classe derivado de CCmdTarget. A macro DECLARE_INTERFACE_MAP informa o quadro que essa classe terá um mapa de interface Personalizar. Além disso, o BEGIN_INTERFACE_PART e END_INTERFACE_PART macros definem classes aninhadas, neste caso com nomes CEditObj e CPrintObj (o x é usado apenas para diferenciar as classes aninhadas de global que começam com "C" e interface classes que começam com "I"). Dois membros aninhados dessas classes são criados: m_CEditObj e m_CPrintObj, respectivamente. As macros automaticamente declaram o AddRef, lançamentoe funções QueryInterface ; portanto você só declare as funções específicas para essa interface: EditObject e PrintObject (macro OLE STDMETHOD é usado tal para que palavras-chave virtual e stdcall é fornecido conforme apropriado para a plataforma de destino).

Para implementar o mapa de interface para esta classe:

BEGI&N_INTERFACE_MAP (CPrintEditObj, CCmdTarget)
 nbsp;  INTERFACE_PART (CPrintEditObj, IID_IPrintInterface, PrintObj)
    INTERFACE_PART (CPrintEditObj, IID_IEditInterface, EditObj)
END_INTERFACE_MAP()

Isto liga o IID de IID_IPrintInterface com m_CPrintObj e IID_IEditInterface com m_CEditObj respectivamente. A implementação de CCmdTarget de QueryInterface (CCmdTarget::ExternalQueryInterface) usa esse mapa para retornar ponteiros para m_CPrintObj e m_CEditObj quando solicitada. Não é necessário incluir uma entrada para IID_IUnknown; o quadro irá usar a primeira interface do mapa (no maiúscminúsc, m_CPrintObj) quando é requisitado a IID_IUnknown.

Mesmo que a macro BEGIN_INTERFACE_PART automaticamente declarado o AddRef, lançamento e funções QueryInterface para você, você ainda precisará implementá-las:

ULONG CEditPrintObj::XEditObj::AddRef() de exportação extrema
{
 nbsp;  METHOD_PROLOGUE (CEditPrintObj, EditObj)
    retorno de pThis - > ExternalAddRef();
}

ULONG CEditPrintObj::XEditObj::Release() de exportação extrema
{
    METHOD_PROLOGUE (CEditPrintObj, EditObj)
    retorno de pThis - > ExternalRelease();
}

HRESULT CEditPrintObj::XEditObj::QueryInterface (de exportação até agora
    REFIID iid, void FAR * extremo * ppvObj)
{
    METHOD_PROLOGUE (CEditPrintObj, EditObj)
    retorno (HRESULT) pThis - > ExternalQueryInterface (& iid, ppvObj);
}

privatevoid até agora EXPORTAR CEditPrintObj::XEditObj::EditObject()
{
    METHOD_PROLOGUE (CEditPrintObj, EditObj)
    / / código para "Editar" o objeto, independentemente desse meio...
}

A implementação de CEditPrintObj::CPrintObj, seria semelhante às definições acima para CEditPrintObj::CEditObj. Embora seja possível criar uma macro que pode ser usada para gerar automaticamente essas funções (na verdade, no início do desenvolvimento do MFC/OLE este foi o caso), torna-se difícil definir pontos de interrupção quando uma macro gera mais de uma linha de código. Por esse motivo, esse código é expandido manualmente.

Usando a implementação do quadro de mensagem mapas lá são um número de coisas que não eram necessárias para fazer:

Além disso, a estrutura usa mapas de mensagem internamente. Isso permite que você derivar de uma classe de quadro, dizer COleServerDoc, que já oferece suporte certas interfaces e fornece substituições ou acréscimos para as interfaces fornecidas pela estrutura. Isso é habilitado pelo fato de que o quadro apoia plenamente herdando uma interface mapeiam de uma classe base — é esta a razão por que BEGIN_INTERFACE_MAP toma como seu segundo parâmetro, o nome da classe base.

&Notanbsp;  Geralmente não é possível reutilizar a implementação das implementações internas do MFC das interfaces OLE herdando a especialização incorporado da interface da versão do MFC. Isso não é possível porque o uso da Macro METHOD_PROLOGUE para acessar o recipiente CCmdTarget -derivado objeto implica um fixo de deslocamento do objeto incorporado de CCmdTarget-derivado objeto. Isto significa, por exemplo, que você não pode derivar um XMyAdviseSink incorporado de implementação do MFC em COleClientItem::XAdviseSink, porque XAdviseSink depende de estar em um deslocamento específico da parte superior do objeto COleClientItem.

Você pode, no entanto, delegar a implementação do MFC para todas as funções que você deseja comportamento usar como padrão do MFC. Isso é feito na implementação do MFC de IOleInPlaceFrame (XOleInPlaceFrame) na classe COleFrameHook (ele delega para m_xOleInPlaceUIWindow para muitas funções). Este projeto foi escolhido para reduzir o tamanho de tempo de execução de objetos que implementam várias interfaces; Ele elimina a necessidade para um ponteiro de volta (tais como o m_pParent de maneira foi usado na seção anterior).

Agregação e mapas de Interface

Além de oferecer suporte objetos autônomos, MFC também oferece suporte a agregação. Agregação próprio é demasiado complexo um tópico para discutir aqui; consulte o OLE Programmer Referência para obter mais informações sobre agregação. Esta nota simplesmente irá descrever o suporte para agregação construído em mapas de interface e estrutura.

Existem duas maneiras de usar a agregação: (1) usando um objeto COM que ofereça suporte a agregação e (2) implementação de um objeto que pode ser agregado por outro. Esses recursos podem ser referidos como "usando um objeto agregado" e "fazendo um objeto agregável". MFC oferece suporte a ambos.

Usando um objeto agregado

Para usar um objeto agregado, lá deve ser alguma forma de amarrar o agregado no mecanismo de QueryInterface. Em outras palavras, o objeto agregado deve comportar-se como se fosse uma parte nativo do seu objeto. Então como é que este empate em mecanismo de mapa de interface do MFC? Em adição à macro INTERFACE_PART , onde um objeto aninhado é mapeado para um IID, você pode também declarar um agregado objeto como parte de sua classe CCmdTarget derivado. Para fazer isso, a macro INTERFACE_AGGREGATE é usada. Isso permite que você especificar uma variável de membro (que deve ser um ponteiro para um IUnknown ou classe derivada), que é a integração do mecanismo de mapa de interface. Se o ponteiro não for NULL, quando CCmdTarget::ExternalQueryInterface é chamado, o quadro irá automaticamente chamar função de membro do objeto global QueryInterface , se o IID solicitada não é um do nativo s IIDsuportado pelo próprio objeto CCmdTarget.

Para usar a macro INTERFACE_AGGREGATE, siga estes passos:

  1. Declare uma variável de membro (um IUnknown *) que contém um ponteiro para o objeto agregado.

  2. Incluir uma INTERFACE_AGGREGATE macro em seu mapa de interface, que remete para a variável de membro por nome.

  3. Em algum momento (geralmente durante a CCmdTarget::OnCreateAggregates), inicializar a variável de membro como algo diferente de NULL.

Por exemplo,

classe CAggrExample: público CCmdTarget
{
público:
 nbsp;  CAggrExample();

protegido:
    LPUNKNOWN m_lpAggrInner;
    virtual BOOL OnCreateAggregates();

DECLARE_INTERFACE_MAP()
    / / interface "nativo" parte macros podem ser usadas aqui
};

CAggrExample::CAggrExample()
{
    m_lpAggrInner = NULL;
}

BOOL CAggrExample::OnCreateAggregates()
{
    / / cabo até agregado com o correto controle desconhecido
    m_lpAggrInner = CoCreateInstance (CLSID_Example,
        GetControllingUnknown(), CLSCTX_INPROC_SERVER,
        IID_IUnknown, (LPVOID *) & m_lpAggrInner);
    se (m_lpAggrInner = = NULL)
        retornar FALSE;
    / / Opcionalmente, criar outros objetos agregados aqui
    retornar TRUE;
}

BEGIN_INTERFACE_MAP (CAggrExample, CCmdTarget)
    / / nativas entradas "INTERFACE_PART" ir aqui
    INTERFACE_AGGREGATE (CAggrExample, m_lpAggrInner)
END_INTERFACE_MAP()

O m_lpAggrInner é inicializada no Construtor para NULL. O quadro irá ignorar uma variável de membro nulo na implementação padrão de QueryInterface. OnCreateAggregates é um bom lugar para criar, na verdade, seus agregado objetos. Você terá que chamá-lo explicitamente se você estiver criando o objeto fora da implementação do MFC de COleObjectFactory. A razão para a criação de agregações em CCmdTarget::OnCreateAggregates , bem como o uso de CCmdTarget::GetControllingUnknown se tornará aparente quando criando objetos agregáveis é discutida.

Esta técnica dará seu objeto de todas as interfaces que o agregado objeto suporta além de suas interfaces nativas. Se você quer somente um subconjunto das interfaces que ofereça suporte a agregação, você pode substituir o CCmdTarget::GetInterfaceHook. Isso permite que você hookability de nível muito baixo, semelhante à Falha de QueryInterface. Normalmente, você quer todas as interfaces que o agregado oferece suporte.

Fazendo uma implementação de objeto agregável

Para um objeto ser agregáveis, a implementação de QueryInterface , AddRefe Releasedeve delegar para um "controle desconhecido." Em outras palavras, para ser parte do objeto, ele deve delegado Falha de QueryInterface , AddRefe Releasepara um objeto diferente, também derivado de IUnknown. Este "controle desconhecido" é fornecido para o objeto quando ele é criado, ou seja, ele é fornecido para a implementação de COleObjectFactory. Implementar isso carrega uma pequena quantidade de sobrecarga e em alguns casos não é desejável, o MFC faz isso opcional. Para ativar um objeto para ser agregáveis, você chama CCmdTarget::EnableAggregation do Construtor do objeto.

Se o objeto usa também agregados, você também deve ser-se de passar o correto "controle desconhecido" para os agregado objetos. Geralmente esse ponteiro IUnknown é passado para o objeto quando o agregado é criado. Por exemplo, o parâmetro pUnkOuter é o controle "desconhecido" para objetos criados com CoCreateInstance. O ponteiro "Controlando desconhecido" correta pode ser recuperado por chamado CCmdTarget::GetControllingUnknown. O valor retornado da função de que, no entanto, não é válido durante o Construtor. Por esta razão, recomenda-se que você crie seus agregados apenas em uma Substituir do CCmdTarget::OnCreateAggregates, onde o valor de retorno de GetControllingUnknown é confiável, mesmo se criado a partir da aplicação de COleObjectFactory.

Também é importante que o objeto manipular a contagem de referência correta quando adicionando ou liberando contagens de referência artificial. Para garantir que este é o caso, sempre Chame ExternalAddRef e ExternalRelease em vez de InternalRelease e InternalAddRef. É raro para chamar InternalRelease ou InternalAddRef em uma classe que oferece suporte à agregação.

Material de referência

Uso avançado de OLE, como definir suas próprias interfaces ou substituir implementação a estrutura das interfaces do OLE requer o uso do subjacente mecanismo de mapa de interface.

Esta seção aborda cada macro e as APIs que são usadas para implementar esses recursos avançados.

CCmdTarget::EnableAggregation — Função descrição

privatevoid EnableAggregation();

&Notanbsp;  Chame essa função no Construtor da classe derivada se você quiser suporte a agregação de OLE para objetos desse tipo. Isto prepara uma implementação IUnknown especial que é exigida para objetos agregáveis.

CCmdTarget::ExternalQueryInterface — Função descrição

DWORD ExternalQueryInterface (const void FAR * lpIID, LPVOID FAR * ppvObj);

lpIID

Um ponteiro até que ponto a um IID (o primeiro argumento para QueryInterface)

ppvObj

Um ponteiro para um IUnknown (segundo argumento para QueryInterface)

&Notanbsp;  Chamar essa função em sua implementação de IUnknown para cada interface de sua classe implementa. Essa função fornece o padrão orientado a dados implementação de QueryInterface com base no mapa de interface do objeto. É necessário converter o valor de retorno para um HRESULT. Se o objeto é agregado, essa função irá chamar o "controle IUnknown" em vez de usar o mapa de interface local.

CCmdTarget::ExternalAddRef — Função descrição

DWORD ExternalAddRef();

&Notanbsp;  Chamar essa função em sua implementação de IUnknown:: AddRef para cada interface de sua classe implementa. O valor de retorno é o novo contar de referência no objeto CCmdTarget. Se o objeto é agregado, essa função irá chamar o "controle IUnknown" em vez de manipular a contagem de referência local.

CCmdTarget::ExternalRelease — Função descrição

DWORD ExternalRelease();

&Notanbsp;  Chamar essa função em sua implementação de IUnknown:: Release para cada interface de sua classe implementa. O valor de retorno indica o novo contar de referência no objeto. Se o objeto é agregado, essa função irá chamar o "controle IUnknown" em vez de manipular a contagem de referência local.

DECLARE_INTERFACE_MAP — Descrição da Macro

DECLARE_INTERFACE_MAP

&Notanbsp;  Use essa macro em qualquer classe derivado de CCmdTarget que terá um mapa de interface. Usado em muito da mesma maneira que DECLARE_MESSAGE_MAP. Esta chamada de macro deve ser colocada na definição de classe, geralmente em um cabeçalho (.H) arquivo. Uma classe com DECLARE_INTERFACE_MAP deve definir o mapa de interface no arquivo de implementação (.CPP) com o BEGIN_INTERFACE_MAP e END_INTERFACE_MAP macros.

BEGIN_INTERFACE_PART e END_INTERFACE_PART — as descrições de Macro

BEGIN_INTERFACE_PART (localClass, iface);

END_INTERFACE_PART (localClass)

localClass

O nome da classe que implementa a interface

iface

O nome da interface que implementa essa classe

&Notanbsp;  Para cada interface que sua classe irá implementar, você precisará ter um par de BEGIN_INTERFACE_PART e END_INTERFACE_PART . Essas macros Definirm uma local classe derivada da interface OLE que você define como uma variável de membro incorporado dessa classe. Os Falha de QueryInterface , AddRefe ReleaseMembros são declarados automaticamente. Você deve incluir as declarações para as funções de membro que são parte da interface está sendo implementada (estas declarações são colocadas entre o BEGIN_INTERFACE_PART e END_INTERFACE_PART macros).

O argumento iface é a interface OLE que você deseja implementar, como IAdviseSink, ou IPersistStorage (ou sua própria interface personalizada).

O argumento localClass é o nome da classe local que será definido. Um ' X' automaticamente ser colocado antes do nome. Essa Convenção de nomenclatura é usada para evitar colisões com classes globais de mesmo nome. Além disso, o nome do membro incorporado, o mesmo que o nome de localClass , exceto que ele é prefixado por 'm_x'.

Por exemplo:

BEGI&N_INTERFACE_PART (MyAdviseSink, IAdviseSink)
 nbsp; STDMETHOD_(void,OnDataChange) (LPFORMATETC, LPSTGMEDIUM);
   STDMETHOD_(void,OnViewChange) (DWORD, longo);
   STDMETHOD_(void,OnRename)(LPMONIKER);
   STDMETHOD_(void,OnSave)();
   STDMETHOD_(void,OnClose)();
END_INTERFACE_PART(MyAdviseSink)

definiria uma classe local chamada XMyAdviseSink derivado de IAdviseSink, e um membro da classe no qual é declarada chamado m_xMyAdviseSink.Note:

&Notanbsp;  As linhas que começam com STDMETHOD_ essencialmente são copiadas de OLE2.H e ligeiramente modificado. Copiá-los de OLE2.H pode reduzir os erros que são difíceis de resolver.

BEGIN_INTERFACE_MAP e END_INTERFACE_MAP — as descrições de Macro

BEGIN_INTERFACE_MAP (theClass, baseClass)

END_INTERFACE_MAP

theClass

A classe na qual o mapa de interface é definida

baseClass

A classe da qual theClass deriva.

Observações: O BEGIN_INTERFACE_MAP e END_INTERFACE_MAP macros é usado no arquivo de implementação para realmente definir o mapa de interface. Para cada interface que é implementada há um ou mais chamadas de macro INTERFACE_PART . Para cada agregado que usa a classe, há uma INTERFACE_AGGREGATE macro chamada.

INTERFACE_PART — Descrição da Macro

INTERFACE_PART (theClass, iid, localClass)

theClass

O nome da classe que contém o mapa de interface.

iid

O IID que deve ser mapeada para a classe incorporada.

localClass

O nome da classe local (menos o ' X')

&Notanbsp;  Essa macro é usada entre a macro BEGIN_INTERFACE_MAP e a macro END_INTERFACE_MAP para cada interface que seu objeto oferecerá suporte. Ele permite que você mapear um IID para um membro da classe indicado pelo theClass e localClass. 'm_x' será adicionado automaticamente ao localClass . Note que mais do que um IID pode ser associado com um único membro. Isto é muito útil quando você estiver implementando apenas uma interface "mais derivado" e deseja fornecer, bem como todas as interfaces de intermediárias. Um bom exemplo disto é a interface de IOleInPlaceFrameWindow . Sua hierarquia fica assim:

IU&nknown
 nbsp;  IOleWindow
        IOleUIWindow
            IOleInPlaceFrameWindow

Se um objeto implementa IOleInPlaceFrameWindow, um cliente pode Falha de QueryInterface em qualquer uma dessas interfaces: IOleUIWindow, IOleWindowou IUnknown, além da interface "mais derivada" IOleInPlaceFrameWindow (o que você realmente está implementando). Para lidar com isso você pode usar mais de uma macro INTERFACE_PART para mapear cada interface base para a interface de IOleInPlaceFrameWindow

no arquivo de definição de classe:

BEGIN_INTERFACE_PART (CMyFrameWindow, IOleInPlaceFrameWindow)

no arquivo de implementação da classe:

BEGI&N_INTERFACE_MAP (CMyWnd, CFrameWnd)
 nbsp;  INTERFACE_PART (CMyWnd, IID_IOleWindow, MyFrameWindow)
    INTERFACE_PART (CMyWnd, IID_IOleUIWindow, MyFrameWindow)
    INTERFACE_PART (CMyWnd, IID_IOleInPlaceFrameWindow, MyFrameWindow)
END_INTERFACE_MAP

A estrutura cuida de IUnknown desde que sempre é necessário.

INTERFACE_PART — Descrição da Macro

INTERFACE_AGGREGATE (theClass, theAggr)

theClass

O nome da classe que contém o mapa de interface,

theAggr

O nome da variável de membro a ser agregados.

&Notanbsp;  Essa macro é usada para informar o quadro que a classe é usando um objeto agregado. Ele deve aparecer entre o BEGIN_INTERFACE_PART e END_INTERFACE_PART macros. Um agregado objeto é um objeto separado, derivado de IUnknown. Usando um agregado e a macro INTERFACE_AGGREGATE , você pode fazer todas as interfaces que suporta a agregação parece ser diretamente suportado pelo objeto. O argumento theAggr é simplesmente o nome de uma variável de membro de sua classe que é derivada de IUnknown (direta ou indiretamente). Todas as macros INTERFACE_AGGREGATE devem seguir as macros INTERFACE_PART quando colocados em um mapa de interface.

Técnico anotações por número |nbsp; &Notas técnicas por categoria

Index