Il commence assez facile. Le marshaller PinVoke appelle d'abord LoadLibrary et transmet le nom de la DLL que vous avez spécifié, la propriété DllImportAttribute.Value. Dans votre cas, user32.dll est déjà chargé car il est chargé par le programme d'amorçage .NET, son compteur de références est simplement incrémenté. Mais normalement, le chargeur Windows obtient la DLL mappée dans l'espace d'adressage du processus afin que les fonctions exportées puissent être appelées.
Ensuite, vous obtenez GetProcAddress pour obtenir l'adresse de la fonction à appeler, la propriété DllImportAttribute.EntryPoint. Le marshaller fait quelques essais, sauf si vous avez utilisé ExactSpelling. Un nom de fonction comme "foo" est testé de plusieurs manières possibles, foo et fooW ou fooA. Détails d'implémentation Nasty de Win32 liés à la différence entre les caractères Unicode et Ansi. La propriété CharSet est importante ici.
Maintenant j'ai besoin d'agiter les mains un peu parce que ça devient compliqué. Le marshaller construit un cadre de pile, définissant les arguments devant être transmis à la fonction exportée. Cela nécessite un code de bas niveau, soigneusement exclu des regards indiscrets. Prenons la valeur nominale qu'il effectue le genre de traductions que la classe Marshal prend en charge pour convertir entre les types gérés et non gérés. La propriété DllImportAttribute.CallingConvention est importante car elle détermine la valeur de l'argument à placer de sorte que la fonction appelée puisse la lire correctement.
Ensuite, il configure un gestionnaire d'exceptions SEH afin que les exceptions matérielles déclenchées par le code appelé puissent être interceptées et traduites en une exception gérée. Celui qui génère le plus commun, AccessViolationException. Et d'autres. Ensuite, il pousse un cookie spécial sur la pile pour indiquer que le code non géré est sur le point de commencer à utiliser la pile. Cela empêche le garbage collector de déformer les cadres de pile non gérés et d'interpréter les pointeurs qu'il trouve en tant que références d'objets gérés. Vous pouvez voir ce cookie dans la pile d'appels du débogueur, [Managed to Native Transition].
Ensuite, juste un appel indirect à l'adresse de fonction trouvée avec GetProcAddress(). Cela fait fonctionner le code non managé. Après l'appel, un nettoyage peut être nécessaire pour libérer la mémoire qui a été allouée pour transmettre les arguments non gérés. La valeur de retour peut devoir être convertie en une valeur gérée. Et c'est tout, en supposant que rien de méchant ne soit arrivé, l'exécution continue sur la prochaine instruction de code managé.
Vous pouvez créer une enveloppe autour du composant non géré, et mettre en œuvre l'interface 'IDisposable', pour un moyen plus propre de le désaffecter. Le garbage collector appelle la fonction définie dans cette interface pour éviter de "oublier" de nettoyer. –
Vous pouvez l'insérer dans IDisposable, mais veillez à appeler Dispose ou à remplacer le finaliseur. Il ne suffit pas d'implémenter IDispose, le GC n'appelle pas Dispose, il appelle Object.Finalize (voir http://stackoverflow.com/questions/45036/will-the-gc-call-idisposable-dispose-for-me) – pstrjds