Created
August 18, 2012 06:55
-
-
Save kornman00/3384992 to your computer and use it in GitHub Desktop.
GetDelegateForFunctionPointer, kornman00
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 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; | |
} | |
}; | |
} |
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 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