TN039 : implémentation d'Automation MFC/OLE
Remarque
La note technique suivante n'a pas été mise à jour depuis son inclusion initiale dans la documentation en ligne. Par conséquent, certaines procédures et rubriques peuvent être obsolètes ou incorrectes. Pour obtenir les informations les plus récentes, il est recommandé de rechercher l'objet qui vous intéresse dans l'index de la documentation en ligne.
Vue d’ensemble de l’interface OLE IDispatch
L’interface IDispatch
est le moyen par lequel les applications exposent des méthodes et des propriétés telles que d’autres applications telles que Visual BASIC ou d’autres langages, peuvent utiliser les fonctionnalités de l’application. La partie la plus importante de cette interface est la IDispatch::Invoke
fonction. MFC utilise des « mappages de répartition » pour implémenter IDispatch::Invoke
. La carte de répartition fournit les informations d’implémentation MFC sur la disposition ou la « forme » de vos CCmdTarget
classes dérivées, afin qu’elle puisse manipuler directement les propriétés de l’objet, ou appeler des fonctions membres au sein de votre objet pour répondre IDispatch::Invoke
aux demandes.
Pour la plupart, ClassWizard et MFC collaborent pour masquer la plupart des détails de l’automatisation OLE auprès du programmeur d’applications. Le programmeur se concentre sur les fonctionnalités réelles à exposer dans l’application et n’a pas à vous soucier de la plomberie sous-jacente.
Il existe toutefois des cas où il est nécessaire de comprendre ce que fait MFC en arrière-plan. Cette note traite de la façon dont l’infrastructure affecte des s DISPIDaux fonctions et propriétés membres. La connaissance de l’algorithme utilisé par MFC pour l’attribution de DISPIDn’est nécessaire que si vous devez connaître les ID, par exemple lorsque vous créez une « bibliothèque de types » pour les objets de votre application.
Affectation DISPID MFC
Bien que l’utilisateur final de l’automatisation (utilisateur Visual Basic, par exemple), voit les noms réels des propriétés et méthodes activées par automation dans leur code (par exemple, obj). ShowWindow), l’implémentation de IDispatch::Invoke
ne reçoit pas les noms réels. Pour des raisons d’optimisation, il reçoit un DISPID, qui est un « cookie magique » 32 bits qui décrit la méthode ou la propriété à accéder. Ces valeurs DISPID sont retournées à partir de l’implémentation par le IDispatch
biais d’une autre méthode, appelée IDispatch::GetIDsOfNames
. Une application cliente Automation appelle GetIDsOfNames
une fois pour chaque membre ou propriété auquel elle a l’intention d’accéder, et les met en cache pour les appels ultérieurs à IDispatch::Invoke
. De cette façon, la recherche de chaîne coûteuse n’est effectuée qu’une seule fois par objet, au lieu d’une seule fois par IDispatch::Invoke
appel.
MFC détermine les s DISPIDpour chaque méthode et propriété en fonction de deux éléments :
Distance du haut de la carte de répartition (1 relative)
Distance de la carte de répartition de la classe la plus dérivée (0 relative)
Le DISPID est divisé en deux parties. Le LOWORD du DISPID contient le premier composant, la distance entre le haut de la carte de répartition. HiWORD contient la distance entre la classe la plus dérivée. Par exemple :
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()
Comme vous pouvez le voir, il existe deux classes, qui exposent des interfaces d’automatisation OLE. L’une de ces classes est dérivée de l’autre et tire ainsi parti des fonctionnalités de la classe de base, y compris la partie Automation OLE (« x » et « y » dans ce cas).
MFC génère des DISPIDpour la classe CDispPoint comme suit :
property X (DISPID)0x00000001
property Y (DISPID)0x00000002
Étant donné que les propriétés ne se trouvent pas dans une classe de base, le HIWORD du DISPID est toujours égal à zéro (la distance de la classe la plus dérivée pour CDispPoint est égale à zéro).
MFC génère des S DISPIDpour la classe CDisp3DPoint comme suit :
property Z (DISPID)0x00000001
property X (DISPID)0x00010001
property Y (DISPID)0x00010002
La propriété Z reçoit un DISPID avec un HIWORD zéro, car elle est définie dans la classe qui expose les propriétés, CDisp3DPoint. Étant donné que les propriétés X et Y sont définies dans une classe de base, le HIWORD du DISPID est 1, car la classe dans laquelle ces propriétés sont définies est à une distance d’une dérivation de la classe la plus dérivée.
Remarque
Le LOWORD est toujours déterminé par la position dans la carte, même s’il existe des entrées dans la carte avec DISPID explicite (consultez la section suivante pour plus d’informations sur les versions _ID des DISP_PROPERTY
macros et DISP_FUNCTION
des macros).
Fonctionnalités avancées du mappage de répartition MFC
Il existe plusieurs fonctionnalités supplémentaires que ClassWizard ne prend pas en charge avec cette version de Visual C++. ClassWizard prend en charge DISP_FUNCTION
, DISP_PROPERTY
et DISP_PROPERTY_EX
qui définit respectivement une méthode, une propriété de variable membre et get/set. Ces fonctionnalités sont généralement nécessaires pour créer la plupart des serveurs Automation.
Les macros supplémentaires suivantes peuvent être utilisées lorsque les macros prises en charge par ClassWizard ne sont pas adéquates : DISP_PROPERTY_NOTIFY
et DISP_PROPERTY_PARAM
.
DISP_PROPERTY_NOTIFY — Description des macros
DISP_PROPERTY_NOTIFY(
theClass,
pszName,
memberName,
pfnAfterSet,
vtPropType)
Paramètres
theClass
Nom de la classe.
pszName
Nom externe de la propriété.
Membername
Nom de la variable membre dans laquelle la propriété est stockée.
pfnAfterSet
Nom de la fonction membre à appeler lorsque la propriété est modifiée.
vtPropType
Valeur spécifiant le type de la propriété.
Notes
Cette macro est beaucoup comme DISP_PROPERTY, sauf qu’elle accepte un argument supplémentaire. L’argument supplémentaire, pfnAfterSet, doit être une fonction membre qui ne retourne rien et ne prend aucun paramètre, 'void OnPropertyNotify()'. Elle sera appelée une fois la variable membre modifiée.
DISP_PROPERTY_PARAM — Macro Description
DISP_PROPERTY_PARAM(
theClass,
pszName,
pfnGet,
pfnSet,
vtPropType,
vtsParams)
Paramètres
theClass
Nom de la classe.
pszName
Nom externe de la propriété.
memberGet
Nom de la fonction membre utilisée pour obtenir la propriété.
memberSet
Nom de la fonction membre utilisée pour définir la propriété.
vtPropType
Valeur spécifiant le type de la propriété.
vtsParams
Chaîne d’espace séparé VTS_ pour chaque paramètre.
Notes
Tout comme la macro DISP_PROPERTY_EX, cette macro définit une propriété accessible avec des fonctions membres Get et Set distinctes. Toutefois, cette macro vous permet de spécifier une liste de paramètres pour la propriété. Cela est utile pour implémenter des propriétés indexées ou paramétrables d’une autre manière. Les paramètres sont toujours placés en premier, suivis de la nouvelle valeur de la propriété. Par exemple :
DISP_PROPERTY_PARAM(CMyObject, "item", GetItem, SetItem, VT_DISPATCH, VTS_I2 VTS_I2)
correspondrait à l’obtention et à la définition des fonctions membres :
LPDISPATCH CMyObject::GetItem(short row, short col)
void CMyObject::SetItem(short row, short col, LPDISPATCH newValue)
DISP_XXXX_ID — Descriptions des macros
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)
Paramètres
theClass
Nom de la classe.
pszName
Nom externe de la propriété.
dispid
DISPID fixe pour la propriété ou la méthode.
pfnGet
Nom de la fonction membre utilisée pour obtenir la propriété.
pfnSet
Nom de la fonction membre utilisée pour définir la propriété.
Membername
Nom de la variable membre à mapper à la propriété
vtPropType
Valeur spécifiant le type de la propriété.
vtsParams
Chaîne d’espace séparé VTS_ pour chaque paramètre.
Notes
Ces macros vous permettent de spécifier un DISPID au lieu de laisser MFC en attribuer automatiquement un. Ces macros avancées ont les mêmes noms, sauf que l’ID est ajouté au nom de la macro (par exemple , DISP_PROPERTY_ID) et que l’ID est déterminé par le paramètre spécifié juste après le paramètre pszName . Voir AFXDISP. H pour plus d’informations sur ces macros. Les entrées _ID doivent être placées à la fin de la carte de répartition. Ils affectent la génération DISPID automatique de la même façon qu’une version non _ID de la macro (les s DISPIDsont déterminés par position). Par exemple :
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 génère des DISPID pour la classe CDisp3DPoint comme suit :
property X (DISPID)0x00020003
property Y (DISPID)0x00000002
property Z (DISPID)0x00000001
La spécification d’un DISPID fixe est utile pour maintenir la compatibilité descendante à une interface de distribution existante précédemment, ou pour implémenter certaines méthodes ou propriétés définies par le système (généralement indiquées par un DISPID négatif, tel que la collection DISPID_NEWENUM).
Récupération de l’interface IDispatch pour un COleClientItem
De nombreux serveurs prennent en charge l’automatisation au sein de leurs objets de document, ainsi que les fonctionnalités du serveur OLE. Pour accéder à cette interface Automation, il est nécessaire d’accéder directement à la COleClientItem::m_lpObject
variable membre. Le code ci-dessous récupère l’interface IDispatch
d’un objet dérivé de COleClientItem
. Vous pouvez inclure le code ci-dessous dans votre application si vous trouvez cette fonctionnalité nécessaire :
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;
}
L’interface de distribution retournée à partir de cette fonction peut ensuite être utilisée directement ou attachée à un COleDispatchDriver
accès de type sécurisé. Si vous l’utilisez directement, assurez-vous d’appeler son Release
membre lorsque vous utilisez le pointeur (le COleDispatchDriver
destructeur effectue cette opération par défaut).