Skip to content

Instantly share code, notes, and snippets.

@steveisok
Created March 25, 2019 18:43
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 steveisok/a04557c332dfaf6ba5415a6b426ec39d to your computer and use it in GitHub Desktop.
Save steveisok/a04557c332dfaf6ba5415a6b426ec39d to your computer and use it in GitHub Desktop.
using System;
using System.Reflection;
using System.Reflection.Emit;
namespace Crash
{
class MainClass
{
public static void Main(string[] args)
{
MainClass m = new MainClass();
m.RunTest();
}
void RunTest()
{
EventHandler handler;
MethodInfo info;
Delegate del = Delegate.CreateDelegate(
type: typeof(EventHandler),
target: this,
method: "Page_Load",
ignoreCase: true,
throwOnBindFailure: false);
info = del.Method;
IntPtr ptr = info.MethodHandle.GetFunctionPointer();
handler = (new CalliEventHandlerDelegateProxy(this, ptr, false)).Handler;
handler(this, new EventArgs());
}
void Page_Load(object sender, EventArgs e)
{
Console.WriteLine("Page Load Called");
}
}
// NOTE: This is lifted from refsrc System.Web as it is internal. This
// just helps shrink the test.
public class CalliEventHandlerDelegateProxy
{
private delegate void ParameterlessDelegate();
private delegate void ParameterfulDelegate(object sender, EventArgs e);
private IntPtr _functionPointer;
private Object _target;
private bool _argless;
public CalliEventHandlerDelegateProxy(Object target, IntPtr functionPointer, bool argless)
{
_argless = argless;
_target = target;
_functionPointer = functionPointer;
}
public void Callback(Object sender, EventArgs e)
{
if (_argless)
{
ParameterlessDelegate del = FastDelegateCreator<ParameterlessDelegate>.BindTo(_target, _functionPointer);
del();
}
else
{
ParameterfulDelegate del = FastDelegateCreator<ParameterfulDelegate>.BindTo(_target, _functionPointer);
del(sender, e);
}
}
public EventHandler Handler
{
get
{
return new EventHandler(Callback);
}
}
}
public static class FastDelegateCreator<TDelegate> where TDelegate : class
{
private static readonly Func<object, IntPtr, TDelegate> _factory = GetFactory();
// obj is the 'this' pointer (potentially null)
// method is RuntimeMethodHandle.GetFunctionPointer()
public static TDelegate BindTo(object obj, IntPtr method)
{
return _factory(obj, method);
}
// obj is the 'this' pointer (potentially null)
// method is the MethodInfo to bind to
public static TDelegate BindTo(object obj, MethodInfo method)
{
return BindTo(obj, method.MethodHandle.GetFunctionPointer());
}
// Assert needed since we'll be asked to create delegates to potentially private / protected methods
private static Func<object, IntPtr, TDelegate> GetFactory()
{
// All delegates have a .ctor(object @object, IntPtr method), but since we can't call this
// directly from C# we just need to LCG it.
ConstructorInfo delegateCtor = typeof(TDelegate).GetConstructor(new Type[] { typeof(object), typeof(IntPtr) });
// Define method FastCreateDelegate_X: (object, IntPtr) -> TDelegate
DynamicMethod dynamicMethod = new DynamicMethod(
name: "FastCreateDelegate_" + typeof(TDelegate).Name,
returnType: typeof(TDelegate),
parameterTypes: new Type[] { typeof(object), typeof(IntPtr) },
owner: typeof(FastDelegateCreator<TDelegate>),
skipVisibility: true);
// return new TDelegate(obj, method);
ILGenerator ilGen = dynamicMethod.GetILGenerator();
ilGen.Emit(OpCodes.Ldarg_0); // Stack contains ('this')
ilGen.Emit(OpCodes.Ldarg_1); // Stack contains ('this', 'method')
ilGen.Emit(OpCodes.Newobj, delegateCtor); // Stack contains (delegate)
ilGen.Emit(OpCodes.Ret);
return (Func<object, IntPtr, TDelegate>)dynamicMethod.CreateDelegate(typeof(Func<object, IntPtr, TDelegate>));
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment