-2

这是一个测试代码:

Public Class Class2
   Public GoodName as String
   Public Function someFunction ()
   End Function
End Class

Public Class Class1
    Public Enum ENUM_VAL1_
       abc = 1
       def = 2
    End Enum

    Public Enum ERROR_VAL1_
       yhn = 1
       ujm = 2
    End Enum

    Public Function fun1(byval _enumArg1 as ENUM_VAL1_,
                         byval _stringArg as String,
                         byref _classArg Class2) as ERROR_VAL1_
       ... SOMETHING HERE
       return something
    End Function
End Class

和测试

    Dim asm As Assembly = Assembly.LoadFrom(<the source>)
    If asm Is Nothing Then Return Nothing

    Dim typ As Type = asm.GetType(<namespace>.<class1>)
    If typ Is Nothing Then Return Nothing

    Dim obj As Object = Activator.CreateInstance(typ)
    If obj Is Nothing Then Return Nothing

    Dim arg As Object() = New Object() {ENUM_VAL1_.abc, "qazxsw", New Class2}

    Dim res As Object = typ.InvokeMember( _
        "fun1", _
        BindingFlags.[Default] Or BindingFlags.InvokeMethod, _
        Nothing, _
        obj, _
        arg)

[ Dim res As Object = typ.InvokeMember( _ "fun1", _ BindingFlags.[Default] Or BindingFlags.InvokeMethod, _ Nothing, _ obj, _ arg)] 返回错误:function '[fullname]' not found.

然后我意识到 fun1 签名与我的调用不同(即使枚举值是整数)。我做了一些研究,发现当目标程序集上的属性或函数具有与标准类型不同的参数时如何实现反射的“一些”示例。但我成功地将这些样本“概念”转化为我的需要。

所以,在这里我放了一些带有虚拟枚举类型的虚拟类,只是为了指出问题的根源。

原始代码是关于 windows 防火墙/端口 (win7/xp/vista) 的,其中有许多枚举值,包括来自 firewallApi.dll 和 Hnetcfg.dll 的类型。

我遇到的问题是我不能“InvokeMember”(也是“SetProperties”和“SetProperties”)使用说:

Dim args As Object() = New Object() {"SQL", 6, "1433", 1}

我的程序集函数具有这些参数类型。

Public Function PortExists( _
    ByVal _ruleName As String, _
    ByVal _protocol As NET_FW_IP_PROTOCOL_, _
    ByVal _remotePorts As String, _
    ByVal _direction As NET_FW_RULE_DIRECTION_ _
) As FW_ERROR_CODE

好的,为简化起见,我制作了自己的枚举类型(无需安装额外的库)

Public Function PortExists( _
    ByVal _ruleName As String, _
    ByVal _protocol As FW_IP_PROTOCOL, _
    ByVal _remotePorts As String, _
    ByVal _direction As FW_RULE_DIRECTION _
) As FW_ERROR_CODE

不知何故,参数“6”和“1”应该分别转换为FW_IP_PROTOCOLFW_RULE_DIRECTION类型......

我不知道怎么办!

4

2 回答 2

1

好的,这是解决方案。

当我们从反射中使用 Invoke 方法时,我们必须将参数作为对象(object ())传递,并且类型完全匹配。即使枚举类型的值是一个整数(这是我最初遇到问题的地方),我必须将此值转换为引用程序集类型的确切枚举类型。所以下面的函数 <_FEnumToObject> 将完成这项工作。我添加了 _F 以将我所有的私有函数推到列表底部并将它们分组(IDE 属性列表);_S 将代表 subs 和 _P 代表属性...

Private Function _FEnumToObject( _
    ByVal _typeName As String, _
    ByVal _typeValue As String _
) As Object
    Dim enumType As Type = m_Assembly.[GetType](_typeName)
    Dim enumInfo As FieldInfo = enumType.GetField(_typeValue)
    Dim valueINT As Integer = CInt(enumInfo.GetValue(enumType))
    Dim valueOBJ As Object = [Enum].ToObject(enumType, valueINT)
    Return valueOBJ
End Function

下一个函数 <_FProtocolToObject> 用于将协议值(枚举)转换为对象。

Public Function _FProtocolToObject(ByVal _protocol As FW_IP_PROTOCOL) As Object
    'where <m_AssemblyRef> is like "wf7."
    Return _FEnumToObject(m_AssemblyRef + "FW_IP_PROTOCOL", _protocol.ToString)
End Function

这是最后的电话:

Public Function PortExists( _
    ByVal _ruleName As String, _
    ByVal _protocol As FW_IP_PROTOCOL, _
    ByVal _portNumber As Integer _
) As Boolean
    If m_AssemblyNotFound Then Return False
    Static mi As MethodInfo = m_RemoteType.GetMethod("PortExists")
    Dim m As FW_ERROR_CODE = FW_ERROR_CODE.UNKNOWN
    Try
        Dim a As Object = New Object() _
            { _
                _ruleName, _
                _FProtocolToObject(_protocol), _
                _portNumber _
            }
        m = mi.Invoke(m_RemoteObject, a)

    Catch ex As Exception
        'some error handler here...

    End Try
    Return (m = FW_ERROR_CODE.SUCCESS)
End Function

我还发布了一些额外的代码,以在我的示例代码中呈现一些无法解释/未设置的变量分配。

Private ReadOnly Property _PAssemblyPath() As String
    Get
        Dim ad As String = IO.Path.Combine(My.Application.Info.DirectoryPath, "bin")
        Dim af As String = IO.Path.Combine(ad, _PAssemblyName + ".dll")
        Return af
    End Get
End Property

公共子新()

    m_AssemblyPath = _PAssemblyPath
    m_AssemblyRef = _PAssemblyName + "."
    m_ClassPath = m_AssemblyRef + _PClassName

    If IO.File.Exists(m_AssemblyPath) Then

        Dim name As New AssemblyName()
        name.CodeBase = m_AssemblyPath
        m_Assembly = AppDomain.CurrentDomain.Load(name)
        m_RemoteObject = m_Assembly.CreateInstance(m_ClassPath)
        m_RemoteType = m_Assembly.[GetType](m_ClassPath)
        m_AssemblyNotFound = False

    Else
        'some error handler here

    End If

End Sub

假设“SCSysInfo”是一个用于检查操作系统系统等的实用程序类......

Private ReadOnly Property _PClassName() As String
    Get
        Dim s As String = "CFwRule"
        If SCSysInfo.IsWin7 Then
            s += "7"
        ElseIf SCSysInfo.IsWinVista Then
            s += "Vista"
        ElseIf SCSysInfo.IsWinXP Then
            s += "XP"
        End If
        Return s
    End Get
End Property
Private ReadOnly Property _PAssemblyName() As String
    Get
        Dim s As String = String.Empty
        If SCSysInfo.IsWin7 Then
            s = "fw7"
        ElseIf SCSysInfo.IsWinVista Then
            s = "fwv"
        ElseIf SCSysInfo.IsWinXP Then
            s = "fwxp"
        End If
        Return s
    End Get
End Property

我的班级中的“PortExists”函数(上面部分列出)还有两个用于 vista 和 win7 的签名。我必须提到的一件事是反射不能识别方法/属性的多重签名。使用反射时,方法或属性必须只有一个签名。谈谈我的情况:win7、vista 和 xp 为要编程的端口公开了完全不同的属性。在每个程序集中(我在各自的环境中编译),都有“相同”的方法和属性具有不同数量的参数。从我的“适配器”类(上面发布了其中的一部分)中,我从具有 3 个不同签名的所有 3 个程序集中调用“相同”方法(基于操作系统,我选择适当的签名)。所以,我对“PortExists”方法的所有 3 个签名都在我的“适配器”类中。

于 2012-08-21T17:55:46.250 回答
0

通常,一个好的设计会使反射变得多余。我的建议是让不同的对象实现一些定义要调用的函数的通用接口。

常见定义:

Public Enum MyValueEnum
    value1 = 1
    value2 = 2
End Enum

Public Enum MyErrorEnum
    error1 = 1
    error2 = 2
End Enum

Public Class SomeClass
    Public GoodName As String

    Public Function SomeFunction() As Object
        Return Nothing
    End Function
End Class

界面

Public Interface IMyInterface
    Function TheFunction(ByVal val As MyValueEnum, ByVal name As String, _
                         ByRef someObject As SomeClass) As MyErrorEnum
End Interface

实现接口的两个不同的类

Public Class SomeImplementation
  Implements IMyInterface

  Public Function TheFunction(ByVal val As MyValueEnum, ByVal name As String, _
                              ByRef someObject As SomeClass) As MyErrorEnum _
      Implements IMyInterface.TheFunction
      '...
      Return MyErrorEnum.error1
  End Function
End Class

Public Class SomeOtherImplementation
  Implements IMyInterface

  Public Function TheFunction(ByVal val As MyValueEnum, ByVal name As String, _
                              ByRef someObject As SomeClass) As MyErrorEnum _
      Implements IMyInterface.TheFunction
      '...
      Return MyErrorEnum.error2
  End Function
End Class

考试

Dim obj As IMyInterface
Dim result As MyErrorEnum

obj = New SomeImplementation()
result = obj.TheFunction(MyValueEnum.value1, "test", New SomeClass())

obj = New SomeOtherImplementation()
result = obj.TheFunction(MyValueEnum.value2, "another test", New SomeClass())
于 2012-08-07T17:40:50.620 回答