Compartilhar via


TN038: Implementação de IUnknown em MFC/OLE

Nota

A nota técnica a seguir 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 desatualizados ou incorretos. Para obter as informações mais recentes, é recomendável que você pesquise o tópico de interesse no índice de documentação online.

No centro do OLE 2 está o "Modelo de Objeto do Componente OLE" ou COM. O COM define um padrão de como os objetos que cooperam se comunicam entre si. Isso inclui os detalhes da aparência de um "objeto", incluindo como os métodos são expedidos em um objeto. O COM também define uma classe base, da qual todas as classes compatíveis com COM são derivadas. Esta classe base é IUnknown. Embora a interface IUnknown seja conhecida como uma classe C++, COM não é específica para nenhuma linguagem , ela pode ser implementada em C, PASCAL ou qualquer outra linguagem que possa dar suporte ao layout binário de um objeto COM.

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

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

class IUnknown
{
public:
    virtual HRESULT QueryInterface(REFIID iid, void** ppvObj) = 0;
    virtual ULONG AddRef() = 0;
    virtual ULONG Release() = 0;
};

Nota

Determinados detalhes necessários da convenção de chamada, como __stdcall são deixados de fora para esta ilustração.

As funções membro AddRef e Release controlam o gerenciamento de memória do objeto. O COM usa um esquema de contagem de referência para controlar objetos. Um objeto nunca é referenciado diretamente como você faria em C++. Em vez disso, os objetos COM são sempre referenciados por meio de um ponteiro. Para liberar o objeto quando o proprietário terminar de usá-lo, o membro Release do objeto é chamado (em vez de usar a exclusão do operador, como seria feito para um objeto C++ tradicional). O mecanismo de contagem de referência permite que várias referências a um único objeto sejam gerenciadas. Uma implementação de AddRef e Release mantém uma contagem de referência no objeto – o objeto não é excluído até que sua contagem de referência atinja zero.

AddRef e Release são bastante simples do ponto de vista de implementação. Aqui está uma implementação trivial:

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

ULONG CMyObj::Release()
{
    if (--m_dwRef == 0)
    {
        delete this;
        return 0;
    }
    return m_dwRef;
}

A função de membro QueryInterface é um pouco mais interessante. Não é muito interessante possuir um objeto cujas únicas as funções de membro sejam AddRef e Release – seria bom comandar o objeto a realizar mais tarefas do que o IUnknown oferece. É aqui que QueryInterface é útil. Ele permite que você obtenha uma "interface" diferente no mesmo objeto. Essas interfaces geralmente são derivadas de IUnknown e adicionam funcionalidade adicional adicionando novas funções de membro. As interfaces COM nunca têm variáveis de membro declaradas na interface e todas as funções de membro são declaradas como puras virtuais. Por exemplo

class IPrintInterface : public IUnknown
{
public:
    virtual void PrintObject() = 0;
};

Para obter um IPrintInterface se você tiver apenas um IUnknown, chame QueryInterface usando o IID de IPrintInterface. Um IID é um número de 128 bits que identifica exclusivamente a interface. Há um IID para cada interface definida por você ou OLE. Se pUnk for um ponteiro para um objeto IUnknown, o código para recuperar um IPrintInterface dele poderá ser:

IPrintInterface* pPrint = NULL;
if (pUnk->QueryInterface(IID_IPrintInterface, (void**)&pPrint) == NOERROR)
{
    pPrint->PrintObject();
    pPrint->Release();
    // release pointer obtained via QueryInterface
}

Isso parece bastante fácil, mas como você implementaria um objeto que dá suporte à interface IPrintInterface e IUnknown Nesse caso, é simples, pois o IPrintInterface é derivado diretamente de IUnknown – implementando IPrintInterface, IUnknown é automaticamente compatível. Por exemplo:

class CPrintObj : public CPrintInterface
{
    virtual HRESULT QueryInterface(REFIID iid, void** ppvObj);
    virtual ULONG AddRef();
    virtual ULONG Release();
    virtual void PrintObject();
};

As implementações de AddRef e Release seriam exatamente iguais às implementadas acima. CPrintObj::QueryInterface teria esta aparência:

HRESULT CPrintObj::QueryInterface(REFIID iid, void FAR* FAR* ppvObj)
{
    if (iid == IID_IUnknown || iid == IID_IPrintInterface)
    {
        *ppvObj = this;
        AddRef();
        return NOERROR;
    }
    return E_NOINTERFACE;
}

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

class IEditInterface : public IUnkown
{
public:
    virtual void EditObject() = 0;
};

Embora haja várias maneiras diferentes de implementar uma classe que dá suporte a IEditInterface e IPrintInterface, incluindo o uso de herança múltipla do C++, essa observação se concentrará no uso de classes aninhadas para implementar essa funcionalidade.

class CEditPrintObj
{
public:
    CEditPrintObj();

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

    class CPrintObj : public IPrintInterface
    {
    public:
        CEditPrintObj* m_pParent;
        virtual HRESULT QueryInterface(REFIID iid, void** ppvObj);
        virtual ULONG AddRef();
        virtual ULONG Release();
    } m_printObj;

    class CEditObj : public IEditInterface
    {
    public:
        CEditPrintObj* m_pParent;
        virtual ULONG QueryInterface(REFIID iid, void** ppvObj);
        virtual ULONG AddRef();
        virtual ULONG Release();
    } m_editObj;
};

Toda a implementação está incluída abaixo:

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

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

CEditPrintObj::Release()
{
    if (--m_dwRef == 0)
    {
        delete this;
        return 0;
    }
    return m_dwRef;
}

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

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

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

HRESULT CEditPrintObj::CEditObj::QueryInterface(REFIID iid, void** ppvObj)
{
    return m_pParent->QueryInterface(iid, ppvObj);
}

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

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

HRESULT CEditPrintObj::CPrintObj::QueryInterface(REFIID iid, void** ppvObj)
{
    return m_pParent->QueryInterface(iid, ppvObj);
}

Observe que a maior parte da implementação IUnknown é colocada na classe CEditPrintObj em vez de duplicar o código em CEditPrintObj::CEditObj e CEditPrintObj::CPrintObj. Isso reduz a quantidade de código e evita bugs. O ponto chave aqui é que, a partir da interface IUnknown, é possível chamar QueryInterface para recuperar qualquer interface que o objeto possa dar suporte e, de cada uma dessas interfaces, é possível fazer o mesmo. Isso significa que todas as funções QueryInterface disponíveis de cada interface devem se comportar exatamente da mesma maneira. Para que esses objetos inseridos chamem a implementação no "objeto externo", um ponteiro para trás é usado (m_pParent). O ponteiro m_pParent é inicializado no construtor CEditPrintObj. Em seguida, você implementaria também CEditPrintObj::CPrintObj::PrintObject e CEditPrintObj::CEditObj::EditObject. Um pouco de código foi adicionado para adicionar um recurso : a capacidade de editar o objeto. Felizmente, é bastante incomum que as interfaces tenham apenas uma única função de membro (embora isso aconteça) e, nesse caso, EditObject e PrintObject normalmente seriam combinados em uma única interface.

Isso é muita explicação e muito código para um cenário tão simples. As classes MFC/OLE fornecem uma alternativa mais simples. A implementação do MFC usa uma técnica semelhante à maneira como as mensagens do Windows são encapsuladas com Mapas de Mensagens. A funcionalidade é chamada Mapas de Interface e é discutida na próxima seção.

Mapas de interface MFC

MFC/OLE inclui uma implementação de "Mapas de Interface" semelhante aos "Mapas de Mensagens" e "Mapas de Expedição" do MFC em conceito e execução. Os principais recursos dos Mapas de Interface do MFC são os seguintes:

  • Uma implementação padrão de IUnknown , integrada à classe CCmdTarget.

  • Manutenção da contagem de referência, modificada por AddRef e Release

  • Implementação controlada por dados de QueryInterface

Além disso, os mapas de interface dão suporte aos seguintes recursos avançados:

  • Suporte para a criação de objetos COM agregáveis

  • Suporte para usar objetos agregados na implementação de um objeto COM

  • A implementação é ligável e extensível

Para obter mais informações sobre agregação, consulte o tópico Agregação.

O suporte ao mapa de interface do MFC está enraizado na classe CCmdTarget. Contagem de referência CCmdTarget "has-a", bem como todas as funções de membro associadas à implementação IUnknown (a contagem de referência, por exemplo, está em CCmdTarget). Para criar uma classe compatível com OLE COM, você deriva uma classe de CCmdTarget e usa várias macros, bem como funções membro de CCmdTarget para implementar as interfaces desejadas. A implementação do MFC usa classes aninhadas para definir cada implementação de interface muito semelhante ao exemplo acima. Isso é facilitado com uma implementação padrão do IUnknown, bem como várias macros que eliminam parte do código repetitivo.

Noções básicas do mapa de interface

Para implementar uma classe usando mapas de interface do MFC

  1. Derivar 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 dar suporte, use as macros BEGIN_INTERFACE_PART e END_INTERFACE_PART na definição de classe.

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

  5. Para cada IID com suporte, use a macro INTERFACE_PART entre as macros BEGIN_INTERFACE_MAP e END_INTERFACE_MAP para mapear essa IID para uma "parte" específica da classe.

  6. Implemente cada uma das classes aninhadas que representam as interfaces compatíveis.

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

  8. AddRef, Releasee QueryInterface podem delegar à implementação CCmdTarget dessas funções (ExternalAddRef, ExternalReleasee ExternalQueryInterface).

O exemplo CPrintEditObj acima pode ser implementado da seguinte maneira:

class CPrintEditObj : public CCmdTarget
{
public:
    // member data and member functions for CPrintEditObj go here

// Interface Maps
protected:
    DECLARE_INTERFACE_MAP()

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

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

A declaração acima cria uma classe derivada de CCmdTarget. A macro DECLARE_INTERFACE_MAP informa à estrutura que essa classe terá um mapa de interface personalizado. Além disso, as macros BEGIN_INTERFACE_PART e END_INTERFACE_PART definem classes aninhadas, nesse caso com nomes CEditObj e CPrintObj (o X é usado apenas para diferenciar as classes aninhadas de classes globais que começam com "C" e classes de interface que começam com "I"). Dois membros aninhados dessas classes são criados: m_CEditObj e m_CPrintObj, respectivamente. As macros declaram automaticamente as funções AddRef, Releasee QueryInterface; portanto, você declara apenas as funções específicas para esta interface: EditObject e PrintObject (a macro OLE STDMETHOD é usada para que _stdcall e palavras-chave virtuais sejam fornecidas conforme apropriado para a plataforma de destino).

Para implementar o mapa de interface para esta classe:

BEGIN_INTERFACE_MAP(CPrintEditObj, CCmdTarget)
    INTERFACE_PART(CPrintEditObj, IID_IPrintInterface, PrintObj)
    INTERFACE_PART(CPrintEditObj, IID_IEditInterface, EditObj)
END_INTERFACE_MAP()

Isso conecta a IID IID_IPrintInterface com a m_CPrintObj e a IID_IEditInterface com a m_CEditObj, respectivamente. A implementação CCmdTarget de QueryInterface (CCmdTarget::ExternalQueryInterface) usa esse mapa para retornar ponteiros para m_CPrintObj e m_CEditObj quando solicitado. Não é necessário incluir uma entrada para IID_IUnknown; a estrutura usará a primeira interface no mapa (nesse caso, m_CPrintObj) quando IID_IUnknown for solicitado.

Embora a macro BEGIN_INTERFACE_PART tenha declarado automaticamente as funções AddRef, Release e QueryInterface para você, você ainda precisa implementá-las:

ULONG FAR EXPORT CEditPrintObj::XEditObj::AddRef()
{
    METHOD_PROLOGUE(CEditPrintObj, EditObj)
    return pThis->ExternalAddRef();
}

ULONG FAR EXPORT CEditPrintObj::XEditObj::Release()
{
    METHOD_PROLOGUE(CEditPrintObj, EditObj)
    return pThis->ExternalRelease();
}

HRESULT FAR EXPORT CEditPrintObj::XEditObj::QueryInterface(
    REFIID iid,
    void FAR* FAR* ppvObj)
{
    METHOD_PROLOGUE(CEditPrintObj, EditObj)
    return (HRESULT)pThis->ExternalQueryInterface(&iid, ppvObj);
}

void FAR EXPORT CEditPrintObj::XEditObj::EditObject()
{
    METHOD_PROLOGUE(CEditPrintObj, EditObj)
    // code to "Edit" the object, whatever that means...
}

A implementação para CEditPrintObj::CPrintObj seria semelhante às definições acima para CEditPrintObj::CEditObj. Embora seja possível criar uma macro que poderia ser usada para gerar automaticamente essas funções (mas anteriormente no desenvolvimento MFC/OLE esse era 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 da estrutura de mapas de mensagens, há várias coisas que não eram necessárias para fazer:

  • Implementar QueryInterface

  • Implementar AddRef e Release

  • Declarar qualquer um desses métodos internos em ambas as interfaces

Além disso, a estrutura usa mapas de mensagens internamente. Isso permite derivar de uma classe de estrutura, digamos COleServerDoc, que já dá suporte a determinadas interfaces e fornece substituições ou adições às interfaces fornecidas pela estrutura. Você pode fazer isso porque a estrutura dá suporte total à herdação de um mapa de interface de uma classe base. Esse é o motivo pelo qual BEGIN_INTERFACE_MAP usa como segundo parâmetro o nome da classe base.

Nota

Em geral, não é possível reutilizar a implementação das implementações internas do MFC das interfaces OLE apenas herdando a especialização inserida dessa interface da versão MFC. Isso não é possível porque usar a macro METHOD_PROLOGUE para obter acesso ao objeto derivado de CCmdTarget contentor implica um deslocamento fixo do objeto inserido do objeto derivado de CCmdTarget. Isso significa, por exemplo, que você não pode derivar um XMyAdviseSink inserido da implementação do MFC em COleClientItem::XAdviseSink, pois o XAdviseSink depende de estar em um deslocamento específico da parte superior do objeto COleClientItem.

Nota

No entanto, você pode delegar à implementação do MFC para todas as funções nas quais deseja o comportamento padrão do MFC. Isso é feito na implementação MFC de IOleInPlaceFrame (XOleInPlaceFrame) na classe COleFrameHook (ele delega a m_xOleInPlaceUIWindow para várias funções). Esse design foi escolhido para reduzir o tamanho do runtime de objetos que implementam muitas interfaces; elimina a necessidade de um ponteiro de fundo (como a maneira como m_pParent foi usado na seção anterior).

Mapas de agregação e interface

Além de dar suporte a objetos COM autônomos, o MFC também dá suporte à agregação. A agregação em si é um tópico muito complexo para discutir aqui; consulte o tópico de agregação para obter mais informações sobre agregação. Esta observação simplesmente descreverá o suporte para agregação integrado aos mapas de estrutura e interface.

Há duas maneiras de usar a agregação: (1) usando um objeto COM que dá suporte à agregação e (2) implementar um objeto que pode ser agregado por outro. Esses recursos podem ser chamados de "usando um objeto agregado" e "tornando um objeto agregável". O MFC dá suporte a ambos.

Usando um objeto de agregação

Para usar um objeto agregado, é necessário que haja alguma maneira de vincular a agregação ao mecanismo QueryInterface. Em outras palavras, o objeto de agregação deve se comportar como se fosse uma parte nativa do seu objeto. Portanto, como isso está relacionado ao mecanismo de mapa de interface do MFC? Além da macro INTERFACE_PART, em que um objeto aninhado é mapeado para um Identificador de Interface (IID), você também pode declarar um objeto agregado como parte da sua classe derivada CCmdTarget. Para fazer isso, a macro INTERFACE_AGGREGATE é usada. Isso permite que você especifique uma variável de membro (que deve ser um ponteiro para uma IUnknown ou classe derivada), a qual deve ser integrada ao mecanismo de mapeamento de interface. Se o ponteiro não for NULL quando CCmdTarget::ExternalQueryInterface for chamado, a estrutura chamará automaticamente a função de membro QueryInterface do objeto agregado, se o IID solicitado não for um dos IID nativos com suporte do objeto CCmdTarget em si.

Para usar a macro INTERFACE_AGGREGATE

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

  2. Inclua uma macro INTERFACE_AGGREGATE no mapa da interface, que se refere à variável de membro pelo nome.

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

Por exemplo:

class CAggrExample : public CCmdTarget
{
public:
    CAggrExample();

protected:
    LPUNKNOWN m_lpAggrInner;
    virtual BOOL OnCreateAggregates();

    DECLARE_INTERFACE_MAP()
    // "native" interface part macros may be used here
};

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

BOOL CAggrExample::OnCreateAggregates()
{
    // wire up aggregate with correct controlling unknown
    m_lpAggrInner = CoCreateInstance(CLSID_Example,
        GetControllingUnknown(), CLSCTX_INPROC_SERVER,
        IID_IUnknown, (LPVOID*)&m_lpAggrInner);

    if (m_lpAggrInner == NULL)
        return FALSE;
    // optionally, create other aggregate objects here
    return TRUE;
}

BEGIN_INTERFACE_MAP(CAggrExample, CCmdTarget)
    // native "INTERFACE_PART" entries go here
    INTERFACE_AGGREGATE(CAggrExample, m_lpAggrInner)
END_INTERFACE_MAP()

A variável m_lpAggrInner é inicializada no construtor para NULL. A estrutura ignora uma variável de membro NULL na implementação padrão de queryInterface. OnCreateAggregates é um bom lugar para realmente criar seus objetos agregados. Você precisará chamá-lo explicitamente se estiver criando o objeto fora da implementação do MFC de COleObjectFactory. O motivo para a criação de agregações em CCmdTarget::OnCreateAggregates, bem como o uso de CCmdTarget::GetControllingUnknown se tornará aparente quando a criação de objetos aggregáveis for discutida.

Essa técnica fornecerá ao objeto todas as interfaces que o objeto agregado suporta, além de suas interfaces nativas. Se você quiser apenas um subconjunto das interfaces que o agregado suporta, poderá sobrescrever CCmdTarget::GetInterfaceHook. Isso permite que você tenha uma conexão de nível muito baixo, semelhante a QueryInterface. Normalmente, você deseja todas as interfaces compatíveis com a agregação.

Tornando uma implementação de objeto aggregável

Para que um objeto seja agregável, a implementação de AddRef, Release e QueryInterface deve delegar a um "controle desconhecido". Em outras palavras, para que ele faça parte do objeto, ele deve delegar AddRef, Release e QueryInterface a um objeto diferente, também derivado de IUnknown. Esse "controle desconhecido" é fornecido ao 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, portanto, o MFC torna isso opcional. Para permitir que um objeto seja aggregável, chame CCmdTarget::EnableAggregation do construtor do objeto.

Se o objeto também usa agregações, passe o "controle desconhecido" correto para os objetos de agregação. Em geral, esse ponteiro IUnknown é passado para o objeto quando a agregação é criada. Por exemplo, o parâmetro pUnkOuter é o "controlador desconhecido" para objetos criados com CoCreateInstance. O ponteiro "controlador desconhecido" correto pode ser recuperado chamando CCmdTarget::GetControllingUnknown. O valor retornado dessa função, no entanto, não é válido durante o construtor. Por esse motivo, sugere-se que você crie suas agregações somente em uma substituição de CCmdTarget::OnCreateAggregates, em que o valor retornado GetControllingUnknown é confiável, mesmo que criado com base na implementação de COleObjectFactory.

Também é importante que o objeto manipule a contagem de referência correta ao adicionar ou liberar contagens de referência artificial. Para garantir que esse seja o caso, sempre chame ExternalAddRef e ExternalRelease em vez de InternalRelease e InternalAddRef. É raro chamar InternalRelease ou InternalAddRef em uma classe que dá suporte à agregação.

Material de referência

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

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

CCmdTarget::EnableAggregation — Descrição da função

void EnableAggregation();

Observações

Chame essa função no construtor da classe derivada se você quiser dar suporte à agregação OLE para objetos desse tipo. Isso prepara uma implementação especial do IUnknown que é necessária para objetos aggregáveis.

CCmdTarget::ExternalQueryInterface — Descrição da função

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

Parâmetros

lpIID
Um ponteiro distante para um IID (o primeiro argumento para QueryInterface)

ppvObj
Um ponteiro para um IUnknown* (segundo argumento para QueryInterface)

Observações

Chame essa função na implementação do IUnknown para cada interface que sua classe implementa. Essa função fornece a implementação padrão controlada por dados de QueryInterface com base no mapa de interface do objeto. É necessário converter o valor retornado em um HRESULT. Se o objeto for agregado, essa função chamará o "controle IUnknown" em vez de usar o mapa da interface local.

CCmdTarget::ExternalAddRef — Descrição da função

DWORD ExternalAddRef();

Observações

Chame essa função na implementação de IUnknown::AddRef para cada interface que sua classe implementa. O valor retornado é a nova contagem de referência no objeto CCmdTarget. Se o objeto for agregado, essa função chamará o "controlling IUnknown" em vez de manipular a contagem de referência local.

CCmdTarget::ExternalRelease — Descrição da função

DWORD ExternalRelease();

Observações

Chame essa função na implementação de IUnknown::Release para cada interface que sua classe implementa. O valor retornado indica a nova contagem de referência no objeto. Se o objeto for agregado, essa função chamará o "controlling IUnknown" em vez de manipular a contagem de referência local.

DECLARE_INTERFACE_MAP, descrição da macro

DECLARE_INTERFACE_MAP

Observações

Use essa macro em qualquer classe derivada de CCmdTarget que terá um mapa de interface. Usado da mesma maneira que DECLARE_MESSAGE_MAP. Essa invocação 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 da interface no arquivo de implementação (. CPP) com as macros BEGIN_INTERFACE_MAP e END_INTERFACE_MAP.

BEGIN_INTERFACE_PART e END_INTERFACE_PART, descrições de macro

BEGIN_INTERFACE_PART(localClass, iface);
END_INTERFACE_PART(localClass)

Parâmetros

localClass
O nome da classe que implementa a interface

iface
O nome da interface que essa classe implementa

Observações

Para cada interface que sua classe implementará, você precisa ter um par BEGIN_INTERFACE_PART e END_INTERFACE_PART. Essas macros definem uma classe local derivada da interface OLE que você define, bem como uma variável de membro inserida dessa classe. Os membros AddRef, Releasee QueryInterface são declarados automaticamente. Você deve incluir as declarações para as outras funções membro que fazem parte da interface que está sendo implementada (essas declarações são colocadas entre as macros BEGIN_INTERFACE_PART e END_INTERFACE_PART).

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

O argumento localClass é o nome da classe local que será definida. Um 'X' será anexado automaticamente ao nome. Essa convenção de nomenclatura é usada para evitar colisões com classes globais de mesmo nome. Além disso, o nome do membro inserido é o mesmo do nome localClass, só que com o prefixo 'm_x'.

Por exemplo:

BEGIN_INTERFACE_PART(MyAdviseSink, IAdviseSink)
    STDMETHOD_(void, OnDataChange)(LPFORMATETC, LPSTGMEDIUM);
    STDMETHOD_(void, OnViewChange)(DWORD, LONG);
    STDMETHOD_(void, OnRename)(LPMONIKER);
    STDMETHOD_(void, OnSave)();
    STDMETHOD_(void, OnClose)();
END_INTERFACE_PART(MyAdviseSink)

definiria uma classe local chamada XMyAdviseSink derivada de IAdviseSink e um membro da classe na qual ela é declarada chamada m_xMyAdviseSink.Observação:

Nota

As linhas que começam com STDMETHOD_ são essencialmente copiadas do OLE2.H e modificadas ligeiramente. Copiando-os do OLE2. H pode reduzir erros difíceis de resolver.

BEGIN_INTERFACE_MAP e END_INTERFACE_MAP, descrições de macro

BEGIN_INTERFACE_MAP(theClass, baseClass)
END_INTERFACE_MAP

Parâmetros

theClass
A classe na qual o mapa da interface deve ser definido

baseClass
A classe da qual theClass deriva.

Observações

As macros BEGIN_INTERFACE_MAP e END_INTERFACE_MAP são usadas no arquivo de implementação para definir o mapa da interface. Para cada interface implementada, há uma ou mais invocações de macro INTERFACE_PART. Para cada agregação que a classe usa, há uma invocação de macro INTERFACE_AGGREGATE.

INTERFACE_PART — Descrição da macro

INTERFACE_PART(theClass, iid, localClass)

Parâmetros

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

iid
O IID que deve ser mapeado para a classe inserida.

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

Observações

Essa macro é usada entre a macro BEGIN_INTERFACE_MAP e a macro END_INTERFACE_MAP para cada interface que seu objeto dará suporte. Ela permite mapear uma IID para um membro da classe indicada por theClass e localClass. O 'm_x' será adicionado ao localClass automaticamente. Observe que mais de um IID pode estar associado a um só membro. Isso é muito útil quando você está implementando apenas uma interface "mais derivada" e deseja fornecer todas as interfaces intermediárias também. Um bom exemplo disso é a interface IOleInPlaceFrameWindow. Sua hierarquia tem esta aparência:

IUnknown
    IOleWindow
        IOleUIWindow
            IOleInPlaceFrameWindow

Se um objeto for implementado IOleInPlaceFrameWindow, um cliente poderá QueryInterface em qualquer uma destas interfaces: IOleUIWindow, IOleWindow ou IUnknown, além da interface IOleInPlaceFrameWindow "mais derivada" (aquela que você está realmente implementando). Para lidar com isso, você pode usar mais de uma macro INTERFACE_PART para mapear cada interface base para a interface IOleInPlaceFrameWindow:

no arquivo de definição de classe:

BEGIN_INTERFACE_PART(CMyFrameWindow, IOleInPlaceFrameWindow)

no arquivo de implementação de classe:

BEGIN_INTERFACE_MAP(CMyWnd, CFrameWnd)
    INTERFACE_PART(CMyWnd, IID_IOleWindow, MyFrameWindow)
    INTERFACE_PART(CMyWnd, IID_IOleUIWindow, MyFrameWindow)
    INTERFACE_PART(CMyWnd, IID_IOleInPlaceFrameWindow, MyFrameWindow)
END_INTERFACE_MAP

A estrutura cuida do IUnknown porque ele sempre é necessário.

INTERFACE_PART — Descrição da macro

INTERFACE_AGGREGATE(theClass, theAggr)

Parâmetros

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

theAggr
O nome da variável de membro que deve ser agregada.

Observações

Essa macro é usada para informar à estrutura que a classe está usando um objeto de agregação. Ele deve aparecer entre as macros BEGIN_INTERFACE_PART e END_INTERFACE_PART. Um objeto agregado é um objeto separado, derivado de IUnknown. Usando uma agregação e a macro INTERFACE_AGGREGATE, você pode fazer com que todas as interfaces compatíveis com a agregação pareçam ser diretamente compatíveis com o objeto. O argumento theAggr é simplesmente o nome de uma variável membro de sua classe que é derivada de IUnknown (direta ou indiretamente). Todas as macros INTERFACE_AGGREGATE devem seguir as macros INTERFACE_PART quando colocadas em um mapa de interface.

Consulte também

Observações técnicas por número
Notas técnicas por categoria