Per the comments: here is how it can be done from CIL, which can be generated from C#.
I was hoping to use a DynamicMethod
, but I cannot manage to get this working without creating a custom delegate type at runtime, so I needed to use AssemblyBuilder
instead.
using System;
using System.Reflection;
using System.Reflection.Emit;
public delegate void CallBadFunction(Delegate d, Callback c);
public delegate void Callback(ref int i);
static class Program
{
static int i;
static object BadMethod()
{
return i;
}
static MethodInfo GetBadMethod()
{
return typeof(Program).GetMethod("BadMethod", BindingFlags.Static | BindingFlags.NonPublic);
}
static void Main()
{
var badMethod = GetBadMethod();
var assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("-"), AssemblyBuilderAccess.Run);
var module = assembly.DefineDynamicModule("-");
var badDelegate = module.DefineType("BadDelegateType", TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Sealed, typeof(MulticastDelegate));
var badDelegateCtor = badDelegate.DefineConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, CallingConventions.Standard, new Type[] { typeof(object), typeof(IntPtr) });
badDelegateCtor.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);
var badDelegateInvoke = badDelegate.DefineMethod("Invoke", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.HideBySig, typeof(int).MakeByRefType(), Type.EmptyTypes);
badDelegateInvoke.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);
var badDelegateType = badDelegate.CreateType();
var method = module.DefineGlobalMethod("-", MethodAttributes.Public | MethodAttributes.Static, typeof(void), new[] { typeof(Delegate), typeof(Callback) });
var il = method.GetILGenerator();
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Castclass, badDelegate);
il.Emit(OpCodes.Callvirt, badDelegateInvoke);
il.Emit(OpCodes.Callvirt, typeof(Callback).GetMethod("Invoke"));
il.Emit(OpCodes.Ret);
module.CreateGlobalFunctions();
var callBadFunction = (CallBadFunction)Delegate.CreateDelegate(typeof(CallBadFunction), module.GetMethod("-"));
callBadFunction(badMethod.CreateDelegate(badDelegateType), (ref int i) =>
{
i++;
});
}
}
After compiling this program, use ILDASM to disassemble it, and replace BadMethod
's definition by
.method private hidebysig static int32&
BadMethod() cil managed
{
ldsflda int32 Program::i
ret
}
This turns it into a function returning int32&
, which the following code will then manage to call. The only location C# allows int32&
types is in function parameters (ref int
), so to make the result usable, I used a callback function, which gets passed the return value of BadMethod
.