Created
October 6, 2013 09:24
-
-
Save breezhang/6851641 to your computer and use it in GitHub Desktop.
Marshal --> ( Marshal.GetFunctionPointerForDelegate GetDelegateForFunctionPointer)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static partial class Util | |
{ | |
const string kDelegateInvokeMethodName = "Invoke"; | |
// http://www.codeproject.com/Tips/441743/A-look-at-marshalling-delegates-in-NET | |
public static T GetDelegateForFunctionPointer<T>(IntPtr ptr, System.Runtime.InteropServices.CallingConvention call_conv) | |
where T : class | |
{ | |
Contract.Requires<ArgumentException>(typeof(T).IsSubclassOf(typeof(Delegate))); | |
Contract.Requires<ArgumentNullException>(ptr != IntPtr.Zero); | |
Contract.Requires<ArgumentException>(call_conv != System.Runtime.InteropServices.CallingConvention.ThisCall, | |
"TODO: ThisCall's require a different implementation"); | |
Contract.Ensures(Contract.Result<T>() != null); | |
var type = typeof(T); | |
var method = type.GetMethod(kDelegateInvokeMethodName); | |
var ret_type = method.ReturnType; | |
var param_types = (from param in method.GetParameters() | |
select param.ParameterType) | |
.ToArray(); | |
var invoke = new System.Reflection.Emit.DynamicMethod(kDelegateInvokeMethodName, ret_type, param_types, | |
typeof(Delegate)); | |
var il = invoke.GetILGenerator(); | |
// Generate IL for loading all the args by index | |
// TODO: IL has Ldarg_0 to Ldarg_3...do these provide any tangible perf benefits? | |
for (int x = 0; x < param_types.Length; x++) | |
il.Emit(System.Reflection.Emit.OpCodes.Ldarg, x); | |
// Generate the IL for Calli's entry pointer (pushed to the stack) | |
if (Environment.Is64BitProcess) | |
il.Emit(System.Reflection.Emit.OpCodes.Ldc_I8, ptr.ToInt64()); | |
else | |
il.Emit(System.Reflection.Emit.OpCodes.Ldc_I4, ptr.ToInt32()); | |
il.EmitCalli(System.Reflection.Emit.OpCodes.Calli, call_conv, ret_type, param_types); | |
il.Emit(System.Reflection.Emit.OpCodes.Ret); | |
return invoke.CreateDelegate(type) as T; | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static IntPtr GetFunctionPointerForDelegate<T>(T delegateCallback, out object binder) | |
where T : class | |
{ | |
var del = delegateCallback as Delegate; | |
IntPtr result; | |
try | |
{ | |
result = Marshal.GetFunctionPointerForDelegate(del); | |
binder = del; | |
} | |
catch (ArgumentException) // generic type delegate | |
{ | |
var delegateType = typeof (T); | |
var method = delegateType.GetMethod("Invoke"); | |
var returnType = method.ReturnType; | |
var paramTypes = | |
method | |
.GetParameters() | |
.Select((x) => x.ParameterType) | |
.ToArray(); | |
// builder a friendly name for our assembly, module, and proxy type | |
var nameBuilder = new StringBuilder(); | |
nameBuilder.Append(delegateType.Name); | |
foreach (var pType in paramTypes) | |
{ | |
nameBuilder | |
.Append("`") | |
.Append(pType.Name); | |
} | |
var name = nameBuilder.ToString(); | |
// check if we've previously proxied this type before | |
var proxyAssemblyExist = | |
AppDomain | |
.CurrentDomain | |
.GetAssemblies() | |
.FirstOrDefault((x) => x.GetName().Name == name); | |
Type proxyType; | |
if (proxyAssemblyExist == null) | |
{ | |
/// create a proxy assembly | |
var proxyAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly( | |
new AssemblyName(name), | |
AssemblyBuilderAccess.Run | |
); | |
var proxyModule = proxyAssembly.DefineDynamicModule(name); | |
// begin creating the proxy type | |
var proxyTypeBuilder = proxyModule.DefineType(name, | |
TypeAttributes.AutoClass | | |
TypeAttributes.AnsiClass | | |
TypeAttributes.Sealed | | |
TypeAttributes.Public, | |
typeof (MulticastDelegate) | |
); | |
// implement the basic methods of a delegate as the compiler does | |
var methodAttributes = | |
MethodAttributes.Public | | |
MethodAttributes.HideBySig | | |
MethodAttributes.NewSlot | | |
MethodAttributes.Virtual; | |
proxyTypeBuilder | |
.DefineConstructor( | |
MethodAttributes.FamANDAssem | | |
MethodAttributes.Family | | |
MethodAttributes.HideBySig | | |
MethodAttributes.RTSpecialName, | |
CallingConventions.Standard, | |
new Type[] {typeof (object), typeof (IntPtr)}) | |
.SetImplementationFlags( | |
MethodImplAttributes.Runtime | | |
MethodImplAttributes.Managed | |
); | |
proxyTypeBuilder | |
.DefineMethod( | |
"BeginInvoke", | |
methodAttributes, | |
typeof (IAsyncResult), | |
paramTypes) | |
.SetImplementationFlags( | |
MethodImplAttributes.Runtime | | |
MethodImplAttributes.Managed); | |
proxyTypeBuilder | |
.DefineMethod( | |
"EndInvoke", | |
methodAttributes, | |
null, | |
new Type[] {typeof (IAsyncResult)}) | |
.SetImplementationFlags( | |
MethodImplAttributes.Runtime | | |
MethodImplAttributes.Managed); | |
proxyTypeBuilder | |
.DefineMethod( | |
"Invoke", | |
methodAttributes, | |
returnType, | |
paramTypes) | |
.SetImplementationFlags( | |
MethodImplAttributes.Runtime | | |
MethodImplAttributes.Managed); | |
// create & wrap an instance of the proxy type | |
proxyType = proxyTypeBuilder.CreateType(); | |
} | |
else | |
{ | |
// pull the type from an existing proxy assembly | |
proxyType = proxyAssemblyExist.GetType(name); | |
} | |
// marshal and bind the proxy so the pointer doesn't become invalid | |
var repProxy = Delegate.CreateDelegate(proxyType, del.Target, del.Method); | |
result = Marshal.GetFunctionPointerForDelegate(repProxy); | |
binder = Tuple.Create(del, repProxy); | |
} | |
return result; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Linq; | |
using System.Reflection; | |
using System.Reflection.Emit; | |
using System.Runtime.InteropServices; | |
using System.Text; | |
using Xunit; | |
namespace Class1sharp | |
{ | |
public class Program | |
{ | |
[System.Flags] | |
private enum LoadLibraryFlags : uint | |
{ | |
DONT_RESOLVE_DLL_REFERENCES = 0x00000001, | |
LOAD_IGNORE_CODE_AUTHZ_LEVEL = 0x00000010, | |
LOAD_LIBRARY_AS_DATAFILE = 0x00000002, | |
LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 0x00000040, | |
LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020, | |
LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008 | |
} | |
[UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.StdCall, SetLastError = true)] | |
private delegate void ManagedTypeSignature(uint a, uint b); | |
[DllImport("user32.dll", CharSet = CharSet.Auto)] | |
private static extern int MessageBox(IntPtr hWnd, String text, String caption, int options); | |
[DllImport("kernel32.dll")] | |
private static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hReservedNull, LoadLibraryFlags dwFlags); | |
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)] | |
private static extern IntPtr LoadLibrary(string lpFileName); | |
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)] | |
internal static extern IntPtr GetProcAddress(IntPtr hModule, string procName); | |
[DllImport("kernel32.dll", SetLastError = true)] | |
[return: MarshalAs(UnmanagedType.Bool)] | |
static extern bool FreeLibrary(IntPtr hModule); | |
internal delegate int DllRegisterServerInvoker(); | |
public static IntPtr GetFunctionPointerForDelegate<T>(T delegateCallback, out object binder) | |
where T : class | |
{ | |
var del = delegateCallback as Delegate; | |
IntPtr result; | |
try | |
{ | |
result = Marshal.GetFunctionPointerForDelegate(del); | |
binder = del; | |
} | |
catch (ArgumentException) // generic type delegate | |
{ | |
var delegateType = typeof (T); | |
var method = delegateType.GetMethod("Invoke"); | |
var returnType = method.ReturnType; | |
var paramTypes = | |
method | |
.GetParameters() | |
.Select((x) => x.ParameterType) | |
.ToArray(); | |
// builder a friendly name for our assembly, module, and proxy type | |
var nameBuilder = new StringBuilder(); | |
nameBuilder.Append(delegateType.Name); | |
foreach (var pType in paramTypes) | |
{ | |
nameBuilder | |
.Append("`") | |
.Append(pType.Name); | |
} | |
var name = nameBuilder.ToString(); | |
// check if we've previously proxied this type before | |
var proxyAssemblyExist = | |
AppDomain | |
.CurrentDomain | |
.GetAssemblies() | |
.FirstOrDefault((x) => x.GetName().Name == name); | |
Type proxyType; | |
if (proxyAssemblyExist == null) | |
{ | |
/// create a proxy assembly | |
var proxyAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly( | |
new AssemblyName(name), | |
AssemblyBuilderAccess.Run | |
); | |
var proxyModule = proxyAssembly.DefineDynamicModule(name); | |
// begin creating the proxy type | |
var proxyTypeBuilder = proxyModule.DefineType(name, | |
TypeAttributes.AutoClass | | |
TypeAttributes.AnsiClass | | |
TypeAttributes.Sealed | | |
TypeAttributes.Public, | |
typeof (MulticastDelegate) | |
); | |
// implement the basic methods of a delegate as the compiler does | |
var methodAttributes = | |
MethodAttributes.Public | | |
MethodAttributes.HideBySig | | |
MethodAttributes.NewSlot | | |
MethodAttributes.Virtual; | |
proxyTypeBuilder | |
.DefineConstructor( | |
MethodAttributes.FamANDAssem | | |
MethodAttributes.Family | | |
MethodAttributes.HideBySig | | |
MethodAttributes.RTSpecialName, | |
CallingConventions.Standard, | |
new Type[] {typeof (object), typeof (IntPtr)}) | |
.SetImplementationFlags( | |
MethodImplAttributes.Runtime | | |
MethodImplAttributes.Managed | |
); | |
proxyTypeBuilder | |
.DefineMethod( | |
"BeginInvoke", | |
methodAttributes, | |
typeof (IAsyncResult), | |
paramTypes) | |
.SetImplementationFlags( | |
MethodImplAttributes.Runtime | | |
MethodImplAttributes.Managed); | |
proxyTypeBuilder | |
.DefineMethod( | |
"EndInvoke", | |
methodAttributes, | |
null, | |
new Type[] {typeof (IAsyncResult)}) | |
.SetImplementationFlags( | |
MethodImplAttributes.Runtime | | |
MethodImplAttributes.Managed); | |
proxyTypeBuilder | |
.DefineMethod( | |
"Invoke", | |
methodAttributes, | |
returnType, | |
paramTypes) | |
.SetImplementationFlags( | |
MethodImplAttributes.Runtime | | |
MethodImplAttributes.Managed); | |
// create & wrap an instance of the proxy type | |
proxyType = proxyTypeBuilder.CreateType(); | |
} | |
else | |
{ | |
// pull the type from an existing proxy assembly | |
proxyType = proxyAssemblyExist.GetType(name); | |
} | |
// marshal and bind the proxy so the pointer doesn't become invalid | |
var repProxy = Delegate.CreateDelegate(proxyType, del.Target, del.Method); | |
result = Marshal.GetFunctionPointerForDelegate(repProxy); | |
binder = Tuple.Create(del, repProxy); | |
} | |
return result; | |
} | |
public delegate int MessageBoxDelegate(IntPtr hWnd, string lpText, string lpCaption, uint uType); | |
public delegate int Posy(int a, int b); | |
[Fact] | |
public static void Main() | |
{ | |
// let's get a managed method and convert it to an int: | |
var managedMethod = new ManagedTypeSignature(MyManagedMethod); | |
var untypedManagedMethod = (Delegate) managedMethod; | |
IntPtr nativeThunk = Marshal.GetFunctionPointerForDelegate(untypedManagedMethod); | |
// nativeThunk can now be called by C++ using the following typedef: | |
// void (__stdcall *NativeTypeSignature) ( uint32_t a, uint32_t b ); | |
// now let's turn it back into a function: | |
Delegate untypedNativeMethod = Marshal.GetDelegateForFunctionPointer(nativeThunk, typeof (ManagedTypeSignature)); | |
var managedMethod2 = (ManagedTypeSignature) untypedNativeMethod; | |
// let's call it, just to be sure: | |
managedMethod2(37, 4); | |
Posy m = (i, i1) => i + i1; | |
var tx= Marshal.GetFunctionPointerForDelegate(m); | |
//var ptr = GetProcAddress(LoadLibraryA("user32.dll"), "MessageBoxA"); | |
//var MessageBoxA = | |
// GetDelegateForFunctionPointer<Func<IntPtr, string, string, | |
// int, int>>(ptr, CallingConvention.StdCall); | |
//MessageBoxA(IntPtr.Zero, "Hello world", "Test", 0); | |
} | |
private static void MyManagedMethod(uint a, uint b) | |
{ | |
Console.Write("{0} + {1} = {2}", a, b, a + b); | |
} | |
[Fact] | |
public void Testd() | |
{ | |
//var module = LoadLibrary("user32.dll"); | |
//// Can't use MessageBoxW, seems to implicitly marshal to UTF8? Could have sworn .NET strings were internally UTF16 | |
//// Then again, my system is setup for en-us... | |
//var proc_ptr = GetProcAddress(module, "MessageBoxA"); | |
//const System.Runtime.InteropServices.CallingConvention call_conv = | |
// System.Runtime.InteropServices.CallingConvention.Winapi; | |
//var msg_box = Util.GetDelegateForFunctionPointer<MessageBoxDelegate>(proc_ptr, call_conv); | |
//msg_box(IntPtr.Zero, "Hello World", "Test1", 0); | |
//var msg_box_gen = Util.GetDelegateForFunctionPointer<MessageBoxDelegateGeneric>(proc_ptr, call_conv); | |
//msg_box_gen(IntPtr.Zero, "Goodbye World", "Test2", 0); | |
//Func<int, int> c = (x) =>x; | |
//IntPtr functionPointerForDelegate = Marshal.GetFunctionPointerForDelegate(c); | |
object ttys; | |
IntPtr pointerForDelegate = GetFunctionPointerForDelegate<Func<int, int>>(i => 1, out ttys); | |
Console.Write(pointerForDelegate); | |
Marshal.GetDelegateForFunctionPointer() | |
// FreeLibrary(module); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment