Skip to content

Instantly share code, notes, and snippets.

@breezhang
Created October 6, 2013 09:24
Show Gist options
  • Save breezhang/6851641 to your computer and use it in GitHub Desktop.
Save breezhang/6851641 to your computer and use it in GitHub Desktop.
Marshal --> ( Marshal.GetFunctionPointerForDelegate GetDelegateForFunctionPointer)
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;
}
};
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;
}
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