Za komentarze: oto jak można to zrobić z CIL, który można wygenerować z C#.
Miałem nadzieję, że skorzystam z DynamicMethod
, ale nie uda mi się uzyskać tego działa bez tworzenia niestandardowego typu delegata w czasie wykonywania, więc musiałem użyć AssemblyBuilder
zamiast tego.
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++;
});
}
}
Po kompilacji program, użyj ILDASM aby go zdemontować i zastąpić definicję BadMethod
„s poprzez
.method private hidebysig static int32&
BadMethod() cil managed
{
ldsflda int32 Program::i
ret
}
ta zamienia ją w funkcję powracającego int32&
, która następujący kod następnie udaje się zadzwonić . Jedynym miejscem C# pozwala int32&
typów jest w parametrach funkcji (ref int
), więc aby wynik był użyteczny, użyłem funkcji wywołania zwrotnego, która przekazuje wartość zwracaną BadMethod
.
Interesujące, ale wyjątek wydaje się dość jasny - funkcja zwraca odwołanie do bajtu, a wywołanie nie obsługuje funkcji, które zwracają odwołania (dlaczego, nie wiem ... może na wypadek, gdyby coś na stosie było zwrócony). Czy ta metoda jest publiczna, może mogłaby być wywołana przez zarządzaną bibliotekę DLL C++? –
@ T.Kiley - zgodzili się, wyjątek jest jasny, tzn. "Nieobsługiwany". Ale moje pytanie brzmi: jak to obejść. Tak, może zrobi to DLL w C++, ale chciałbym pracować w C#, jeśli to możliwe. Zaktualizuję pytanie. Dzięki. – Les
Czy TypeDescriptors będzie warta posiadania efektu w. Wiem, że zapewniają dodatkowe możliwości metadanych w porównaniu do refleksji, jestem pewien, że tam też jest trochę rzeczy wywołujących. – brumScouse