Ответ 1
API-интерфейсы профилировщика возвращают метаданные, указанные в управляемом коде, обычно через DllImportAttribute. В случае "динамического pinvoke", который использует метод Marshal.GetDelegateForFunctionPointer, имена модулей и функций никогда не указывались как метаданные и не были доступны. Альтернативный подход к динамическим объявлениям пинвокей, который включает необходимые метаданные, вероятно, позволит избежать этой проблемы. Попробуйте использовать API-интерфейсы System.Reflection.Emit, такие как TypeBuilder.DefinePInvokeMethod как одно из решений.
Вот пример использования 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);
}
Функция MessageBox не экспортируется из user32.dll. Он содержит только MessageBoxA и MessageBoxW. Поскольку мы не указали ExactSpelling = false в атрибуте DllImport, а наш CharSet - Unicode,.Net также будет искать user32.dll для нашей точки входа прилагается к W. Это означает, что MessageBoxW на самом деле является нативной функцией, которую мы вызываем. Однако GetPinvokeMap возвращает "MessageBox" как имя функции (переменная module_name в вашем коде).
Теперь вместо этого вызовите функцию по порядковому номеру, а не по имени. Использование программы dumpbin в SDK Windows:
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". Мы можем видеть, что профилировщик ничего не знает о "имени" вызываемой нативной функции.
Идя еще дальше, нативный код, который вызывается, может даже не иметь имени. В следующем примере функция "Добавить" создается в исполняемой памяти во время выполнения. Никакое имя функции или библиотека никогда не ассоциировались с исполняемым кодом.
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);
}
}
}