c++ - Perfilado de un pinvoke dinámico
cil (1)
Las API de perfil devuelven metadatos especificados en el código administrado, normalmente a través de DllImportAttribute . En el caso del ''pinvoke dinámico'' que utiliza el método Marshal.GetDelegateForFunctionPointer , los nombres de los módulos y las funciones nunca se especificaron como metadatos y no estuvieron disponibles. Un enfoque alternativo a las declaraciones dinámicas de pinvoke que incluya los metadatos requeridos probablemente evitará este problema. Intente utilizar System.Reflection.Emit API como TypeBuilder.DefinePInvokeMethod como una solución.
Aquí hay un ejemplo que usa System.Reflection.Emit que funciona con las API del perfilador.
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);
}
}
}
Algunos ejemplos para demostrar los problemas con el enfoque actual.
[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);
}
No se exporta ninguna función MessageBox desde user32.dll. Solo contiene MessageBoxA y MessageBoxW. Como no especificamos ExactSpelling=false en el atributo DllImport y nuestro CharSet es Unicode, .Net también buscará user32.dll para nuestro punto de entrada con una W. Esto significa que MessageBoxW es, de hecho, la función nativa que estamos llamando. Sin embargo, GetPinvokeMap devuelve ''MessageBox'' como el nombre de la función (variable module_name en su código).
Ahora, en cambio, invoquemos la función a través del número ordinal en lugar del nombre. Usando el programa dumpbin en el SDK de Windows:
dumpbin /exports C:/Windows/SysWOW64/user32.dll
...
2046 215 0006FD3F MessageBoxW
...
2046 es el número ordinal para MessageBoxW. Ajustando nuestra declaración DllImport para usar el campo EntryPoint obtenemos:
[DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "#2046")]
public static extern int MessageBox(IntPtr hWnd, string text, string caption, int options);
Esta vez GetPInvokeMap devuelve "# 2046". Podemos ver que el generador de perfiles no sabe nada sobre el ''nombre'' de la función nativa que se invoca.
Yendo aún más allá, el código nativo que se está llamando puede que ni siquiera tenga un nombre. En el siguiente ejemplo, se crea una función ''Agregar'' en la memoria ejecutable en tiempo de ejecución. Ningún nombre de función o biblioteca ha sido asociado con el código nativo que se está ejecutando.
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);
}
}
}
Estoy trabajando en el generador de perfiles MSIL y ManagedToUnmanagedTransition
problemas con las devoluciones de llamada ManagedToUnmanagedTransition
y ICorProfilerCallback
interfaz ICorProfilerCallback
.
Lo que quiero recuperar es una información sobre el método que se llama (nombre y nombre del módulo en el que reside).
Hasta ahora estaba funcionando bien. Hasta que se produjera el denominado pinvoke dinámico (descrito en detalle en: http://blogs.msdn.com/b/jonathanswift/archive/2006/10/03/dynamically-calling-an-unmanaged-dll-from-.net-_2800_c_23002900_.aspx )
En este escenario, IMetaDataImport::GetPinvokeMap
falla. También IMetaDataAssemblyImport::GetAssemblyProps
devuelve "dynamic_pinvoke" como nombre del ensamblado.
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"
¿Cómo obtener un nombre de módulo (.dll) y un nombre de función pinvoked a través de pinvoke dinámico?