2010-10-07 64 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