【问题标题】:Dynamic invocation of methods in DLLs; How to change method signature?动态调用 DLL 中的方法;如何更改方法签名?
【发布时间】:2011-12-27 04:57:00
【问题描述】:

我正在使用一些有趣的代码来执行动态非托管 dll 调用:

Imports System.Runtime.InteropServices

Module NativeMethods
Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal dllToLoad As String) As IntPtr
Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As IntPtr, ByVal procedureName As String) As IntPtr
Declare Function FreeLibrary Lib "kernel32" (ByVal hModule As IntPtr) As Boolean
End Module

Module Program
<UnmanagedFunctionPointer(CallingConvention.Cdecl)> _
Delegate Function MultiplyByTen(ByVal numberToMultiply As Integer) As Integer


Sub Main()
    Dim pDll As IntPtr = NativeMethods.LoadLibrary("MultiplyByTen.dll")

    Dim pAddressOfFunctionToCall As IntPtr = NativeMethods.GetProcAddress(pDll, "MultiplyByTen")

    Dim multiplyByTen As MultiplyByTen = DirectCast(Marshal.GetDelegateForFunctionPointer(pAddressOfFunctionToCall, GetType(MultiplyByTen)), MultiplyByTen)

    Dim theResult As Integer = multiplyByTen(10)

    Console.WriteLine(theResult)

    NativeMethods.FreeLibrary(pAddressOfFunctionToCall)
End Sub

End Module

我希望能够将委托的参数签名和类型更改为 void 或返回整数、字符串或布尔值的函数。

基本上,我希望我的程序(解释器)能够调用程序员可以访问的任何非托管 dll 中的任何方法......因为我无法预测程序员想要访问的方法 - 我'想让他们能够使用任何有用的方法。

这似乎在 vb.net 中是可能的——也许通过反射? - 但我只是不知道该怎么做。

--- 编辑:这是我想出的:

<UnmanagedFunctionPointer(CallingConvention.Cdecl)> _
Delegate Function DelegateInteger(<[ParamArray]()> ByVal args() As Object) As Integer

...

    Dim GetStdHandle = NativeDllCallIntegerMethod("kernel32", "GetStdHandle", -11)
...
    Function NativeDllCallIntegerMethod(ByVal DllPath As String, ByVal DllMethod As String, ByVal ParamArray Arguments() As Object) As Integer
    Dim pDll As IntPtr = NativeMethods.LoadLibrary(DllPath)

    Dim pAddressOfFunctionToCall As IntPtr = NativeMethods.GetProcAddress(pDll, DllMethod)

    Dim IntegerFunction As DelegateInteger = DirectCast(Marshal.GetDelegateForFunctionPointer(pAddressOfFunctionToCall, GetType(DelegateInteger)), DelegateInteger)

    Dim theResult As Object = IntegerFunction.DynamicInvoke(Arguments)

    NativeDllCallIntegerMethod = theResult

    NativeMethods.FreeLibrary(pAddressOfFunctionToCall)
    End Function

这会引发对“Dim theResult = ...”行的投诉。错误是“'System.Int32' 类型的对象无法转换为 'System.Object[]' 类型。”

我似乎正在某处,但是...在哪里?我真的不知道。

【问题讨论】:

  • 所以要明确一点,您希望用户能够指定方法名称和签名,并让您的程序自动生成正确的代码来调用该函数?
  • 是的。这就是我想要做的。
  • 这对于最一般的情况(可以使用任何签名)来说并不容易。您将需要了解 IL 如何发出等效的“.NET 汇编代码”来生成动态方法调用程序。但是,这个项目可能是一个很好的起点:dynamic.codeplex.com
  • 我想我之前提到过 TypeBuilder.DefinePInvokeMethod()。
  • @HansPassant:我找到了this article on DefinePInvokeMethod,而且我有 vb.net 的代码 - 这就是你的想法吗?

标签: vb.net dll delegates


【解决方案1】:

我明白了。我现在可以动态调用方法了:

Imports System
Imports System.Reflection
Imports System.Reflection.Emit
Imports System.Runtime.InteropServices

Module DynamicInvocation

Sub Main()
    Dim DI As New DynamicInvoke
    DI.Invoke("FreeConsole", "Kernel32", GetType(Boolean), Nothing)
    DI.Invoke("AllocConsole", "Kernel32", GetType(Boolean), Nothing)
    Dim StandardOutputHandle = DI.Invoke("GetStdHandle", "Kernel32", GetType(Integer), -11)

    DI.Invoke("WriteConsoleA", "Kernel32", GetType(Integer), StandardOutputHandle, "Testing!", 8, 0, 0)

End Sub

End Module

Public Class DynamicInvoke
',  Optional ByVal AssemblyName As String = "DynamicInvoke", Optional ByVal TypeName As String = "DynamicType", Optional ByVal Convention As CallingConvention = CallingConvention.Winapi, Optional ByVal CharacterSet As CharSet = CharSet.Ansi

Public AssemblyName As String = "DynamicInvoke"
Public TypeName As String = "DynamicType"
Public Convention As CallingConvention = CallingConvention.Winapi
Public CharacterSet As CharSet = CharSet.Ansi

Function Invoke(ByVal MethodName As String, ByVal LibraryName As String, ByVal ReturnType As Type, ByVal ParamArray Parameters() As Object)

    Dim ParameterTypesArray As Array

    If Parameters IsNot Nothing Then
        ParameterTypesArray = Array.CreateInstance(GetType(Type), Parameters.Length)
        Dim PTIndex As Integer = 0

        For Each Item In Parameters
            If Item IsNot Nothing Then
                ParameterTypesArray(PTIndex) = Item.GetType
            Else
                'ParameterTypesArray(PTIndex) = 0
            End If
            PTIndex += 1
        Next
    Else
        ParameterTypesArray = Nothing
    End If

    Dim ParameterTypes() As Type = ParameterTypesArray


    Dim asmName As New AssemblyName(AssemblyName)
    Dim dynamicAsm As AssemblyBuilder = _
        AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, _
            AssemblyBuilderAccess.RunAndSave)

    ' Create the module.
    Dim dynamicMod As ModuleBuilder = _
        dynamicAsm.DefineDynamicModule(asmName.Name, asmName.Name & ".dll")

    ' Create the TypeBuilder for the class that will contain the 
    ' signature for the PInvoke call.
    Dim tb As TypeBuilder = dynamicMod.DefineType(TypeName, _
        TypeAttributes.Public Or TypeAttributes.UnicodeClass)

    Dim mb As MethodBuilder = tb.DefinePInvokeMethod( _
        MethodName, _
        LibraryName, _
        MethodAttributes.Public Or MethodAttributes.Static Or MethodAttributes.PinvokeImpl, _
        CallingConventions.Standard, _
        ReturnType, _
        ParameterTypes, _
        Convention, _
        CharacterSet)

    ' Add PreserveSig to the method implementation flags. NOTE: If this line
    ' is commented out, the return value will be zero when the method is
    ' invoked.
    mb.SetImplementationFlags( _
        mb.GetMethodImplementationFlags() Or MethodImplAttributes.PreserveSig)

    ' The PInvoke method does not have a method body.

    ' Create the class and test the method.
    Dim t As Type = tb.CreateType()

    Dim mi As MethodInfo = t.GetMethod(MethodName)
    Return mi.Invoke(Me, Parameters)

    '' Produce the .dll file.
    'Console.WriteLine("Saving: " & asmName.Name & ".dll")
    'dynamicAsm.Save(asmName.Name & ".dll")
End Function

End Class

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多