Skip to content

Instantly share code, notes, and snippets.

@kornman00
Created August 18, 2012 06:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kornman00/3384992 to your computer and use it in GitHub Desktop.
Save kornman00/3384992 to your computer and use it in GitHub Desktop.
GetDelegateForFunctionPointer, kornman00
using System;
using System.Linq;
using Contracts = System.Diagnostics.Contracts;
using Contract = System.Diagnostics.Contracts.Contract;
using Reflect = System.Reflection;
using Interop = System.Runtime.InteropServices;
namespace KSoft.Reflection
{
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, Interop.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 != Interop.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 Reflect.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(Reflect.Emit.OpCodes.Ldarg, x);
// Generate the IL for Calli's entry pointer (pushed to the stack)
if (Environment.Is64BitProcess)
il.Emit(Reflect.Emit.OpCodes.Ldc_I8, ptr.ToInt64());
else
il.Emit(Reflect.Emit.OpCodes.Ldc_I4, ptr.ToInt32());
il.EmitCalli(Reflect.Emit.OpCodes.Calli, call_conv, ret_type, param_types);
il.Emit(Reflect.Emit.OpCodes.Ret);
return invoke.CreateDelegate(type) as T;
}
};
}
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace KSoft.Reflection.Test
{
using MessageBoxDelegateGeneric = Func<IntPtr, string, string, uint, int>;
[TestClass]
public partial class ReflectUtilTest : BaseTestClass
{
delegate int MessageBoxDelegate(IntPtr hWnd, string lpText, string lpCaption, uint uType);
[TestMethod]
public void ReflectUtil_GetDelegateForFunctionPointerTest()
{
var module = LowLevel.Windows.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 = LowLevel.Windows.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);
LowLevel.Windows.FreeLibrary(module);
}
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment