2010-10-07 70 views
0

我在制定一个系统,我打算使用RealProxy对象来启用对一组对象的拦截方法调用,处理该调用,然后返回适当的结果。如何通过RealProxy透明代理返回一个对象作为返回值?

这只适用于简单的返回类型,如字符串或整数,但我似乎无法从RealProxy.Invoke方法返回对象。

一切正常。我没有得到任何错误,但返回的值总是没有,而不是一个对象。

我已经完成了我可以的最小示例代码,并且已经包含在下面。

本质上,只需调用RPtest并单步执行即可。 该代码创建一个简单的对象,RPTestA,使用字符串字段和对象值字段 它然后检索字符串 昏暗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的返回值字段设置为是由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 

回答

3

哦,原来是一个非常简单的修复,一旦你过去的神可怕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 

似乎完全是合理的,是新创建的对象被传递到所谓的返回值的ReturnMessage构造函数的参数。

但是没有。实际上,你必须创建一个对象数组并把它传递是作为数组中的三要素,像这样:

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对于该呼叫的目的意味着它没有返回值可言,而是返回值被返回作为3'rd在该列表中的论点。

因为我实际上并没有调用真正的FieldGetter函数,所以前两个参数(typename和fieldname)是不重要的,但是第3个参数是放置返回值的适当位置。

事后总是很明显的!

非常感谢Rich。

+2

如果我知道这是关于所以我会在这里回应! – RQDQ 2010-10-15 19:46:21

+0

糟糕。抱歉。这就是你亲自了解我的原因 DarinH 2010-10-17 03:39:40