剖析dynamic拼贴

我正在处理MSIL分析器,并遇到ICorProfilerCallback接口的ManagedToUnmanagedTransitionUnmanagedToManagedTransitioncallback问题。

我想要检索的是关于正在调用的方法的信息(它驻留的名称和模块名称)。

到目前为止,它工作正常。 直到所谓的dynamic拼写发生(详细描述在: http : //blogs.msdn.com/b/jonathanswift/archive/2006/10/03/dynamically-calling-an-unmanaged-dll-from-.net-_2800_c_23002900_ .aspx )

在这种情况下IMetaDataImport::GetPinvokeMap失败。 另外IMetaDataAssemblyImport::GetAssemblyProps返回“dynamic_pinvoke”作为程序集的名称。

 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" 

如何获取模块名称(.dll)和通过dynamicpinvoke被pinvoked的函数名称?

分析器API正在通过DllImportAttribute返回在托pipe代码中指定的元数据。 在使用Marshal.GetDelegateForFunctionPointer方法的“dynamicpinvoke”的情况下,模块和函数名称从未被指定为元数据并且不可用。 包含所需元数据的dynamic拼写声明的替代方法可能会避免此问题。 尝试使用System.Reflection.Emit API(如TypeBuilder.DefinePInvokeMethod)作为一个解决scheme。

下面是一个使用System.Reflection.Emit的例子,它可以与分析器API一起工作。

 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, string text, string caption, int options); static readonly Type[] MessageBoxArgTypes = new Type[] { typeof(IntPtr), typeof(string), typeof(string), typeof(int)}; [DllImport("kernel32.dll")] public static extern IntPtr LoadLibrary(string dllToLoad); [DllImport("kernel32.dll")] public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName); [DllImport("kernel32.dll")] public static extern bool FreeLibrary(IntPtr hModule); static MethodInfo BuildMessageBoxPInvoke(string module, string 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, MethodAttributes.Static | MethodAttributes.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(string[] args) { MessageBoxFunc func = CreateFunc(); func(IntPtr.Zero, "Hello World", "From C#", 0); } } } 

举几个例子来说明当前方法的问题。

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

没有从user32.dll导出的MessageBox函数。 它只包含MessageBoxA和MessageBoxW。 因为我们没有在DllImport属性中指定ExactSpelling = false ,并且我们的CharSet是Unicode,所以.Net也会searchuser32.dll,我们的入口点被添加了W.这意味着MessageBoxW实际上是我们调用的本地函数。 但是,GetPinvokeMap返回“MessageBox”作为函数名称(您的代码中的module_namevariables)。

现在让我们通过序号而不是名字来调用函数。 使用Windows SDK中的dumpbin程序:

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

2046是MessageBoxW的序号。 调整我们的DllImport声明来使用EntryPoint字段,我们得到:

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

这次GetPInvokeMap返回“#2046”。 我们可以看到,剖析器对被调用的本地函数的“名称”一无所知。

更进一步,被调用的本地代码可能甚至没有名称。 在下面的例子中,运行时在可执行内存中创build一个“添加”函数。 没有函数名称或库曾经与正在执行的本机代码相关联。

 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(string[] args) { AddFunc func = CreateFunc(); int value = func(10, 20); Console.WriteLine(value); } } }