62

私はMSILプロファイラーに取り組んでおり、インターフェイスのコールバックで問題が発生しManagedToUnmanagedTransitionましたUnmanagedToManagedTransitionICorProfilerCallback

取得したいのは、呼び出されているメソッドに関する情報(メソッドが存在する名前とモジュール名)です。

これまでのところ、正常に機能していました。いわゆる動的ピンボークが発生するまで(詳細は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)とダイナミックピンボークを介してピンボークされている関数の名前を取得するにはどうすればよいですか?

4

1 に答える 1

4

プロファイラー API は、通常はDllImportAttributeを介して、マネージ コードで指定されたメタデータを返します。Marshal.GetDelegateForFunctionPointerメソッドを使用する「動的 pinvoke」の場合、モジュールと関数の名前がメタデータとして指定されておらず、利用できませんでした。必要なメタデータを含む動的 pinvoke 宣言への代替アプローチは、おそらくこの問題を回避します。TypeBuilder.DefinePInvokeMethodなどの System.Reflection.Emit APIを 1 つの解決策として使用してみてください。

以下は、プロファイラー API で機能する System.Reflection.Emit を使用した例です。

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 は、W が追加されたエントリ ポイントの user32.dll も検索します。これは、MessageBoxW が実際に呼び出しているネイティブ関数であることを意味します。ただし、GetPinvokeMap は関数名 (コード内の module_name 変数) として「MessageBox」を返します。

代わりに、名前ではなく序数を介して関数を呼び出すことができます。Windows SDK で dumpbin プログラムを使用する:

dumpbin /exports C:\Windows\SysWOW64\user32.dll

...
2046  215 0006FD3F MessageBoxW
...

2046 は MessageBoxW の序数です。取得した EntryPoint フィールドを使用するように DllImport 宣言を調整します。

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

今度は GetPInvokeMap が「#2046」を返します。プロファイラーは、呼び出されるネイティブ関数の「名前」について何も知らないことがわかります。

さらに言えば、呼び出されるネイティブ コードには名前さえない場合があります。次の例では、'Add' 関数が実行時に実行可能メモリに作成されます。実行中のネイティブ コードに関数名またはライブラリが関連付けられたことはありません。

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);
        }
    }
}
于 2012-07-26T03:35:23.467 に答える