我可以对 RealProxy 实例使用反射吗?
Can I use reflection with RealProxy instances?
我很确定我在某处遗漏了一些限制或警告,但这是我的情况。假设我有一个 class 我想为其代理,如下所示:
public class MyList : MarshalByRefObject, IList<string>
{
private List<string> innerList;
public MyList(IEnumerable<string> stringList)
{
this.innerList = new List<string>(stringList);
}
// IList<string> implementation omitted for brevity.
// For the sake of this exercise, assume each method
// implementation merely passes through to the associated
// method on the innerList member variable.
}
我想为那个 class 创建一个代理,这样我就可以拦截方法调用并对底层对象执行一些处理。这是我的实现:
public class MyListProxy : RealProxy
{
private MyList actualList;
private MyListProxy(Type typeToProxy, IEnumerable<string> stringList)
: base(typeToProxy)
{
this.actualList = new MyList(stringList);
}
public static object CreateProxy(IEnumerable<string> stringList)
{
MyListProxy listProxy = new MyListProxy(typeof(MyList), stringList);
object foo = listProxy.GetTransparentProxy();
return foo;
}
public override IMessage Invoke(IMessage msg)
{
IMethodCallMessage callMsg = msg as IMethodCallMessage;
MethodInfo proxiedMethod = callMsg.MethodBase as MethodInfo;
return new ReturnMessage(proxiedMethod.Invoke(actualList, callMsg.Args), null, 0, callMsg.LogicalCallContext, callMsg);
}
}
最后,我有一个使用代理 class 的 class,我通过反射设置 MyList
成员的值。
public class ListConsumer
{
public MyList MyList { get; protected set; }
public ListConsumer()
{
object listProxy = MyListProxy.CreateProxy(new List<string>() { "foo", "bar", "baz", "qux" });
PropertyInfo myListPropInfo = this.GetType().GetProperty("MyList");
myListPropInfo.SetValue(this, listProxy);
}
}
现在,如果我尝试使用反射来访问代理对象,我 运行 就会遇到问题。这是一个例子:
class Program
{
static void Main(string[] args)
{
ListConsumer listConsumer = new ListConsumer();
// These calls merely illustrate that the property can be
// properly accessed and methods called through the created
// proxy without issue.
Console.WriteLine("List contains {0} items", listConsumer.MyList.Count);
Console.WriteLine("List contents:");
foreach(string stringValue in listConsumer.MyList)
{
Console.WriteLine(stringValue);
}
Type listType = listConsumer.MyList.GetType();
foreach (Type interfaceType in listType.GetInterfaces())
{
if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(ICollection<>))
{
// Attempting to get the value of the Count property via
// reflection throws an exception.
Console.WriteLine("Checking interface {0}", interfaceType.Name);
System.Reflection.PropertyInfo propInfo = interfaceType.GetProperty("Count");
int count = (int)propInfo.GetValue(listConsumer.MyList, null);
}
else
{
Console.WriteLine("Skipping interface {0}", interfaceType.Name);
}
}
Console.ReadLine();
}
}
尝试通过反射在 Count
属性 上调用 GetValue
会引发以下异常:
An exception of type 'System.Reflection.TargetException' occurred in
mscorlib.dll but was not handled in user code
Additional information: Object does not match target type.
当尝试获取 Count
属性 的值时,显然框架正在向下调用 System.Runtime.InteropServices.WindowsRuntime.IVector
以调用 get_Size
方法。我不明白此调用如何在代理的基础对象(实际列表)上失败以实现此目的。如果我没有使用对象的代理,则通过反射获取 属性 值可以正常工作。我究竟做错了什么?我什至可以做我想完成的事情吗?
编辑: Microsoft Connect 站点上关于此问题的bug has been opened。
我认为这可能是 .Net 框架中的错误。 RuntimePropertyInfo.GetValue
方法以某种方式为 ICollection<>.Count
属性 选择了错误的实现,它似乎与 WindowsRuntime 投影有关。也许当他们将 WindowsRuntime 互操作放入框架时,远程处理代码被重写了。
我将框架切换到目标 .Net 2.0,因为我认为如果这是一个错误,它不应该在那个框架中。转换时,Visual Studio 删除了对我的控制台 exe 项目的 "Prefer 32 bit" 检查(因为这在 2.0 中不存在)。当它不存在时,运行无一例外。
总而言之,它 运行 在 .Net 2.0 上以 32 位和 64 位运行。它 运行 在 .Net 4.x 上以 64 位运行。仅在 .Net 4.x 32 位上抛出异常。这肯定看起来像一个错误。如果你可以 运行 它是 64 位的,那将是一个解决方法。
请注意,我已经安装了 .Net 4.6,它取代了大部分 .Net 框架 v4.x。这可能是引入问题的地方;在我得到一台没有 .Net 4.6 的机器之前我无法测试。
更新时间:2015-09-08
它也发生在只安装了 .Net 4.5.2(没有 4.6)的机器上。
更新时间:2015-09-07
这是一个较小的重现,使用相同的 类:
static void Main(string[] args)
{
var myList = MyListProxy.CreateProxy(new[] {"foo", "bar", "baz", "quxx"});
var listType = myList.GetType();
var interfaceType = listType.GetInterface("System.Collections.Generic.ICollection`1");
var propInfo = interfaceType.GetProperty("Count");
// TargetException thrown on 32-bit .Net 4.5.2+ installed
int count = (int)propInfo.GetValue(myList, null);
}
我也试过 IsReadOnly
属性,但它似乎有效(无一例外)。
至于 bug 的来源,属性有两层间接层,一层是远程处理,另一层是称为 MethodDef
s 的元数据结构与实际 运行time 方法,内部称为 MethodDesc
。 This mapping is specialized for properties (as well as events), where additional MethodDesc
s to support the property's get/set PropertyInfo instances are known as Associates
。通过调用 PropertyInfo.GetValue
,我们通过这些 Associate MethodDesc
指针中的一个指向底层方法实现,远程处理进行一些指针数学运算以在通道的另一侧获得正确的 MethodDesc
。 CLR 代码在这里非常复杂,我对 MethodTable
的内存布局没有足够的经验,它包含远程处理使用的这些 MethodDesc
记录(或它用来获取的映射) MethodTable?),但我认为远程处理通过一些错误的指针数学运算获取错误的 MethodDesc
是一个合理的猜测。这就是为什么我们看到一个相似但不相关的(就您的程序而言)MethodDesc
- UInt32 get_Size
of IVector<T>
在调用中被调用:
System.Reflection.RuntimeMethodInfo.CheckConsistency(Object target)
System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
ConsoleApplication1.MyListProxy.Invoke(IMessage msg) Program.cs: line: 60
System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
System.Runtime.InteropServices.WindowsRuntime.IVector`1.get_Size()
System.Runtime.InteropServices.WindowsRuntime.VectorToCollectionAdapter.Count[T]()
这是一个非常有趣的 CLR 错误,它的一些胆量在事故中表现出来。您可以从堆栈跟踪中看出它正在尝试调用 VectorToCollectionAdapter 的计数 属性.
这个class比较特殊,它的实例从未被创建过。它是 .NET 4.5 中添加的 语言投影 的一部分,使 WinRT 接口类型看起来像 .NET Framework 类型。它非常类似于 SZArrayHelper class,一个适配器 class,有助于实现非泛型数组实现泛型接口类型的错觉,如 IList<T>
.
此处使用的接口映射适用于 WinRT IVector<T>
接口。如 MSDN 文章中所述,该接口类型映射到 IList<T>
。内部 VectorToListAdapter class 负责 IList<T>
成员,VectorToCollectionAdapter 处理 ICollection<T>
成员。
您的代码强制 CLR 查找 ICollection<>.Count 的实现,它可以是 .NET class 正常实现它,也可以是将其公开为 IVector< >.大小。显然你创建的代理让它很头疼,它错误地决定了 WinRT 版本。
如何应该 找出哪个是正确的选择是相当模糊的。毕竟,您的代理 可以 是实际 WinRT 对象的代理,然后它所做的选择将是正确的。这很可能是结构性问题。它的行为如此随机,代码确实可以在 64 位模式下工作,这并不是很鼓舞人心。 VectorToCollectionAdapter 非常危险,请注意 JitHelpers.UnsafeCast 调用,此错误可能会被利用。
好吧,提醒当局,在 connect.microsoft.com 提交错误报告。如果您不想花时间,请告诉我,我会处理的。很难找到解决方法,使用以 WinRT 为中心的 TypeInfo class 进行反射没有任何区别。移除抖动强制使其在 64 位模式下运行是一种创可贴,但很难保证。
我们目前正在通过这种脆弱的干预来解决这个问题(对代码表示歉意):
public class ProxyBase : RealProxy
{
// ... stuff ...
public static T Cast<T>(object o)
{
return (T)o;
}
public static object Create(Type interfaceType, object coreInstance,
IEnforce enforce, string parentNamingSequence)
{
var x = new ProxyBase(interfaceType, coreInstance, enforce,
parentNamingSequence);
MethodInfo castMethod = typeof(ProxyBase).GetMethod(
"Cast").MakeGenericMethod(interfaceType);
return castMethod.Invoke(null, new object[] { x.GetTransparentProxy() });
}
public override IMessage Invoke(IMessage msg)
{
IMethodCallMessage methodCall = (IMethodCallMessage)msg;
var method = (MethodInfo)methodCall.MethodBase;
if(method.DeclaringType.IsGenericType
&& method.DeclaringType.GetGenericTypeDefinition().FullName.Contains(
"System.Runtime.InteropServices.WindowsRuntime"))
{
Dictionary<string, string> methodMap = new Dictionary<string, string>
{ // add problematic methods here
{ "Append", "Add" },
{ "GetAt", "get_Item" }
};
if(methodMap.ContainsKey(method.Name) == false)
{
throw new Exception("Unable to resolve '" + method.Name + "'.");
}
// thanks microsoft
string correctMethod = methodMap[method.Name];
method = m_baseInterface.GetInterfaces().Select(
i => i.GetMethod(correctMethod)).Where(
mi => mi != null).FirstOrDefault();
if(method == null)
{
throw new Exception("Unable to resolve '" + method.Name +
"' to '" + correctMethod + "'.");
}
}
try
{
if(m_coreInstance == null)
{
var errorMessage = Resource.CoreInstanceIsNull;
WriteLogs(errorMessage, TraceEventType.Error);
throw new NullReferenceException(errorMessage);
}
var args = methodCall.Args.Select(a =>
{
object o;
if(RemotingServices.IsTransparentProxy(a))
{
o = (RemotingServices.GetRealProxy(a)
as ProxyBase).m_coreInstance;
}
else
{
o = a;
}
if(method.Name == "get_Item")
{ // perform parameter conversions here
if(a.GetType() == typeof(UInt32))
{
return Convert.ToInt32(a);
}
return a;
}
return o;
}).ToArray();
// this is where it barfed
var result = method.Invoke(m_coreInstance, args);
// special handling for GetType()
if(method.Name == "GetType")
{
result = m_baseInterface;
}
else
{
// special handling for interface return types
if(method.ReturnType.IsInterface)
{
result = ProxyBase.Create(method.ReturnType, result, m_enforce, m_namingSequence);
}
}
return new ReturnMessage(result, args, args.Length, methodCall.LogicalCallContext, methodCall);
}
catch(Exception e)
{
WriteLogs("Exception: " + e, TraceEventType.Error);
if(e is TargetInvocationException && e.InnerException != null)
{
return new ReturnMessage(e.InnerException, msg as IMethodCallMessage);
}
return new ReturnMessage(e, msg as IMethodCallMessage);
}
}
// ... stuff ...
}
m_coreInstance这里是代理正在包装的对象实例。
m_baseInterface 是对象要用作的接口。
此代码拦截在 VectorToListAdapter 和 VectorToCollectionAdapter 中进行的调用,并通过该 methodMap 字典将其转换回原始调用。
条件部分:
method.DeclaringType.GetGenericTypeDefinition().FullName.Contains(
"System.Runtime.InteropServices.WindowsRuntime")
确保它只拦截来自 System.Runtime.InteropServices.WindowsRuntime 命名空间中的东西的调用 - 理想情况下我们会直接针对类型但它们是不可访问的 - 这可能应该更改为针对特定的 class 名称在命名空间中。
然后将参数转换为适当的类型并调用该方法。参数转换似乎是必要的,因为传入的参数类型是基于方法调用的参数类型 来自 System.Runtime.InteropServices.WindowsRuntime 命名空间中的对象,而不是方法调用 到 原始对象类型;即 System.Runtime.InteropServices.WindowsRuntime 命名空间中对象之前的原始类型劫持了该机制。
例如,WindowsRuntime 拦截了对 get_Item 的原始调用,并将其转换为对 Indexer_Get 方法的调用:http://referencesource.microsoft.com/#mscorlib/system/runtime/interopservices/windowsruntime/vectortolistadapter.cs,de8c78a8f98213a0,references。此方法然后使用不同的参数类型调用 GetAt 成员,然后调用对象上的 GetAt(同样使用不同的参数类型)——这是我们在 Invoke() 中劫持的调用,并将其转换回原始方法调用原始参数类型。
如果能够反映 VectorToListAdapter 和 VectorToCollectionAdapter 以提取它们的所有方法和它们进行的嵌套调用,那就太好了,但不幸的是,这些 classes 被标记为内部。
这对我们有用,但我敢肯定它充满了漏洞 - 这是一个反复试验的案例,运行 它会查看失败的地方,然后添加所需的字典 entries/parameter 转换。我们正在继续寻找更好的解决方案。
HTH
我很确定我在某处遗漏了一些限制或警告,但这是我的情况。假设我有一个 class 我想为其代理,如下所示:
public class MyList : MarshalByRefObject, IList<string>
{
private List<string> innerList;
public MyList(IEnumerable<string> stringList)
{
this.innerList = new List<string>(stringList);
}
// IList<string> implementation omitted for brevity.
// For the sake of this exercise, assume each method
// implementation merely passes through to the associated
// method on the innerList member variable.
}
我想为那个 class 创建一个代理,这样我就可以拦截方法调用并对底层对象执行一些处理。这是我的实现:
public class MyListProxy : RealProxy
{
private MyList actualList;
private MyListProxy(Type typeToProxy, IEnumerable<string> stringList)
: base(typeToProxy)
{
this.actualList = new MyList(stringList);
}
public static object CreateProxy(IEnumerable<string> stringList)
{
MyListProxy listProxy = new MyListProxy(typeof(MyList), stringList);
object foo = listProxy.GetTransparentProxy();
return foo;
}
public override IMessage Invoke(IMessage msg)
{
IMethodCallMessage callMsg = msg as IMethodCallMessage;
MethodInfo proxiedMethod = callMsg.MethodBase as MethodInfo;
return new ReturnMessage(proxiedMethod.Invoke(actualList, callMsg.Args), null, 0, callMsg.LogicalCallContext, callMsg);
}
}
最后,我有一个使用代理 class 的 class,我通过反射设置 MyList
成员的值。
public class ListConsumer
{
public MyList MyList { get; protected set; }
public ListConsumer()
{
object listProxy = MyListProxy.CreateProxy(new List<string>() { "foo", "bar", "baz", "qux" });
PropertyInfo myListPropInfo = this.GetType().GetProperty("MyList");
myListPropInfo.SetValue(this, listProxy);
}
}
现在,如果我尝试使用反射来访问代理对象,我 运行 就会遇到问题。这是一个例子:
class Program
{
static void Main(string[] args)
{
ListConsumer listConsumer = new ListConsumer();
// These calls merely illustrate that the property can be
// properly accessed and methods called through the created
// proxy without issue.
Console.WriteLine("List contains {0} items", listConsumer.MyList.Count);
Console.WriteLine("List contents:");
foreach(string stringValue in listConsumer.MyList)
{
Console.WriteLine(stringValue);
}
Type listType = listConsumer.MyList.GetType();
foreach (Type interfaceType in listType.GetInterfaces())
{
if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(ICollection<>))
{
// Attempting to get the value of the Count property via
// reflection throws an exception.
Console.WriteLine("Checking interface {0}", interfaceType.Name);
System.Reflection.PropertyInfo propInfo = interfaceType.GetProperty("Count");
int count = (int)propInfo.GetValue(listConsumer.MyList, null);
}
else
{
Console.WriteLine("Skipping interface {0}", interfaceType.Name);
}
}
Console.ReadLine();
}
}
尝试通过反射在 Count
属性 上调用 GetValue
会引发以下异常:
An exception of type 'System.Reflection.TargetException' occurred in mscorlib.dll but was not handled in user code
Additional information: Object does not match target type.
当尝试获取 Count
属性 的值时,显然框架正在向下调用 System.Runtime.InteropServices.WindowsRuntime.IVector
以调用 get_Size
方法。我不明白此调用如何在代理的基础对象(实际列表)上失败以实现此目的。如果我没有使用对象的代理,则通过反射获取 属性 值可以正常工作。我究竟做错了什么?我什至可以做我想完成的事情吗?
编辑: Microsoft Connect 站点上关于此问题的bug has been opened。
我认为这可能是 .Net 框架中的错误。 RuntimePropertyInfo.GetValue
方法以某种方式为 ICollection<>.Count
属性 选择了错误的实现,它似乎与 WindowsRuntime 投影有关。也许当他们将 WindowsRuntime 互操作放入框架时,远程处理代码被重写了。
我将框架切换到目标 .Net 2.0,因为我认为如果这是一个错误,它不应该在那个框架中。转换时,Visual Studio 删除了对我的控制台 exe 项目的 "Prefer 32 bit" 检查(因为这在 2.0 中不存在)。当它不存在时,运行无一例外。
总而言之,它 运行 在 .Net 2.0 上以 32 位和 64 位运行。它 运行 在 .Net 4.x 上以 64 位运行。仅在 .Net 4.x 32 位上抛出异常。这肯定看起来像一个错误。如果你可以 运行 它是 64 位的,那将是一个解决方法。
请注意,我已经安装了 .Net 4.6,它取代了大部分 .Net 框架 v4.x。这可能是引入问题的地方;在我得到一台没有 .Net 4.6 的机器之前我无法测试。
更新时间:2015-09-08
它也发生在只安装了 .Net 4.5.2(没有 4.6)的机器上。
更新时间:2015-09-07
这是一个较小的重现,使用相同的 类:
static void Main(string[] args)
{
var myList = MyListProxy.CreateProxy(new[] {"foo", "bar", "baz", "quxx"});
var listType = myList.GetType();
var interfaceType = listType.GetInterface("System.Collections.Generic.ICollection`1");
var propInfo = interfaceType.GetProperty("Count");
// TargetException thrown on 32-bit .Net 4.5.2+ installed
int count = (int)propInfo.GetValue(myList, null);
}
我也试过 IsReadOnly
属性,但它似乎有效(无一例外)。
至于 bug 的来源,属性有两层间接层,一层是远程处理,另一层是称为 MethodDef
s 的元数据结构与实际 运行time 方法,内部称为 MethodDesc
。 This mapping is specialized for properties (as well as events), where additional MethodDesc
s to support the property's get/set PropertyInfo instances are known as Associates
。通过调用 PropertyInfo.GetValue
,我们通过这些 Associate MethodDesc
指针中的一个指向底层方法实现,远程处理进行一些指针数学运算以在通道的另一侧获得正确的 MethodDesc
。 CLR 代码在这里非常复杂,我对 MethodTable
的内存布局没有足够的经验,它包含远程处理使用的这些 MethodDesc
记录(或它用来获取的映射) MethodTable?),但我认为远程处理通过一些错误的指针数学运算获取错误的 MethodDesc
是一个合理的猜测。这就是为什么我们看到一个相似但不相关的(就您的程序而言)MethodDesc
- UInt32 get_Size
of IVector<T>
在调用中被调用:
System.Reflection.RuntimeMethodInfo.CheckConsistency(Object target)
System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
ConsoleApplication1.MyListProxy.Invoke(IMessage msg) Program.cs: line: 60
System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
System.Runtime.InteropServices.WindowsRuntime.IVector`1.get_Size()
System.Runtime.InteropServices.WindowsRuntime.VectorToCollectionAdapter.Count[T]()
这是一个非常有趣的 CLR 错误,它的一些胆量在事故中表现出来。您可以从堆栈跟踪中看出它正在尝试调用 VectorToCollectionAdapter 的计数 属性.
这个class比较特殊,它的实例从未被创建过。它是 .NET 4.5 中添加的 语言投影 的一部分,使 WinRT 接口类型看起来像 .NET Framework 类型。它非常类似于 SZArrayHelper class,一个适配器 class,有助于实现非泛型数组实现泛型接口类型的错觉,如 IList<T>
.
此处使用的接口映射适用于 WinRT IVector<T>
接口。如 MSDN 文章中所述,该接口类型映射到 IList<T>
。内部 VectorToListAdapter class 负责 IList<T>
成员,VectorToCollectionAdapter 处理 ICollection<T>
成员。
您的代码强制 CLR 查找 ICollection<>.Count 的实现,它可以是 .NET class 正常实现它,也可以是将其公开为 IVector< >.大小。显然你创建的代理让它很头疼,它错误地决定了 WinRT 版本。
如何应该 找出哪个是正确的选择是相当模糊的。毕竟,您的代理 可以 是实际 WinRT 对象的代理,然后它所做的选择将是正确的。这很可能是结构性问题。它的行为如此随机,代码确实可以在 64 位模式下工作,这并不是很鼓舞人心。 VectorToCollectionAdapter 非常危险,请注意 JitHelpers.UnsafeCast 调用,此错误可能会被利用。
好吧,提醒当局,在 connect.microsoft.com 提交错误报告。如果您不想花时间,请告诉我,我会处理的。很难找到解决方法,使用以 WinRT 为中心的 TypeInfo class 进行反射没有任何区别。移除抖动强制使其在 64 位模式下运行是一种创可贴,但很难保证。
我们目前正在通过这种脆弱的干预来解决这个问题(对代码表示歉意):
public class ProxyBase : RealProxy
{
// ... stuff ...
public static T Cast<T>(object o)
{
return (T)o;
}
public static object Create(Type interfaceType, object coreInstance,
IEnforce enforce, string parentNamingSequence)
{
var x = new ProxyBase(interfaceType, coreInstance, enforce,
parentNamingSequence);
MethodInfo castMethod = typeof(ProxyBase).GetMethod(
"Cast").MakeGenericMethod(interfaceType);
return castMethod.Invoke(null, new object[] { x.GetTransparentProxy() });
}
public override IMessage Invoke(IMessage msg)
{
IMethodCallMessage methodCall = (IMethodCallMessage)msg;
var method = (MethodInfo)methodCall.MethodBase;
if(method.DeclaringType.IsGenericType
&& method.DeclaringType.GetGenericTypeDefinition().FullName.Contains(
"System.Runtime.InteropServices.WindowsRuntime"))
{
Dictionary<string, string> methodMap = new Dictionary<string, string>
{ // add problematic methods here
{ "Append", "Add" },
{ "GetAt", "get_Item" }
};
if(methodMap.ContainsKey(method.Name) == false)
{
throw new Exception("Unable to resolve '" + method.Name + "'.");
}
// thanks microsoft
string correctMethod = methodMap[method.Name];
method = m_baseInterface.GetInterfaces().Select(
i => i.GetMethod(correctMethod)).Where(
mi => mi != null).FirstOrDefault();
if(method == null)
{
throw new Exception("Unable to resolve '" + method.Name +
"' to '" + correctMethod + "'.");
}
}
try
{
if(m_coreInstance == null)
{
var errorMessage = Resource.CoreInstanceIsNull;
WriteLogs(errorMessage, TraceEventType.Error);
throw new NullReferenceException(errorMessage);
}
var args = methodCall.Args.Select(a =>
{
object o;
if(RemotingServices.IsTransparentProxy(a))
{
o = (RemotingServices.GetRealProxy(a)
as ProxyBase).m_coreInstance;
}
else
{
o = a;
}
if(method.Name == "get_Item")
{ // perform parameter conversions here
if(a.GetType() == typeof(UInt32))
{
return Convert.ToInt32(a);
}
return a;
}
return o;
}).ToArray();
// this is where it barfed
var result = method.Invoke(m_coreInstance, args);
// special handling for GetType()
if(method.Name == "GetType")
{
result = m_baseInterface;
}
else
{
// special handling for interface return types
if(method.ReturnType.IsInterface)
{
result = ProxyBase.Create(method.ReturnType, result, m_enforce, m_namingSequence);
}
}
return new ReturnMessage(result, args, args.Length, methodCall.LogicalCallContext, methodCall);
}
catch(Exception e)
{
WriteLogs("Exception: " + e, TraceEventType.Error);
if(e is TargetInvocationException && e.InnerException != null)
{
return new ReturnMessage(e.InnerException, msg as IMethodCallMessage);
}
return new ReturnMessage(e, msg as IMethodCallMessage);
}
}
// ... stuff ...
}
m_coreInstance这里是代理正在包装的对象实例。
m_baseInterface 是对象要用作的接口。
此代码拦截在 VectorToListAdapter 和 VectorToCollectionAdapter 中进行的调用,并通过该 methodMap 字典将其转换回原始调用。
条件部分:
method.DeclaringType.GetGenericTypeDefinition().FullName.Contains(
"System.Runtime.InteropServices.WindowsRuntime")
确保它只拦截来自 System.Runtime.InteropServices.WindowsRuntime 命名空间中的东西的调用 - 理想情况下我们会直接针对类型但它们是不可访问的 - 这可能应该更改为针对特定的 class 名称在命名空间中。
然后将参数转换为适当的类型并调用该方法。参数转换似乎是必要的,因为传入的参数类型是基于方法调用的参数类型 来自 System.Runtime.InteropServices.WindowsRuntime 命名空间中的对象,而不是方法调用 到 原始对象类型;即 System.Runtime.InteropServices.WindowsRuntime 命名空间中对象之前的原始类型劫持了该机制。
例如,WindowsRuntime 拦截了对 get_Item 的原始调用,并将其转换为对 Indexer_Get 方法的调用:http://referencesource.microsoft.com/#mscorlib/system/runtime/interopservices/windowsruntime/vectortolistadapter.cs,de8c78a8f98213a0,references。此方法然后使用不同的参数类型调用 GetAt 成员,然后调用对象上的 GetAt(同样使用不同的参数类型)——这是我们在 Invoke() 中劫持的调用,并将其转换回原始方法调用原始参数类型。
如果能够反映 VectorToListAdapter 和 VectorToCollectionAdapter 以提取它们的所有方法和它们进行的嵌套调用,那就太好了,但不幸的是,这些 classes 被标记为内部。
这对我们有用,但我敢肯定它充满了漏洞 - 这是一个反复试验的案例,运行 它会查看失败的地方,然后添加所需的字典 entries/parameter 转换。我们正在继续寻找更好的解决方案。
HTH