我正在开发一个系统,其中我计划使用RealProxy对象来实现对一组对象的方法调用的拦截,处理该调用,然后返回适当的结果。
这只适用于简单的返回类型,比如字符串或int,但我似乎不能从RealProxy.Invoke方法返回对象。
一切正常。我没有收到错误,但返回的值始终是NOTHING,而不是object。
我已经尽可能地编写了最小的示例代码,并将其包含在下面。
本质上,只需调用RPtest并单步执行即可。该代码创建一个简单的对象RPTestA,其中包含一个字符串字段和一个对象值字段,然后检索字符串Dim x= c.Name,该字符串运行良好,然后尝试检索该对象
Dim r = c.SubObj它总是不返回任何内容。
但是,在FieldGetter例程中,此代码:
'---- the field is an OBJECT type field
Dim mc = New MethodCallMessageWrapper(Msg)
'---- create the object
Dim o = Activator.CreateInstance(t)
'---- and construct the return message with that object
Dim r = New ReturnMessage(o, mc.Args, mc.Args.Length, mc.LogicalCallContext, mc)
Return r 将ReturnMessage的ReturnValue字段设置为由上面的Activator.CreateInstance(t)调用创建的对象,似乎可以很好地工作。
我怀疑这是某种序列化的东西,但我不知所措。
您应该能够直接运行此代码,但只需将其粘贴到新的VB.net项目中即可。
'----------------------------------------------------------------------------
Imports System.Security.Permissions
Imports System.Diagnostics
Imports System.Reflection
Imports System.Runtime.CompilerServices
Imports System.Runtime.Serialization
Imports System.Runtime.Remoting
Imports System.Runtime.Remoting.Activation
Imports System.Runtime.Remoting.Messaging
Imports System.Runtime.Remoting.Proxies
Public Module RPTest
Public Sub RPTest()
'---- create a new object that is automatically proxied
' See the RPProxyAttribute for details
Dim c = New RPTestA
Dim x = c.Name
'x is returned as a string value just fine
Dim r = c.SubObj
'********* PROBLEM IS HERE, r ends up nothing
End Sub
End Module
'ROOT test object
Public Class RPTestA
Inherits RPBase
Public Name As String = "Test Name"
Public SubObj As RPTestB
End Class
'SUB OBJECT which should be returned as a field value from the root object above
Public Class RPTestB
Inherits RPBase
Public SubProperty As String = "SubObj Test Property"
End Class
''' <summary>
''' Base proxyable object class
''' </summary>
''' <remarks></remarks>
<RPProxy()> _
Public MustInherit Class RPBase
Inherits ContextBoundObject
End Class
<PermissionSet(SecurityAction.Demand, Name:="FullTrust")> _
Public Class RPProxy
Inherits RealProxy
Private m_target As MarshalByRefObject
Public Sub New()
m_target = DirectCast(Activator.CreateInstance(GetType(ConfigRP)), MarshalByRefObject)
Dim myObjRef = RemotingServices.Marshal(m_target)
End Sub
Public Sub New(ByVal classToProxy As Type)
MyBase.New(classToProxy)
End Sub
Public Sub New(ByVal ClassToProxy As Type, ByVal targetObject As MarshalByRefObject)
m_target = targetObject
Dim myObjRef = RemotingServices.Marshal(m_target)
End Sub
Public Overrides Function Invoke(ByVal msg As IMessage) As IMessage
Dim returnMsg As IMethodReturnMessage = Nothing
If TypeOf msg Is IConstructionCallMessage Then
'---- handle constructor calls
Dim ConstructionCallMessage = DirectCast(msg, IConstructionCallMessage)
returnMsg = InitializeServerObject(ConstructionCallMessage)
Me.m_target = Me.GetUnwrappedServer()
SetStubData(Me, Me.m_target)
Return returnMsg
ElseIf TypeOf msg Is IMethodCallMessage Then
'---- handle all other method calls
Dim methodCallMessage = DirectCast(msg, IMethodCallMessage)
'---- before message processing
preprocess(methodCallMessage)
'---- execute the method call
Dim rawReturnMessage = RemotingServices.ExecuteMessage(Me.m_target, methodCallMessage)
'---- and postprocess
returnMsg = postprocess(methodCallMessage, rawReturnMessage)
Else
Throw New NotSupportedException()
End If
Return returnMsg
End Function
'Called BEFORE the actual method is invoked
Private Sub PreProcess(ByVal msg As IMessage)
Console.WriteLine("before method call...")
End Sub
'Called AFTER the actual method is invoked
Private Function PostProcess(ByVal Msg As IMethodCallMessage, ByVal msgReturn As ReturnMessage) As ReturnMessage
Dim r As ReturnMessage
If Msg.MethodName = "FieldGetter" Then
r = FieldGetter(Msg, msgReturn)
ElseIf Msg.MethodName = "FieldSetter" Then
'na
r = msgReturn
ElseIf Msg.MethodName.StartsWith("get_") Then
'na
r = msgReturn
ElseIf Msg.MethodName.StartsWith("set_") Then
'na
r = msgReturn
Else
r = msgReturn
End If
Return r
End Function
Private Function FieldGetter(ByVal Msg As IMethodCallMessage, ByVal msgReturn As IMethodReturnMessage) As IMethodReturnMessage
Dim t = Me.Target.GetType
'---- This retrieves the type of the field that the getter should retrieve
t = t.GetField(Msg.InArgs(1), BindingFlags.Instance Or BindingFlags.Public).FieldType
If t.Name = "String" Then
'---- just return what the object returned as a result of ExecuteMessage
Return msgReturn
ElseIf t.BaseType.Equals(GetType(RPBase)) Then
'---- the field is an OBJECT type field
Dim mc = New MethodCallMessageWrapper(Msg)
'---- create the object
Dim o = Activator.CreateInstance(t)
'---- and construct the return message with that object
Dim r = New ReturnMessage(o, mc.Args, mc.Args.Length, mc.LogicalCallContext, mc)
Return r
Else
Return msgReturn
End If
End Function
Public Property Target() As Object
Get
Return Me.m_target
End Get
Set(ByVal value As Object)
Me.m_target = value
End Set
End Property
End Class
<AttributeUsage(AttributeTargets.Class)> _
<SecurityPermissionAttribute(SecurityAction.Demand, Flags:=SecurityPermissionFlag.Infrastructure)> _
Public Class RPProxyAttribute
Inherits ProxyAttribute
Public Overrides Function CreateInstance(ByVal Type As Type) As MarshalByRefObject
Dim proxy = New RPProxy(Type)
Dim transparentProxy = DirectCast(proxy.GetTransparentProxy(), MarshalByRefObject)
Return transparentProxy
End Function
End Class发布于 2010-10-15 22:49:26
好吧,事实证明这是一个非常简单的修复方法,一旦你越过了可怕的ReturnMessage构造函数,那就会产生误导!
非常感谢我的一位老同事,Rich Quackenbush,他花了几分钟时间来查看这篇文章。有时候,只见树木不见森林!
总之,在FieldGetter中,我是这样做的
ElseIf t.BaseType.Equals(GetType(RPBase)) Then
'---- the field is an OBJECT type field
Dim mc = New MethodCallMessageWrapper(Msg)
'---- create the object
Dim o = Activator.CreateInstance(t)
'---- and construct the return message with that object
Dim r = New ReturnMessage(o, mc.Args, mc.Args.Length, mc.LogicalCallContext, mc)
Return r看起来完全合理,新创建的对象被传递到名为ReturnValue的ReturnMessage构造函数参数中。
但不是。实际上,您必须创建一个对象数组,并将其作为该数组中的3元素传递,如下所示:
ElseIf t.BaseType.Equals(GetType(RPBase)) Then
'---- the field is an OBJECT type field
Dim mc = New MethodCallMessageWrapper(Msg) '---- create the object
Dim o = Activator.CreateInstance(t)
'---- and construct the return message with that object
Dim r = New ReturnMessage(Nothing, New Object() {Nothing, Nothing, o}, 3, mc.LogicalCallContext, mc)
Return r事实证明,这是因为FieldGetter函数是被代理“调用”和拦截的函数,它的签名是
FieldGetter(StringtypeName,StringfieldName,Object&val)对于为该调用构造ReturnMessage的目的,这意味着它根本没有返回值,而是返回值作为该列表中的第三个参数返回。
因为我实际上并没有调用真正的FieldGetter函数,所以前两个参数(类型名和字段名)是无关紧要的,但是第三个参数是放置返回值的合适位置。
事后看来,这总是显而易见的!
非常感谢Rich。
https://stackoverflow.com/questions/3886224
复制相似问题