我有一个由第三方提供的.Net库。我对其中一个类进行了反射,并找到了一个成员方法。签名是......
Byte& FooBar()
所以,我想通过反射调用这个方法并得到异常"反射调用不支持ByRef返回值。"
这是我尝试过的......
var strm = new TheirClass();
var t = strm.GetType();
var ms = t.GetMembers(
BindingFlags.Static|BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
foreach (var m in ms)
{
Debug.WriteLine(String.Format("Name: {0}: {1}", m.Name, m.ToString()));
// ...
// Name: FooBar: Byte& FooBar()
// ...
}
var meth = t.GetMethod("FooBar");
object returnValue = meth.Invoke(strm, new object[] { }); //throw exception
我尝试使用ref参数调用函数时提供参数,但没有区别。
我想在C#中解决这个异常。
答案 0 :(得分:5)
根据评论:这是如何从CIL完成的,可以从C#生成。
我希望使用DynamicMethod
,但如果不在运行时创建自定义委托类型,我就无法实现此功能,因此我需要使用AssemblyBuilder
代替。
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++;
});
}
}
编译完该程序后,使用ILDASM对其进行反汇编,并用
替换BadMethod
的定义
.method private hidebysig static int32&
BadMethod() cil managed
{
ldsflda int32 Program::i
ret
}
这将它变成一个返回int32&
的函数,然后以下代码将设法调用它。 C#允许int32&
类型的唯一位置是函数参数(ref int
),因此为了使结果可用,我使用了一个回调函数,它传递了BadMethod
的返回值。 / p>
答案 1 :(得分:0)
感谢“hvd”指点我正确的方向。经过一番思考,这就是我想出来的。它更简单,但如果你看到一个缺陷,请告诉我。
var theirObj = new TheirClass();
var t = theirObj.GetType();
var fooBar = t.GetMethod("FooBar"); // Byte& FooBar()
DynamicMethod dm = new DynamicMethod(
"MyFooBar",
MethodAttributes.Public | MethodAttributes.Static,
CallingConventions.Standard,
typeof(IntPtr),
new Type[] { typeof(TheirClass) },
typeof(TheirClass),
true);
var ilg = dm.GetILGenerator();
ilg.Emit(OpCodes.Ldarg_0);
ilg.Emit(OpCodes.Call, foobar);
ilg.Emit(OpCodes.Ret);
var del = dm.CreateDelegate(typeof(Func<TheirClass,IntPtr>));
var ret = (IntPtr)del.DynamicInvoke(theirObject);
byte[] buf = new byte[theirObj.FooBarSize()]; //no need for reflection/IL here
// not sure about the following, it works, but should it be inside an "unsafe" section?
Marshal.Copy(ret, buf, 0, buf.Length);
我在一个有问题的方法周围放了一个简单的包装器,IL并不关心我是否对待Byte&amp; amp;作为IntPtr。没有不安全的包装,我仍然不确定复制。但现在这很好。