0

我正在开发一个系统,我计划在其中使用 RealProxy 对象来启用对一组对象的拦截方法调用,处理调用,然后返回适当的结果。

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

一切正常。我没有收到任何错误,但返回的值始终是 NOTHING,而不是对象。

我已经完成了我能做的最小的示例代码,并将其包含在下面。

本质上,只需调用 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
4

1 回答 1

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

似乎完全合理,新创建的对象被传递到名为 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 的目的,这意味着它根本没有 Returnvalue,而是返回值作为该列表中的第三个参数返回。

因为我实际上并没有调用真正的 FieldGetter 函数,所以前两个参数(类型名和字段名)无关紧要,但第三个参数是放置返回值的合适位置。

事后看来总是很明显!

非常感谢 Rich。

于 2010-10-15T14:49:25.883 回答