TN038: implementação de IUnknown MFC/OLE
Dica
A nota técnica a seguir não foi atualizada desde que ela foi incluída pela primeira vez na documentação online.Como resultado, alguns procedimentos e tópicos podem estar incorretos ou expirados.Para obter as informações mais recentes, é recomendável que você procure o tópico de interesse no índice de documentação online.
No centro do OLE 2 está o "OLE Component Object Model", ou COM. COM define um padrão de como os objetos de cooperação se comunicam uns com os outros. Isso inclui os detalhes da aparência de um “objeto”, incluindo como os métodos são despachados em um objeto. COM também define uma classe base, da qual todas as classes compatíveis com COM são derivadas. Essa classe base é IUnknown. Embora a interface de IUnknown seja referida como uma classe de C++, COM não é específico para qualquer linguagem — pode ser implementado em C 2.0, PASCAL, ou em qualquer outra linguagem que possa oferecer suporte ao layout binário de um objeto COM.
O OLE refere-se a todas as classes derivadas de IUnknown como “interfaces.” Esta é uma distinção importante, já que uma “interface” como IUnknown não tem implementação. 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. É função do MFC implementar um comportamento padrão para programas MFC/C++.
Para entender a implementação de IUnknown do MFC, primeiro você deve compreender o que é esta interface. Uma versão simplificada de IUnknown é definida abaixo:
class IUnknown
{
public:
virtual HRESULT QueryInterface(REFIID iid, void** ppvObj) = 0;
virtual ULONG AddRef() = 0;
virtual ULONG Release() = 0;
};
Dica
Determinados detalhes de convenção de chamada, como __stdcall foram deixados de fora desta 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 você gostaria no C++. Em vez disso, os objetos COM são sempre referenciados por um ponteiro. Para liberar o objeto quando o proprietário não estiver mais usando, o membro do objeto Versão é chamado (em vez de usar a exclusão do operador, como seria feito para um objeto de 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 de Versão mantém uma contagem de referência no objeto — o objeto não é excluído até que a contagem de referência chegue ao valor de zero.
AddRef e Versão são relativamente simples de um ponto de vista de implementação. Eis 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 membro QueryInterface é um pouco mais interessante. Não é muito interessante ter um objeto cujas funções membro sejam AddRef e Versão — seria ótimo dizer o objeto para fazer mais coisas do que IUnknown fornece. Isso é onde QueryInterface é útil. Permite que você obtenha uma "interface" diferente no mesmo objeto. Essas interfaces geralmente são derivadas de IUnknown e adicionam outra funcionalidade com a adição de novas funções de membro. As interfaces COM nunca têm variáveis de membros declarados na interface e todas as funções de membro são declaradas como virtuais puras. Por exemplo,
class IPrintInterface : public IUnknown
{
public:
virtual void PrintObject() = 0;
};
Para obter um IPrintInterface se você tiver apenas IUnknown, faça a chamada QueryInterface usando IID de IPrintInterface. Um IID é um número de 128 bits que identifica unicamente a interface. Há um IID para cada interface que você ou o OLE define. Se pUnk for um ponteiro para um objeto IUnknown, o código para a recuperação de um IPrintInterface dele poderia 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 com suporte para a IPrintInterface e a interface IUnknown? Nesse caso é simples, uma vez que IPrintInterface seja diretamente derivado de IUnknown — implementando IPrintInterface, IUnknown terá 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 Version devem ser exatamente as mesmas que aquelas implementadas acima. CPrintObj::QueryInterface teria uma aparência semelhante a esta:
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 ao objeto; caso, contrário ocorrerá um erro. Observe também que um QueryInterface com êxito resulta em AddRefinferido. Naturalmente, você também precisará implementar CEditObj::Print. Isso é simples porque o IPrintInterface foi derivado diretamente de interface IUnknown. No entanto, se você quiser oferecer suporte a duas interfaces diferentes, ambas derivadas de IUnknown, consulte o seguinte:
class IEditInterface : public IUnkown
{
public:
virtual void EditObject() = 0;
};
Embora haja um número de diferentes maneiras de implementar uma classe que suporta IEditInterface e IPrintInterface, incluindo o uso de várias heranças de 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;
};
A implementação inteira é 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 maioria da implementação de IUnknown é colocada na classe CEditPrintObj em vez de duplicar o código em CEditPrintObj::CEditObj e em CEditPrintObj::CPrintObj. Isso reduz a quantidade de código e evita bugs. O ponto-chave aqui é aquele da interface de IUnknown que é possível chamar QueryInterface para recuperar qualquer interface que o objeto possa ter suporte, bem como de cada umas dessas interfaces que seja 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 ponto de retorno é usado (m_pParent). O ponteiro m_pParent é inicializado durante o construtor CEditPrintObj. Você implementará CEditPrintObj::CPrintObj::PrintObject e CEditPrintObj::CEditObj::EditObject também. Muito código foi adicionado para adicionar um recurso - a capacidade de editar o objeto. Felizmente, é muito incomum para interfaces ter apenas uma única função membro (embora isso aconteça) e, nesse caso, EditObject e PrintObject geralmente seriam combinados em uma única interface.
São muitas explicações e muitos códigos 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 à forma como as mensagens do Windows são empacotadas com mapas de mensagens. Esse recurso é chamado Mapas da Interface e será abordado na próxima seção.
Mapas de interface do MFC
O MFC/OLE inclui uma implementação de "Mapas de Interface" semelhante aos "Mapas de Mensagem" e aos "Mapas de Envios" do MFC em conceito e em execução. Os recursos principais dos mapas de interface MFC são:
Uma implementação padrão de IUnknown, compilada na classe CCmdTarget.
Manutenção de contagem de referência, modificada por AddRef e por Versão
Implementação orientada a dados de QueryInterface
Além disso, os mapeamentos da interface oferecem suporte aos seguintes recursos avançados:
Suporte para criar objetos COM agregável
Suporte para usar objetos agregados na implementação de um objeto COM
A implementação pode ser ligada e é extensível
Para obter mais informações sobre agregação, consulte o tópico Agregação.
O suporte do mapa de interface do MFC tem como raiz a classe CCmdTarget. CCmdTarget "tem um" contagem de referência assim como todas as funções membro associadas à implementação IUnknown (a contagem de referência para exemplo está em CCmdTarget). Para criar uma classe que suporta OLE COM, você deriva uma classe de CCmdTarget e usa várias macros bem como funções de membro de CCmdTarget para implementar as interfaces desejadas. A implementação do MFC usa classes aninhadas para definir cada implementação da interface de maneira muito parecida com a do exemplo anterior. Isso é facilitado com uma implementação padrão de IUnknown, bem como um número de macros que eliminam parte do código repetitivo.
Para implementar uma classe usando mapas de interface MFC
Derive uma classe de forma direta ou indireta de CCmdTarget.
Use a função DECLARE_INTERFACE_MAP na definição de classe derivada.
Para cada interface para qual você deseja oferecer suporte, use as macros BEGIN_INTERFACE_PART e END_INTERFACE_PART na definição de classe.
No arquivo de implementação, use as macros BEGIN_INTERFACE_MAP e END_INTERFACE_MAP para definir o mapa da interface da classe.
Para cada IID suportado, use a macro INTERFACE_PART entre as macros BEGIN_INTERFACE_MAP e END_INTERFACE_MAP para mapear o IID a uma "parte" específica da sua classe.
Implemente cada uma das classes aninhadas que representam as interfaces às quais você dá suporte.
Use a macro METHOD_PROLOGUE para acessar o pai, derivado do objeto CCmdTarget.
AddRef, Versão, e QueryInterface podem ser representantes da CCmdTarget implementação dessas funções (ExternalAddRef, ExternalRelease, e ExternalQueryInterface).
O exemplo de 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 diz à estrutura que essa classe terá um mapa personalizado de interface. 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 para diferenciar somente 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, Release e QueryInterface; portanto, você declara somente as funções específicas dessa interface: EditObject e PrintObject (a macro OLESTDMETHOD é usada para que _stdcall e palavras-chave virtuais sejam fornecidas conforme apropriado para a plataforma de destino).
Para implementar o mapa de interface para essa classe:
BEGIN_INTERFACE_MAP(CPrintEditObj, CCmdTarget)
INTERFACE_PART(CPrintEditObj, IID_IPrintInterface, PrintObj)
INTERFACE_PART(CPrintEditObj, IID_IEditInterface, EditObj)
END_INTERFACE_MAP()
Isso conecta o IID IID_IPrintInterface com m_CPrintObj e IID_IEditInterface com m_CEditObj respectivamente. A implementação CCmdTarget de QueryInterface (CCmdTarget::ExternalQueryInterface) usa esse mapa para retornar ponteiros para m_CPrintObj e m_CEditObj quando for solicitado. Não é necessário incluir uma entrada para IID_IUnknown; a estrutura usará a primeira interface no mapa (neste caso, m_CPrintObj) quando IID_IUnknown for solicitado.
Mesmo que a macro BEGIN_INTERFACE_PART tenha declarado automaticamente as funções AddRef, Versão e QueryInterface para você, ainda será preciso 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 será 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 (mas esse era o caso anteriormente no desenvolvimento de MFC/OLE), fica difícil definir pontos de interrupção quando uma macro gerencia mais de uma linha de código. Por esse motivo, esse código é expandido manualmente.
Usando a implementação de estrutura de mapas de mensagem, existem algumas coisas que não eram necessárias fazer:
Implementar QueryInterface
Implemente AddRef e versão
Declarar qualquer um desses métodos internos em ambas as interfaces
Além disso, a estrutura usa mapeamentos de mensagem internamente. Isso permite que você derive de uma classe de estrutura, ou seja COleServerDoc, que já tenha suporte para determinadas interfaces e forneça substituições ou adições às interfaces fornecidas pela estrutura. Você pode fazer isso porque o framework suporta totalmente que um mapa de interface seja herdado de uma classe base. Esse é o motivo pelo qual BEGIN_INTERFACE_MAP utiliza como o segundo parâmetro o nome da classe base.
Dica
Geralmente não é possível reutilizar a implementação das implementações internas do MFC das interfaces OLE simplesmente herdando a especialização inserida da interface de versão do MFC.Isso não é possível porque o uso da macro METHOD_PROLOGUE para obter acesso ao objeto derivado CCmdTargetde conteúdo implica em um deslocamento fixo do objeto inserido do objeto derivado CCmdTarget.Isso significa, por exemplo, que você não pode derivar um XMyAdviseSink inserido da implementação MFC em COleClientItem::XAdviseSink, porque XAdviseSink depende de estar em um deslocamento específico da parte superior do objeto COleClientItem.
Dica
Você pode, no entanto, representar para a implementação MFC para todas as funções nas quais desejar o comportamento padrão MFC.Isso é feito na implementação MFC de IOleInPlaceFrame (XOleInPlaceFrame) na classe de COleFrameHook (ela delega ao m_xOleInPlaceUIWindow para muitas funções).Este projeto foi escolhido para reduzir o tamanho do tempo de execução dos objetos que implementam muitas interfaces; ele elimina a necessidade de um ponteiro de retorno (como a maneira que m_pParent foi usado na seção anterior).
Mapas de agregação e de interface
Além de dar suporte a objetos COM autônomos, o MFC também oferece suporte a agregação. A agregação em si é um tópico muito complexo para discutir aqui; consulte o tópico Agregação para obter mais informações sobre a agregação. Essa observação simplesmente descreverá o suporte para a agregação compilada nos mapas da estrutura e da interface.
Há duas maneiras de usar a agregação: (1) usando um objeto COM que dê suporte à agregação e (2) implementando um objeto que pode ser agregado por outro. Esses recursos podem ser referenciados como “usando um objeto agregado” e “tornando um objeto agregável”. O MFC dá suporte a ambos.
Usando um objeto agregado
Para usar um objeto agregado, é preciso vincular de alguma maneira a agregação em mecanismo de QueryInterface. Em outras palavras, o objeto agregado deve se comportar como se fosse uma parte nativa de seu objeto. Assim, qual é a ligação dele com o mecanismo de mapa da interface do MFC? Além da macro INTERFACE_PART, onde um objeto aninhado é mapeado para uma IID, você também pode declarar um objeto agregado como parte de 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 classe IUnknown ou derivada), que deve ser integrada ao mecanismo do mapa da interface. Se o ponteiro for não NULO quando CCmdTarget::ExternalQueryInterface for chamado, a estrutura chamará automaticamente a função membro QueryInterface do objeto agregado, caso a IID solicitada não seja uma das IIDs nativas com suporte do próprio objeto CCmdTarget.
Para usar a macro INTERFACE_AGGREGATE
Declare um variável membro ( IUnknown*) que conterá um ponteiro para o objeto agregado.
Inclua uma macro INTERFACE_AGGREGATE no mapa de interface, que referencia a variável membro por nome.
Em algum ponto (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 membro NULL na implementação padrão de QueryInterface. OnCreateAggregates é um bom local para criar realmente seus objetos agregados. Você terá que chamar explicitamente se estiver criando o objeto fora de implementação MFC de COleObjectFactory. O motivo para criar agregações em CCmdTarget::OnCreateAggregates assim como o uso de CCmdTarget::GetControllingUnknown será aparente quando a criação de objetos agregáveis for abordada.
Essa técnica dará ao objeto todas as interfaces que o objeto agregado suporta além de suas interfaces nativas. Se você só quiser um subconjunto das interfaces às quais o agregado dá suporte, poderá substituir CCmdTarget::GetInterfaceHook. Isso permite que você tenha uma capacidade de ligação de nível muito baixo, semelhante a QueryInterface. Geralmente, você deseja todas as interfaces que a agregação suporta.
Tornando uma implementação de objeto agregável
Para que um objeto seja agregável, a implementação de AddRef, Versão e QueryInterface deverá delegar a um "controle desconhecido". Em outras palavras, para que ele faça parte do objeto, deverá delegar AddRef, Release e QueryInterface a um objeto diferente, também derivado de IUnknown. Esse “controle desconhecido” é fornecido para o objeto quando ele é criado, isto é, é fornecido para a implementação de COleObjectFactory. Implementar isso representa uma pequena quantidade de sobrecarga e, em alguns casos, isso não é desejável, portando o MFC torna isso opcional. Para ativar um objeto para que seja agregável, chame CCmdTarget::EnableAggregation do construtor do objeto.
Se o objeto também usar agregados, você também deverá garantir que o "controle desconhecido" correto seja passado para os objetos agregados. Esse ponteiro IUnknown geralmente é passado para o objeto quando a agregação é criada. Por exemplo, o parâmetro pUnkOuter é o "controle desconhecido" para objetos criados com CoCreateInstance. O ponteiro “desconhecido de controle” 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 seus agregados somente em uma substituição de CCmdTarget::OnCreateAggregates, onde o valor de retorno GetControllingUnknown é confiável, mesmo se criado da implementação de COleObjectFactory.
Também é importante que o objeto manipule a contagem de referências correta ao adicionar ou liberar contagens de referência artificiais. Para garantir que esse seja o caso, sempre chame ExternalAddRef e ExternalRelease em vez de InternalRelease e de InternalAddRef. É incomum chamar InternalRelease ou InternalAddRef em uma classe que ofereça suporte a agregação.
Material de Referência
O uso eficiente do OLE, como a definição de suas próprias interfaces ou substituição da implementação de layout das interfaces do OLE, requer uso do mecanismo subjacente do mapa da interface.
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 desejar oferecer suporte a agregação 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 a um IID (o primeiro argumento para QueryInterface)ppvObj
Um ponteiro para um IUnknown* (o segundo argumento para QueryInterface)
Comentários
Chame essa função em sua implementação de IUnknown para cada interface que sua classe implementa. Esta função fornece a implementação baseada em dados padrão de QueryInterface com base no mapa da interface do objeto. É necessário converter o valor de retorno para um HRESULT. Se o objeto for agregado, essa função chamará o "controle IUnknown" em vez de usar o mapa de interface local.
CCmdTarget::ExternalAddRef — descrição da função
DWORD ExternalAddRef();
Comentários
Chame essa função em sua implementação de IUnknown::AddRef para cada interface que sua classe implementa. O valor de retorno é a nova contagem de referência no objeto CCmdTarget. Se o objeto for agregado, essa função chamará o "controle IUnknown" em vez de manipular a contagem de referência local.
CCmdTarget::ExternalRelease — descrição da função
DWORD ExternalRelease();
Comentários
Chame essa função em sua implementação de IUnknown::Release para cada interface que sua classe implementa. O valor de retorno indica a nova contagem de referência no objeto. Se o objeto for agregado, essa função chamará o "controle IUnknown" em vez de manipular a contagem de referência local.
DECLARE_INTERFACE_MAP — descrição de macro
DECLARE_INTERFACE_MAP
Comentários
Use este macro em qualquer classe derivada de CCmdTarget que terá um mapa da interface. Usado de forma semelhante a DECLARE_MESSAGE_MAP. Essa invocação de macro deve ser colocada na definição de classe, geralmente em um arquivo de cabeçalho (. H). Uma classe com DECLARE_INTERFACE_MAP deve definir o mapa de interface no arquivo de implementação (.CPP) com macros de BEGIN_INTERFACE_MAP e de END_INTERFACE_MAP.
BEGIN_INTERFACE_PART e END_INTERFACE_PART — Descrições 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 essa classe implementa
Comentários
Para cada interface que a classe implementará, você precisa ter um par de BEGIN_INTERFACE_PART e END_INTERFACE_PART. Essas macros definem uma classe local derivada da interface OLE que você define, bem como um variável de membro inserida dessa classe. Os membros de AddRef, Versão e QueryInterface são declarados automaticamente. Você deve colocar as declarações para as outras funções de membro que são parte de interface que está sendo implementada (as declarações são colocadas entre BEGIN_INTERFACE_PART e macros de END_INTERFACE_PART ).
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á definida. Um "X" precederá automaticamente o nome. Esta convenção de nomenclatura é usada para evitar conflitos com classes globais do mesmo nome. Além disso, o nome do membro inserido, igual ao nome de localClass, exceto pelo 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 derivado de IAdviseSink, e um membro da classe na qual é declarado m_xMyAdviseSink.Note chamado:
Dica
As linhas que começam com STDMETHOD_ são essencialmente copiadas de OLE2.H e ligeiramente modificadas.Copiá-los de OLE2.H pode reduzir erros difíceis de resolver.
BEGIN_INTERFACE_MAP e END_INTERFACE_MAP — Descrições macro
BEGIN_INTERFACE_MAP(
theClass,
baseClass
)
END_INTERFACE_MAP
Comentários
Parâmetros
theClass
A classe na qual o mapa da interface deve ser definidobaseClass
A classe da qual theClass é derivada.
Comentários
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, existe uma ou mais chamadas de macro INTERFACE_PART . Para cada agregação usada pela classe, há uma chamada de macro INTERFACE_AGGREGATE .
INTERFACE_PART — descrição da macro
INTERFACE_PART(
theClass,
iid,
localClass
)
Comentários
Parâmetros
theClass
O nome da classe que contém a mapa da interface.iid
O IID que deve ser mapeado para a classe inserida.localClass
O nome da classe local (menos “X").
Comentários
Essa macro é usada entre a macro BEGIN_INTERFACE_MAP e a macro END_INTERFACE_MAP para cada interface que o objeto dará suporte. Permite que você mapeie uma IID para um membro da classe indicada por theClass e por localClass. O 'm_x' será adicionado ao localClass automaticamente. Observe que mais de uma IID pode ser associada com um único membro. Isso é muito útil quando você está implementando apenas uma interface “mais derivada" e também deseja fornecer todas as interfaces intermediárias. Um bom exemplo disso é a interface IOleInPlaceFrameWindow. Sua hierarquia tem esta aparência:
IUnknown
IOleWindow
IOleUIWindow
IOleInPlaceFrameWindow
Se um objeto implementar IOleInPlaceFrameWindow, um cliente poderá QueryInterface em qualquer uma das interfaces: IOleUIWindow, IOleWindow, ou IUnknown, além da interface "mais derivada" IOleInPlaceFrameWindow (aquela que você estiver realmente implementando). Para tratar isso você pode usar mais de uma macro de 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 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 tem IUnknown, pois ele 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 a mapa da interface.theAggr
O nome da variável de membro que deve ser agregada.
Comentários
Essa macro é usada para informar a estrutura que a classe está usando um objeto agregado. Ele deve aparecer entre as macros BEGIN_INTERFACE_PART e END_INTERFACE_PART. Um objeto de agregação é um objeto separado, derivado de IUnknown. Usando uma agregação e a macro de INTERFACE_AGGREGATE , você pode fazer todas as interfaces que suportam agregados parecerem ser suportadas pelo objeto diretamente. O argumento theAggr é simplesmente o nome de uma variável de membro da classe que é derivada de IUnknown (direta ou indiretamente). Todos os usuários ou grupos adicionados manualmente que requerem acesso ao projeto.INTERFACE_AGGREGATE INTERFACE_PART