juin
2007
Il peut être parfois utile de pouvoir appeler du code managé depuis un programme complétement natif.
Pour cela, plusieurs solutions dont une assez pratique à mettre en oeuvre : COM INTEROP.
On utilise CCW (COM Callable Wrappers) qui est un mécanisme de .Net pour permettre à un client COM d’accéder à des objets managés à travers un proxy COM qui encapsule l’assembly managée.
On peut voir ca comme un mécanisme de génération automatique de wrapper qui expose des interfaces COM et qui s’occupe de toutes les taches de conversions / marshalling.
Nous allons donc voir cela à travers un exemple extremement simpliste, soit la classe managée suivante :
public ref class MaClasse
{
private:
String ^c;
public:
MaClasse()
{
c = "hello world";
}
void Print()
{
Console::WriteLine(c);
}
};
}
J’ai donc créé une assembly (class library) qui s’appelle assembly et qui expose une classe MaCasse, avec un constructeur par défaut, et une méthode Print qui affiche une chaine. Très basique. (je l’ai faite en C++/CLI, mais on pourrait très bien la faire en C#, le principe est naturellement le meme).
Nous allons donc pouvoir procéder à la génération de notre wrapper. Pour ceci, il faut respecter les étapes suivantes :
1) signer l’assembly
Pour rappel, on utilise l’utilitaire sn.exe pour générer une paire de clé publique, clé privée :
sn.exe -k maCle.snk
Ensuite, il faut utiliser ce fichier.
Clic droit sur le projet -> propriétés -> linker -> advanced -> Key file -> et ici mettre le chemin d’accès vers le fichier maCle.snk.
Au prochain build, l’assembly sera signée.
2) Marquer l’assembly visible pour com
Pour ce faire, ouvrez le fichier AssemblyInfo.cpp, par défaut il a été généré :
[assembly:ComVisible(false)];
changez le en
[assembly:ComVisible(true)];
3) Enregistrer l’assembly pour com interop
en C#, il s’agit d’une option à cocher, en C++/CLI on va utiliser l’utilitaire regasm.exe.
regasm.exe assembly.dll /tlb:assembly.tlb /codebase
Cette ligne de commande va faire plusieurs choses : insérer des informations dans la base de registre (faites une recherche sur assembly.MaClasse vous verrez de quoi je parle, un prog-id et un clsid) ; générer un tlb et faire un lien vers le path de l’assembly (ce qui évite d’avoir à la mettre dans le GAC).
Vous voilà pret à écrire le code natif en C++ pour accéder au wrapper qui accèdera à notre objet CLI.
Soit ce programme :
int _tmain(int argc, _TCHAR* argv[])
{
DISPID dispid;
IDispatch *pDisp;
OLECHAR *methodName = L"Print";
CLSID clsID;
DISPPARAMS param;
param.cArgs=0;
param.rgvarg=NULL;
param.cNamedArgs=0;
param.rgdispidNamedArgs=NULL;
CoInitialize(NULL);
if (SUCCEEDED(::CLSIDFromProgID(L"assembly.MaClasse", &clsID)))
if (SUCCEEDED(CoCreateInstance(clsID, NULL, CLSCTX_ALL, IID_IDispatch, (void**)&pDisp)))
if (SUCCEEDED(pDisp->GetIDsOfNames(IID_NULL, &methodName,1, GetUserDefaultLCID(), &dispid)))
if (SUCCEEDED(pDisp->Invoke(dispid, IID_NULL, GetUserDefaultLCID(), DISPATCH_METHOD, ¶m, NULL, NULL, NULL)))
pDisp->Release();
CoUninitialize();
return 0;
}
On commence bien sur par appeler CoInitialize. Ensuite, on récupère le CLSID à partir du nom qui a été enregistré dans la base de registre par regasm.
Un fois qu’on a récupéré ce CLSID, on appelle CoCreateInstance pour obtenir un IDispatch et appeler le constructeur par défaut de notre classe.
On appelle ensuite GetIDsOfNames en lui passant en paramètre le nom de la méthode que l’on souhaite appeler.
Il ne reste plus qu’à invoquer la méthode en lui passant les paramètres adéquats. Je passe ici NULL en dernier et avant dernier paramètre car je n’ai pas besoin de récupérer d’exception ou d’erreur de paramètres. Je lui passe également NULL en avant avant dernière position car il s’agit du retour de la méthode, et comme il s’agit d’un void, il n’y a pas de retour.
Quand à ¶m, il s’agit de la construction des paramètres de la méthode ; il n’y en a pas non plus, je dois construire mon DISPPARAMS en lui disant qu’il n’y a pas de paramètres (cArgs=0) et en mettant le reste à null.
Enfin, on libère avec CoUninitialize();
Executez le programme, vous verrez sur la sortie console la phrase tant atendue : hello world
Voilà pour commencer avec COM Interop.
Commentaires récents
- [Tests] Arrange Act Assert, une traduction ? dans
- [UnitTest][C#] Tester une méthode privée dans
- Récupérer une valeur d’un contrôle depuis une autre Form / inclusions croisées et déclaration anticipée dans
- Tutoriel : Utiliser la ListBox et l’Isolated Storage dans vos applications Windows Phone 7 avec Silverlight dans
- Tutoriel : Utiliser la ListBox et l’Isolated Storage dans vos applications Windows Phone 7 avec Silverlight dans
Archives
- janvier 2013
- avril 2012
- janvier 2012
- juin 2011
- janvier 2011
- décembre 2010
- novembre 2010
- septembre 2010
- juin 2010
- mars 2010
- février 2010
- janvier 2010
- décembre 2009
- novembre 2009
- octobre 2009
- septembre 2009
- août 2009
- juillet 2009
- mai 2009
- avril 2009
- mars 2009
- janvier 2009
- décembre 2008
- novembre 2008
- octobre 2008
- septembre 2008
- août 2008
- juillet 2008
- juin 2008
- mai 2008
- avril 2008
- mars 2008
- février 2008
- janvier 2008
- décembre 2007
- novembre 2007
- octobre 2007
- septembre 2007
- août 2007
- juillet 2007
- juin 2007
- mai 2007