TN038: MFC/OLE IUnknown implementação
Observaçã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. |
No coração do OLE 2 é o "OLE Component Object Model", ou com.COM define um padrão para objetos como cooperação comunicar ao outro.Isso inclui os detalhes do que um "objeto" aparência, incluindo como os métodos são expedidos em um objeto.COM também define uma classe base, da qual derivam todas as classes compatíveis COM.Essa classe base é IUnknown.Embora o IUnknown interface é conhecido como uma classe C++, COM não é específico para qualquer um idioma — pode ser implementado em C, PASCAL ou qualquer outra linguagem que suporte 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 a "interface", como IUnknown não carrega com ele nenhuma implementação.Ele simplesmente define o protocolo que objetos se comunicar, não as especificidades do que fazem essas implementações.Isso é razoável para um sistema que permite a máxima flexibilidade.É trabalho do MFC para implementar um comportamento padrão para programas do MFC/C++.
Para compreender a implementação do MFC de IUnknown você deve primeiro compreender o que é essa interface.Uma versão simplificada do IUnknown definido abaixo:
class IUnknown
{
public:
virtual HRESULT QueryInterface(REFIID iid, void** ppvObj) = 0;
virtual ULONG AddRef() = 0;
virtual ULONG Release() = 0;
};
Observação |
---|
Detalhes de determinado necessária convenção de chamada, como __stdcall esquerda para esta ilustração. |
O AddRef e Release funções de membro controlam o gerenciamento de memória do objeto.COM usa um esquema de contagem de referência para controlar objetos.Um objeto nunca é referenciado diretamente como faria em C++.Em vez disso, os objetos COM são sempre referenciados através de um ponteiro.Para liberar o objeto quando o proprietário é feito usando ele, o objeto Release membro é chamado (em oposição a usando o operador delete, como faria para um objeto C++ tradicional).A mecanismo de contagem de referência permite várias referências a um único objeto para ser gerenciado.Uma implementação de AddRef e Release 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.
AddRefe Release são bastante simples de um ponto de vista de implementação.Esta é uma implementação simples:
ULONG CMyObj::AddRef()
{
return ++m_dwRef;
}
ULONG CMyObj::Release()
{
if (--m_dwRef == 0)
{
delete this;
return 0;
}
return m_dwRef;
}
O QueryInterface função de membro é um pouco mais interessante.Não é muito interessante ter um objeto cujas somente funções de membro são AddRef e Release — seria bom informar ao objeto para fazer mais coisas que IUnknown fornece.É aqui onde QueryInterface é útil.Permite obter uma "interface" diferente no mesmo objeto.Essas interfaces são geralmente derivadas de IUnknown e adicione a funcionalidade adicional, adicionando novas funções de membro.Interfaces COM nunca tem variáveis de membro declaradas na interface e todas as funções de membro são declaradas como puro virtual.Por exemplo,
class IPrintInterface : public IUnknown
{
public:
virtual void PrintObject() = 0;
};
Para obter uma IPrintInterface se você tiver apenas um IUnknown, chamada 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 IUnknown objeto, o código para recuperar uma IPrintInterface de pode ser:
IPrintInterface* pPrint = NULL;
if (pUnk->QueryInterface(IID_IPrintInterface,
(void**)&pPrint) == NOERROR)
{
pPrint->PrintObject();
pPrint->Release();
// release pointer obtained via QueryInterface
}
Que parece bem fácil, mas como você implementa um objeto que suporte tanto a IPrintInterface e IUnknown interface?Neste caso, é simple como o IPrintInterface é derivada diretamente de IUnknown — implementando IPrintInterface, IUnknown suporte automático.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 seria exatamente o mesmo como esses implementado acima.CPrintObj::QueryInterface poderia ter 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 identificador de interface (IID) é reconhecido, um ponteiro é retornado para o objeto; Caso contrário, ocorrerá um erro.Observe também que um sucesso QueryInterface resulta em uma implícita AddRef.Obviamente, você também teria que implementar CEditObj::Print.É simple porque o IPrintInterface foi derivado diretamente do IUnknown interface.Entretanto se você quiser dar suporte a duas interfaces diferentes, ambos derivado de IUnknown, considere o seguinte:
class IEditInterface : public IUnkown
{
public:
virtual void EditObject() = 0;
};
Embora haja um número de diferentes maneiras de implementar uma classe de suporte ambos IEditInterface e IPrintInterface, inclusive usando C++ herança múltipla, esta observação irá 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;
};
A implementação completa é 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 do IUnknown implementação é colocada na CEditPrintObj classe em vez de duplicar o código de CEditPrintObj::CEditObj e CEditPrintObj::CPrintObj.Isso reduz a quantidade de código e evita erros.O ponto principal aqui é que a partir da interface IUnknown é possível chamar QueryInterface para recuperar qualquer interface o objeto pode suportar e de cada uma dessas interfaces é possível fazer o mesmo.Isso significa que todos os QueryInterface funções disponíveis de cada interface devem se comportar exatamente da mesma maneira.Em ordem para esses objetos incorporados chamar a implementação de "objeto externo", um ponteiro de trás é usado (m_pParent).O ponteiro de m_pParent é inicializado durante o construtor CEditPrintObj.Em seguida, você implementaria CEditPrintObj::CPrintObj::PrintObject e CEditPrintObj::CEditObj::EditObject também.Foi adicionado um pouco de código para adicionar um recurso — a capacidade de editar o objeto.Felizmente, é bastante incomum para interfaces têm apenas uma função de membro único (Apesar de isso acontecer) e nesse caso, EditObject e PrintObject seriam geralmente ser combinadas em uma única interface.
É muito código para um cenário simple e muita explicação.As classes do 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 quebradas com mapas de mensagem.Esse recurso é chamado Interface mapas e é discutido na próxima seção.
Mapas de Interface do MFC
MFC/OLE inclui uma implementação da Interface "Maps" semelhante a "Mapas de mensagem" e "Mapas de despacho" do MFC em conceito e execução.Os principais recursos do MFC Interface mapas são:
Uma implementação padrão de IUnknown, integrado a CCmdTarget classe.
Manutenção da contagem de referência, modificada por AddRef e versão
Implementação de orientados a dadosQueryInterface
Além disso, mapas de interface suportam os seguintes recursos avançados:
Suporte para criação de objetos COM agregáveis
Suporte para o uso agregados objetos na implementação de um objeto COM
A implementação é extensível e hookable
Para obter mais informações sobre agregação, consulte o OLE Programmer referência.
Suporte de mapa de interface do MFC está enraizada na CCmdTarget classe.CCmdTarget"tem um" referência contagem, bem como todas as funções de membro associadas com o IUnknown implementação (por exemplo é a contagem de referência em CCmdTarget).Para criar uma classe que ofereça suporte a COM OLE, você derivar uma classe de CCmdTarget e usar várias macros, bem como funções de membro CCmdTarget para implementar interfaces desejadas.Implementação do MFC usa classes aninhadas para definir cada implementação de interface muito semelhante ao exemplo acima.Isso é mais fácil com uma implementação padrão de IUnknown, bem como um número de macros que eliminam alguns códigos repetitivos.
Para implementar uma classe usando a interface do MFC mapas
Derivar uma classe direta ou indiretamente de CCmdTarget.
Use o DECLARE_INTERFACE_MAP função na definição de classe derivada.
Para cada interface que você deseja dar suporte, use o BEGIN_INTERFACE_PART e END_INTERFACE_PART macros na definição de classe.
No arquivo de implementação, use o BEGIN_INTERFACE_MAP e END_INTERFACE_MAP macros para definir o mapa de interface da classe.
Para cada IID suportada, use o INTERFACE_PART macro entre o BEGIN_INTERFACE_MAP e END_INTERFACE_MAP macros para mapear que IID para um determinado "parte" da sua classe.
Implemente cada uma das classes aninhadas que representam as interfaces de que suporte.
Use o METHOD_PROLOGUE macro para acessar o pai, CCmdTarget-derivado objeto.
AddRef, Release, e QueryInterface pode delegar para o CCmdTarget implementação dessas funções (ExternalAddRef, ExternalRelease, e ExternalQueryInterface).
CPrintEditObj exemplo acima poderia 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.O DECLARE_INTERFACE_MAP macro informa a estrutura que essa classe terá um mapa de interface personalizada.Além disso, o BEGIN_INTERFACE_PART e END_INTERFACE_PART macros definem classes aninhadas, neste caso com nomes CEditObj e CPrintObj (X é usado somente para diferenciar classes aninhadas de globais classes que começam com "C" e interface classes que começam com "I").Dois membros dessas classes aninhados são criados: m_CEditObj e m_CPrintObj, respectivamente.As macros automaticamente declarar o AddRef, Release, e QueryInterface funções; portanto apenas declarar as funções específicas a esta interface: EditObject e PrintObject (macro OLE STDMETHOD é usado como para que stdcall e palavras-chave virtual são 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 IID IID_IPrintInterface com m_CPrintObj e IID_IEditInterface m_CEditObj respectivamente.O CCmdTarget implementação de QueryInterface (CCmdTarget::ExternalQueryInterface) usa este mapa para retornar ponteiros para m_CPrintObj e m_CEditObj quando solicitado.Não é necessário incluir uma entrada para IID_IUnknown; o framework usará a primeira interface no mapa (no caso, m_CPrintObj) quando IID_IUnknown é solicitada.
Embora o BEGIN_INTERFACE_PART macro automaticamente declarada o AddRef, Release e QueryInterface funções para você, você ainda precisará 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 de CEditPrintObj::CPrintObj, seria semelhante para as definições acima para CEditPrintObj::CEditObj.Embora seja possível criar uma macro pode ser usada para gerar automaticamente essas funções (mas anteriormente no desenvolvimento do MFC/OLE esse foi o caso), fica 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 mensagem, há várias coisas que não eram necessárias para fazer:
Implementar QueryInterface
Lançamento e AddRef implementar
Declarar um desses métodos internos em ambas as interfaces
Além disso, a estrutura usa mapas de mensagem internamente.Isso permite que você derivar de uma classe do framework, digamos COleServerDoc, que já suporta certas interfaces e fornece substituições ou acréscimos para as interfaces fornecidas pelo framework.Isso é ativado pelo fato de que a estrutura oferece suporte totalmente herdando um mapa de interface de uma classe base — que é o motivo por que BEGIN_INTERFACE_MAP toma como seu segundo parâmetro, o nome da classe base.
Observação |
---|
Geralmente não é possível reutilizar a implementação de implementações internas do MFC interfaces OLE por herdando a especialização incorporada da interface da versão do MFC.Não é possível porque o uso da METHOD_PROLOGUE macro para obter acesso a que contém CCmdTarget-derivado objeto implica um fixo de deslocamento do objeto incorporado do CCmdTarget-derivado objeto.Isso significa, por exemplo, você não pode derivar um XMyAdviseSink incorporado implementação do MFC no COleClientItem::XAdviseSink, pois XAdviseSink depende sendo em um deslocamento específico da parte superior do COleClientItem objeto. |
Observação |
---|
Você pode, Entretanto, delegar a implementação do MFC para todas as funções que você deseja que o comportamento do padrão do MFC.Isso é feito na implementação do MFC IOleInPlaceFrame (XOleInPlaceFrame) na COleFrameHook classe (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 de um ponteiro de voltar (como m_pParent forma foi usado na seção anterior). |
Agregação e mapas de Interface
Além de dar suporte a objetos COM autônomos, o MFC também oferece suporte à agregação.Agregação em si é um tópico muito complexo para discutir aqui; Consulte o OLE Programmer referência para obter mais informações sobre agregação.Esta nota simplesmente descreve o suporte para agregação incorporado a mapas de estrutura e interface.
Para usar a agregação de duas maneiras: (1) usando um objeto COM que oferece suporte à agregação e (2) a implementação de um objeto que pode ser agregado por outro.Esses recursos podem ser denominados "usando um objeto agregado" e "fazendo um objeto agregável".MFC oferece suporte a ambos.
Usando um objeto agregado
Para usar um objeto agregado, há precisa ser alguma maneira de unir a agregação em mecanismo de QueryInterface.Em outras palavras, o objeto agregado deve se comportam como se fosse uma parte nativa do seu objeto.Assim como o este empate em mecanismo de mapa de interface do MFC?Além de INTERFACE_PART macro, onde um objeto aninhado é mapeado para um IID, você pode também declarar um objeto agregado como parte de sua CCmdTarget classe derivada.Para fazer isso, o INTERFACE_AGGREGATE macro é usado.Isso permite que você especificar uma variável de membro (que deve ser um ponteiro para um IUnknown ou classe derivada), que é para ser integrado ao mecanismo de mapa de interface.Se o ponteiro não é nulo quando CCmdTarget::ExternalQueryInterface é chamado, a estrutura automaticamente chamar o objeto agregado QueryInterface função de membro, se o IID solicitada não é uma nativa IIDs suportados pelo CCmdTarget próprio objeto.
Para usar a macro INTERFACE_AGGREGATE
Declare uma variável de membro (um IUnknown *) que contém um ponteiro para o objeto agregado.
Incluir um INTERFACE_AGGREGATE macro em seu mapa de interface que se refere à variável de membro por nome.
Em algum momento (geralmente durante a CCmdTarget::OnCreateAggregates), inicialize a variável de membro para algo diferente de nulo.
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()
O m_lpAggrInner é inicializada no construtor para NULL.O framework irá ignorar uma variável de membro nulo na implementação padrão de QueryInterface.OnCreateAggregates é um bom lugar para realmente criar objetos de agregação.Você precisará chamar explicitamente se você estiver criando o objeto fora da implementação do MFC de COleObjectFactory.O motivo para criar agregações no CCmdTarget::OnCreateAggregates , bem como o uso de CCmdTarget::GetControllingUnknown ficará aparente quando Criando objetos agregáveis é discutido.
Esta técnica dará seu objeto todas as interfaces que o objeto agregado suporta além de suas interfaces nativas.Se você quiser apenas um subconjunto das interfaces que oferece suporte a agregação, você pode substituir CCmdTarget::GetInterfaceHook.Isso permite que hookability de nível muito baixo, semelhante a QueryInterface.Geralmente, você deseja que todas as interfaces que oferece suporte a agregação.
Fazendo uma implementação de objeto agregável
Para um objeto ser agregáveis, a implementação de AddRef, Release, e QueryInterface deve delegar para "desconhecido controlar". Em outras palavras, para que ela seja parte do objeto, ele deve delegar AddRef, Release, e QueryInterface a 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 executa uma pequena quantidade de sobrecarga e em alguns casos não é desejável, portanto MFC torna opcional.Para ativar um objeto a ser agregáveis, chamada CCmdTarget::EnableAggregation do construtor do objeto.
Se o objeto usa também os agregados, também deverá se passar o correto "controlar desconhecido" para objetos agregados.Geralmente isso IUnknown ponteiro é passado para o objeto quando o agregado é criado.Por exemplo, o parâmetro pUnkOuter é o "controle desconhecido" para objetos criados com CoCreateInstance.O ponteiro correto "controlando desconhecido" pode ser recuperado chamando CCmdTarget::GetControllingUnknown.O valor retornado da função, no entanto, não é válido durante o construtor.Por esse motivo, é sugerido que você crie seus agregados somente em substituição CCmdTarget::OnCreateAggregates, onde o valor de retorno de GetControllingUnknown é confiável, mesmo se criado a partir de COleObjectFactory implementação.
Também é importante que o objeto manipular a contagem de referência correto quando adicionando ou liberando contagens de referência artificial.Para garantir que esse é o caso, sempre chamar 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 substituindo a implementação da estrutura das interfaces OLE requer o uso do mecanismo do mapa interface subjacente.
Esta seção aborda cada macro e as APIs que são usadas para implementar esses recursos avançados.
CCmdTarget::EnableAggregation — Descrição da função
void EnableAggregation();
Comentários
Chame essa função no construtor da classe derivada, se você deseja dar suporte a agregação de OLE para objetos desse tipo.Isso prepara uma implementação IUnknown especial que é necessária para objetos agregáveis.
CCmdTarget::ExternalQueryInterface — Descrição da função
DWORD ExternalQueryInterface(
const void FAR* lpIID,
LPVOID FAR* ppvObj
);
Comentários
Parâmetros
lpIID
Um ponteiro distante para um IID (o primeiro argumento para QueryInterface)ppvObj
Um ponteiro para um IUnknown * (segundo argumento para QueryInterface)
Comentários
Chamar essa função em sua implementação de IUnknown para cada interface de sua classe implementa.Essa função fornece a implementação padrão orientado a dados de QueryInterface com base no mapa de interface do objeto.É necessário converter o valor de retorno HRESULT.Se o objeto é agregado, esta função irá chamar "IUnknown controlar" em vez de usar o mapa de interface local.
CCmdTarget::ExternalAddRef — Descrição da função
DWORD ExternalAddRef();
Comentários
Chamar essa função em sua implementação de IUnknown:: AddRef para cada interface de sua classe implementa.O valor de retorno é a nova contagem de referência no objeto CCmdTarget.Se o objeto é agregado, esta função irá chamar "IUnknown controlar" em vez de manipular a contagem de referência local.
CCmdTarget::ExternalRelease — Descrição da função
DWORD ExternalRelease();
Comentários
Chamar essa função em sua implementação de IUnknown:: Release para cada interface de sua classe implementa.O valor de retorno indica a nova contagem de referência no objeto.Se o objeto é agregado, esta função irá chamar "IUnknown controlar" em vez de manipular a contagem de referência local.
DECLARE_INTERFACE_MAP — Descrição da Macro
DECLARE_INTERFACE_MAP
Comentários
Use essa macro em qualquer classe derivada de CCmdTarget que terá um mapa de interface.Usado da mesma maneira como DECLARE_MESSAGE_MAP.A chamada de macro deve ser colocada na definição de classe, geralmente em um cabeçalho (.H) o 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
)
Comentários
Parâmetros
localClass
O nome da classe que implementa a interfaceiface
O nome da interface que implementa esta classe
Comentários
Para cada interface que implementará sua classe, você precisa ter um BEGIN_INTERFACE_PART e END_INTERFACE_PART par.Essas macros definem uma classe local derivada da interface OLE que definem como uma variável de membro incorporado da classe.O AddRef, Release, e QueryInterface os membros são declarados automaticamente.Você deve incluir as declarações para as funções de membro são parte da interface que está sendo implementada (essas declarações são colocadas entre o BEGIN_INTERFACE_PART e END_INTERFACE_PART macros).
O iface argumento é a interface OLE que você deseja implementar, como IAdviseSink, ou IPersistStorage (ou sua própria interface personalizada).
O localClass argumento é o nome da classe local que será definido.Um ' X' automaticamente será anexado ao nome.Essa convenção de nomenclatura é usada para evitar colisões com classes globais do mesmo nome.Além disso, o nome do membro incorporado, mesmo que o localClass nome exceto 'm_x' como prefixo.
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)
define uma classe local chamada XMyAdviseSink derivada de IAdviseSink e um membro da classe na qual é declarada chamado m_xMyAdviseSink.Note:
Observação |
---|
Linhas que começam com STDMETHOD_ essencialmente são copiados de OLE2.H e ligeiramente modificado.Copiando-os 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
Comentários
Parâmetros
theClass
A classe na qual o mapa de interface é definidabaseClass
A classe da qual theClass deriva.
Comentários
O BEGIN_INTERFACE_MAP e END_INTERFACE_MAP macros são usadas no arquivo de implementação para realmente definir o mapa de interface.Para cada interface implementada há um ou mais INTERFACE_PART chamadas de macro.Para cada agregado que usa a classe, há um INTERFACE_AGGREGATE chamada de macro.
INTERFACE_PART — Descrição da Macro
INTERFACE_PART(
theClass,
iid,
localClass
)
Comentários
Parâmetros
theClass
O nome da classe que contém o mapa de interface.iid
O IID que é mapeada para a classe incorporada.localClass
O nome da classe local (menos 'X').
Comentários
Essa macro é usada entre o BEGIN_INTERFACE_MAP macro e o END_INTERFACE_MAP macro para cada interface oferecerá suporte a seu objeto.Permite mapear um IID para um membro da classe indicada por theClass e localClass.'m_x' será adicionado para o localClass automaticamente.Observe que mais de um IID pode ser associado um único membro.Isso é muito útil quando você estiver implementando somente uma interface "mais derivados" e deseja fornecer todas as interfaces intermediárias.Um bom exemplo é o IOleInPlaceFrameWindow interface.Sua hierarquia tem esta aparência:
IUnknown
IOleWindow
IOleUIWindow
IOleInPlaceFrameWindow
Se um objeto implementa IOleInPlaceFrameWindow, um cliente pode QueryInterface em qualquer uma dessas interfaces: IOleUIWindow, IOleWindow, ou IUnknown, além da interface "mais derivada" IOleInPlaceFrameWindow (aquele que realmente está implementando).Para lidar com isso, você pode usar mais de um INTERFACE_PART macro mapear cada interface base para o IOleInPlaceFrameWindow interface:
no arquivo de definição de classe:
BEGIN_INTERFACE_PART(CMyFrameWindow, IOleInPlaceFrameWindow)
no arquivo de implementação da 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 de IUnknown porque é sempre necessário.
INTERFACE_PART — Descrição da Macro
INTERFACE_AGGREGATE(
theClass,
theAggr
)
Comentários
Parâmetros
theClass
O nome da classe que contém o mapa de interfacetheAggr
O nome da variável de membro a ser agregados.
Comentários
Essa macro é usada para informar a estrutura que a classe está usando um objeto agregado.Deve aparecer entre os BEGIN_INTERFACE_PART e END_INTERFACE_PART macros.Um objeto agregado é um objeto separado, derivado de IUnknown.Usando um agregado e o INTERFACE_AGGREGATE macro, você pode fazer todas as interfaces que suporta agregação parece ser diretamente suportado pelo objeto.O theAggr argumento é simplesmente o nome de uma variável de membro de sua classe que é derivada de IUnknown (direta ou indiretamente).Todos os INTERFACE_AGGREGATE macros devem seguir o INTERFACE_PART macros quando colocados em um mapa de interface.