Profilage d’un pinvoke dynamic

Je travaille sur le profileur MSIL et ManagedToUnmanagedTransition rencontré des problèmes avec les ManagedToUnmanagedTransition et UnmanagedToManagedTransition de l’interface ICorProfilerCallback .

Ce que je veux récupérer, c’est une information sur la méthode appelée (nom et nom du module dans lequel elle réside).

Jusqu’à présent, ça marchait bien. Jusqu’à ce que ce qu’on appelle un pinvoke dynamic s’est produit (décrit en détail à l’adresse: http://blogs.msdn.com/b/jonathanswift/archive/2006/10/03/dynamically-calling-an-unmanaged-dll-from-.net-_2800_c_23002900_ .aspx )

Dans ce scénario, IMetaDataImport::GetPinvokeMap échoue. Aussi, IMetaDataAssemblyImport::GetAssemblyProps renvoie “dynamic_pinvoke” comme nom de l’assembly.

 profiler_1_0->GetTokenAndMetaDataFromFunction(function_id, IID_IMetaDataImport, (IUnknown**) &imd_import, &md_token); imd_import->GetPinvokeMap(md_token, &mapping, module_name, buffer_size, &chars_read, &md_module_ref); // here the fail occurs profiler_1_0->GetTokenAndMetaDataFromFunction(function_id, IID_IMetaDataAssemblyImport, (IUnknown**) &imd_assembly_import, &md_token); imd_assembly_import->GetAssemblyFromScope(&md_assembly); imd_assembly_import->GetAssemblyProps(md_assembly, 0, 0, 0, assembly_name, buffer_size, &chars_read, 0, 0); // assembly_name is set to "dynamic_pinvoke" 

Comment obtenir un nom de module (.dll) et un nom de fonction étant appelé par pinvoke dynamic?

Les API du profileur renvoient les métadonnées spécifiées dans le code géré, normalement via DllImportAtsortingbute . Dans le cas du “Dynamic pinvoke” qui utilise la méthode Marshal.GetDelegateForFunctionPointer , les noms de module et de fonction n’ont jamais été spécifiés en tant que métadonnées et ne sont pas disponibles. Une approche alternative aux déclarations pinvoke dynamics qui incluent les métadonnées requirejses évitera probablement ce problème. Essayez d’utiliser les API System.Reflection.Emit telles que TypeBuilder.DefinePInvokeMethod comme solution unique.

Voici un exemple d’utilisation de System.Reflection.Emit qui fonctionne avec les API du profileur.

 using System; using System.Reflection.Emit; using System.Runtime.InteropServices; using System.Reflection; namespace DynamicCodeCSharp { class Program { [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] private delegate int MessageBoxFunc(IntPtr hWnd, ssortingng text, ssortingng caption, int options); static readonly Type[] MessageBoxArgTypes = new Type[] { typeof(IntPtr), typeof(ssortingng), typeof(ssortingng), typeof(int)}; [DllImport("kernel32.dll")] public static extern IntPtr LoadLibrary(ssortingng dllToLoad); [DllImport("kernel32.dll")] public static extern IntPtr GetProcAddress(IntPtr hModule, ssortingng procedureName); [DllImport("kernel32.dll")] public static extern bool FreeLibrary(IntPtr hModule); static MethodInfo BuildMessageBoxPInvoke(ssortingng module, ssortingng proc) { AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName(module), AssemblyBuilderAccess.Run); ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(module); TypeBuilder typeBuilder = moduleBuilder.DefineType(proc); typeBuilder.DefinePInvokeMethod(proc, module, proc, MethodAtsortingbutes.Static | MethodAtsortingbutes.PinvokeImpl, CallingConventions.Standard, typeof (int), MessageBoxArgTypes, CallingConvention.StdCall, CharSet.Auto); Type type = typeBuilder.CreateType(); return type.GetMethod(proc, BindingFlags.Static | BindingFlags.NonPublic); ; } static MessageBoxFunc CreateFunc() { MethodInfo methodInfo = BuildMessageBoxPInvoke("user32.dll", "MessageBox"); return (MessageBoxFunc)Delegate.CreateDelegate(typeof(MessageBoxFunc), methodInfo); } static void Main(ssortingng[] args) { MessageBoxFunc func = CreateFunc(); func(IntPtr.Zero, "Hello World", "From C#", 0); } } } 

Quelques exemples pour démontrer les problèmes avec l’approche actuelle.

 [DllImport("user32.dll", CharSet = CharSet.Unicode)] public static extern int MessageBox(IntPtr hWnd, ssortingng text, ssortingng caption, int options); static void Main(ssortingng[] args) { MessageBox(IntPtr.Zero, "Hello World", "From C#", 0); } 

Il n’y a pas de fonction MessageBox exscope à partir de user32.dll. Il ne contient que MessageBoxA et MessageBoxW. Comme nous n’avons pas spécifié ExactSpelling = false dans l’atsortingbut DllImport et que notre CharSet est Unicode, .Net recherchera également user32.dll pour notre point d’entrée ajouté à W. Cela signifie que MessageBoxW est en fait la fonction native que nous appelons. Cependant, GetPinvokeMap renvoie ‘MessageBox’ comme nom de fonction (variable nom_module dans votre code).

Invoquons maintenant la fonction via un nombre ordinal plutôt que le nom. Utilisation du programme dumpbin dans le SDK Windows:

 dumpbin /exports C:\Windows\SysWOW64\user32.dll ... 2046 215 0006FD3F MessageBoxW ... 

2046 est le nombre ordinal pour MessageBoxW. En ajustant notre déclaration DllImport pour utiliser le champ EntryPoint, nous obtenons:

 [DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "#2046")] public static extern int MessageBox(IntPtr hWnd, ssortingng text, ssortingng caption, int options); 

Cette fois, GetPInvokeMap renvoie “# 2046”. Nous pouvons voir que le profileur ne sait rien du «nom» de la fonction native invoquée.

En allant plus loin, le code natif appelé peut même ne pas avoir de nom. Dans l’exemple suivant, une fonction “Ajouter” est créée dans la mémoire exécutable au moment de l’exécution. Aucun nom de fonction ou bibliothèque n’a jamais été associé au code natif en cours d’exécution.

 using System; using System.Runtime.InteropServices; namespace DynamicCodeCSharp { class Program { [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate int AddFunc(int a, int b); [DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)] private static extern IntPtr VirtualAlloc(IntPtr addr, IntPtr size, int allocType, int protectType); const int MEM_COMMIT = 0x1000; const int MEM_RESERVE = 0x2000; const int PAGE_EXECUTE_READWRITE = 0x40; static readonly byte[] buf = { // push ebp 0x55, // mov ebp, esp 0x8b, 0xec, // mov eax, [ebp + 8] 0x8b, 0x45, 0x08, // add eax, [ebp + 8] 0x03, 0x45, 0x0c, // pop ebp 0x5d, // ret 0xc3 }; static AddFunc CreateFunc() { // allocate some executable memory IntPtr code = VirtualAlloc(IntPtr.Zero, (IntPtr)buf.Length, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); // copy our add function implementation into the memory Marshal.Copy(buf, 0, code, buf.Length); // create a delegate to this executable memory return (AddFunc)Marshal.GetDelegateForFunctionPointer(code, typeof(AddFunc)); } static void Main(ssortingng[] args) { AddFunc func = CreateFunc(); int value = func(10, 20); Console.WriteLine(value); } } }