TN039: implementação de automação 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.
Visão geral da interface IDispatch OLE
A interface de IDispatch é um conjunto de mídias por que os aplicativos expõe métodos e propriedades de forma que outros aplicativos como o Visual Basic, ou outros idiomas, podem utilizar os recursos de aplicativo. A parte mais importante desta interface é a função de IDispatch::Invoke . O usa “distribuição” MFC mapeiam para implementar IDispatch::Invoke. O mapa de expedição fornece informações sobre a implementação de MFC no layout ou “” na forma do CCmdTarget- classes derivadas, de modo que pode manipular diretamente as propriedades do objeto, ou chama funções de membro dentro de seu objeto para atender solicitações de IDispatch::Invoke .
Em geral, ClassWizard e o MFC cooperam para ocultar a maioria dos detalhes de automação OLE do programador de aplicativo. O programador concentra-se na funcionalidade real para expor no aplicativo e não se precisa se preocupar sobre o indicação subjacente.
Há casos, porém, onde é necessário entender o que o MFC está fazendo os bastidores. Essa observação tratará como a estrutura atribui DISPIDs às funções de membro e propriedades. O conhecimento dos usos MFC do algoritmo para atribuir DISPIDs é necessária apenas quando você precisa saber os IDs, como quando você cria uma biblioteca de “tipo” para os objetos do seu aplicativo.
Atribuição de DISPID MFC
Embora o usuário final de automação (um usuário do Visual Basic, por exemplo), veja os nomes reais das propriedades e dos métodos habilitados automação em seu código (como obj.ShowWindow), a implementação de IDispatch::Invoke não receberá os nomes reais. Por motivo da otimização, recebe DISPID, que é uma “cookie” mágica de 32 bits que descreve o método ou da propriedade que devem ser acessados. Esses valores de DISPID são retornados da implementação de IDispatch com outro método, IDispatch::GetIDsOfNameschamado. Um aplicativo cliente de automação GetIDsOfNames chama uma vez para cada membro ou a propriedade que pretende acessar, e os armazena em cachê para chamadas posteriores para IDispatch::Invoke. Dessa forma, a pesquisa cara de cadeia de caracteres é feita somente uma vez por uso de objeto, em vez de uma vez pela chamada de IDispatch::Invoke .
O MFC determina DISPIDs para cada método e a propriedade com base em duas coisas:
A distância da parte superior da distribuição (1 relativo)
A distância do mapa de expedição da maioria de classe derivada (0) parentes
DISPID é dividido em duas partes. LOWORD de DISPID contém o primeiro componente, a distância da parte superior da remessa. HIWORD contém a distância da maioria da classe derivada. Por exemplo:
class CDispPoint : public CCmdTarget
{
public:
short m_x, m_y;
...
DECLARE_DISPATCH_MAP()
...
};
class CDisp3DPoint : public CDispPoint
{
public:
short m_z;
...
DECLARE_DISPATCH_MAP()
...
};
BEGIN_DISPATCH_MAP(CDispPoint, CCmdTarget)
DISP_PROPERTY(CDispPoint, "x", m_x, VT_I2)
DISP_PROPERTY(CDispPoint, "y", m_y, VT_I2)
END_DISPATCH_MAP()
BEGIN_DISPATCH_MAP(CDisp3DPoint, CDispPoint)
DISP_PROPERTY(CDisp3DPoint, "z", m_z, VT_I2)
END_DISPATCH_MAP()
Como você pode ver, há duas classes, que expõe interfaces DE automação OLE. Uma dessas classes é derivado de outro e aproveita assim a funcionalidade da classe base, incluindo a parte de automação OLE (propriedades de “x” e “y” nesse caso).
O DISPIDMFC gerará o para a classe CDispPoint como segue:
property X (DISPID)0x00000001
property Y (DISPID)0x00000002
Como as propriedades não estão em uma classe base, HIWORD de DISPID sempre será zero (a distância da maioria de classe derivada para CDispPoint é zero).
O DISPIDMFC gerará o para a classe CDisp3DPoint como segue:
property Z (DISPID)0x00000001
property X (DISPID)0x00010001
property Y (DISPID)0x00010002
A propriedade de Z é determinada DISPID com HIWORD zero desde que é definida na classe que é exposta as propriedades, CDisp3DPoint. Como as propriedades X e Y são em uma classe base, HIWORD de DISPID sido 1, pois a classe na qual essas propriedades são definidas estão em uma distância de uma derivação da maioria da classe derivada.
Dica
LOWORD sempre é determinado pela posição no mapa, mesmo se houver entradas no mapa com DISPID explícito (consulte a próxima seção para obter informações sobre as versões de _ID de macros de DISP_PROPERTY e de DISP_FUNCTION ).
Recursos avançados do mapa de expedição MFC
Há vários recursos adicionais que ClassWizard não da suporte a esta versão do Visual C++. ClassWizard oferece suporte a DISP_FUNCTION, DISP_PROPERTY, e DISP_PROPERTY_EX que definem um método, uma propriedade variável de membro, e a obtenção/definem a propriedade da função de membro, respectivamente. Esses recursos são geralmente tudo o que é necessário para criar a maioria dos servidores de automação.
Os seguintes macros adicionais podem ser usados quando as macros suporte ClassWizard não são suficientes: DISP_PROPERTY_NOTIFY, e DISP_PROPERTY_PARAM.
DISP PROPERTY NOTIFY - descrição macro
DISP_PROPERTY_NOTIFY(
theClass,
pszName,
memberName,
pfnAfterSet,
vtPropType
)
Comentários
Parâmetros
theClass
Nome da classe.pszName
Nome externo da propriedade.memberName
Nome da variável de membro em que a propriedade é armazenada.pfnAfterSet
Nome da função de membro para chamar quando a propriedade é alterada.vtPropType
Um valor que especifica o tipo de propriedade.
Comentários
Esta macro é bem como DISP_PROPERTY, exceto que aceita um argumento adicional. O argumento adicional, pfnAfterSet, deve ser uma função de membro que não retorna nada e não usa nenhum parâmetro, “OnPropertyNotify nulo ()”. Será chamado depois que a variável de membro foi alterado.
DISP PROPERTY PARAM - descrição macro
DISP_PROPERTY_PARAM(
theClass,
pszName,
pfnGet,
pfnSet,
vtPropType,
vtsParams
)
Comentários
Parâmetros
theClass
Nome da classe.pszName
Nome externo da propriedade.memberGet
Nome da função de membro usada para obter a propriedade.memberSet
O nome da função de membro usado para definir a propriedade.vtPropType
Um valor que especifica o tipo de propriedade.vtsParams
Uma cadeia de caracteres de espaço VTS_ separados para cada parâmetro.
Comentários
Bem como a macro de DISP_PROPERTY_EX , esta macro define uma propriedade acessada com separado obtém e define as funções de membro. Esta macro, porém, permite que você especifica uma lista de parâmetros para a propriedade. Isso é útil para implementar as propriedades que são indexadas ou com parâmetros em alguma outra forma. Os parâmetros serão colocados sempre primeiro, seguido pelo novo valor da propriedade. Por exemplo:
DISP_PROPERTY_PARAM(CMyObject, "item", GetItem, SetItem, VT_DISPATCH, VTS_I2 VTS_I2)
corresponderia para obter e definir as funções de membro:
LPDISPATCH CMyObject::GetItem(short row, short col)
void CMyObject::SetItem(short row, short col, LPDISPATCH newValue)
DISP_XXXX_ID — descrições macro
DISP_FUNCTION_ID(
theClass,
pszName,
dispid,
pfnMember,
vtRetVal,
vtsParams
)
DISP_PROPERTY_ID(
theClass,
pszName,
dispid,
memberName,
vtPropType
)
DISP_PROPERTY_NOTIFY_ID(
theClass,
pszName,
dispid,
memberName,
pfnAfterSet,
vtPropType
)
DISP_PROPERTY_EX_ID(
theClass,
pszName,
dispid,
pfnGet,
pfnSet,
vtPropType
)
DISP_PROPERTY_PARAM_ID(
theClass,
pszName,
dispid,
pfnGet,
pfnSet,
vtPropType,
vtsParams
)
Comentários
Parâmetros
theClass
Nome da classe.pszName
Nome externo da propriedade.dispid
O DISPID fixo para a propriedade ou o método.pfnGet
Nome da função de membro usada para obter a propriedade.pfnSet
O nome da função de membro usado para definir a propriedade.memberName
O nome da variável de membro ao mapear para a propriedadevtPropType
Um valor que especifica o tipo de propriedade.vtsParams
Uma cadeia de caracteres de espaço VTS_ separados para cada parâmetro.
Comentários
Esses macros permitem que você especifique DISPID em vez de deixar o MFC o atribuirá automaticamente um. Esses macros avançados têm os mesmos nomes exceto se a ID é anexado ao nome da macro (por exemplo. DISP_PROPERTY_ID) e o ID são determinados pelo parâmetro especificado imediatamente depois do parâmetro de pszName . Consulte AFXDISP.H para obter mais informações sobre esses macros. As entradas de _ID devem ser colocadas no final da remessa. Afetará a geração automática de DISPID da mesma maneira que uma versão não de**_ID** de macro ( DISPIDs é determinado pela posição). Por exemplo:
BEGIN_DISPATCH_MAP(CDisp3DPoint, CCmdTarget)
DISP_PROPERTY(CDisp3DPoint, "y", m_y, VT_I2)
DISP_PROPERTY(CDisp3DPoint, "z", m_z, VT_I2)
DISP_PROPERTY_ID(CDisp3DPoint, "x", 0x00020003, m_x, VT_I2)
END_DISPATCH_MAP()
MFC O gerará DISPIDs da classe CDisp3DPoint como segue:
property X (DISPID)0x00020003
property Y (DISPID)0x00000002
property Z (DISPID)0x00000001
Especifique DISPID fixo é útil manter a compatibilidade com versões anteriores para uma interface anteriormente existente de distribuição, ou para implementar determinado sistema definiu os métodos ou propriedades (indicados com frequência por DISPIDnegativo, como a coleção de DISPID_NEWENUM ).
Recuperando a interface IDispatch para um COleClientItem
Muitos servidores oferecerão suporte à automação dentro de seus objetos do documento, junto com a funcionalidade OLE do servidor. Para obter acesso a essa interface de automação, é necessário acessar diretamente a variável de membro de COleClientItem::m_lpObject . O código a seguir recupera a interface de IDispatch para um objeto derivado de COleClientItem. Você pode incluir código a seguir em seu aplicativo se você encontrar essa funcionalidade necessária:
LPDISPATCH CMyClientItem::GetIDispatch()
{
ASSERT_VALID(this);
ASSERT(m_lpObject != NULL);
LPUNKNOWN lpUnk = m_lpObject;
Run(); // must be running
LPOLELINK lpOleLink = NULL;
if (m_lpObject->QueryInterface(IID_IOleLink,
(LPVOID FAR*)&lpOleLink) == NOERROR)
{
ASSERT(lpOleLink != NULL);
lpUnk = NULL;
if (lpOleLink->GetBoundSource(&lpUnk) != NOERROR)
{
TRACE0("Warning: Link is not connected!\n");
lpOleLink->Release();
return NULL;
}
ASSERT(lpUnk != NULL);
}
LPDISPATCH lpDispatch = NULL;
if (lpUnk->QueryInterface(IID_IDispatch, &lpDispatch)
!= NOERROR)
{
TRACE0("Warning: does not support IDispatch!\n");
return NULL;
}
ASSERT(lpDispatch != NULL);
return lpDispatch;
}
A interface de expedição retornada dessa função pode ser usada diretamente ou anexado a COleDispatchDriver para acesso do tipo seguro. Se você usá-la diretamente, certifique-se de que você chama seu membro de Versão quando através do ponteiro (com o destruidor de COleDispatchDriver faz isso por padrão).