Динамический вызов методов в DLL;Как изменить подпись метода? - PullRequest
1 голос
/ 27 декабря 2011

Я использую некоторый интересный код для выполнения динамических неуправляемых вызовов 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 [] »."

Кажется, я получаю где-то , но ... где?Я действительно не знаю.

1 Ответ

1 голос
/ 27 декабря 2011

Я понял.Теперь я могу динамически вызывать методы:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...