2011-12-27 71 views
1

我使用了一些有趣的代碼來執行動態非託管的DLL調用:動態調用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 

這就提出了與行投訴「點心theResult = ......」就可以了。錯誤是「類型'System.Int32'的對象不能轉換爲類型'System.Object []'。」

我似乎在某處但是...哪裏?我不知道。

+0

所以要清楚,你希望用戶能夠指定一個方法名稱和簽名,並有自動程序生成正確的代碼來調用函數? – bobbymcr 2011-12-27 05:07:15

+0

是的。這就是我想要做的。 – Dominick 2011-12-27 05:11:02

+0

對於最一般的情況(可以使用任何簽名),這並不容易。您將需要了解IL如何工作以發出相當於「.NET彙編代碼」來生成動態方法調用者。不過,這個項目可能是一個很好的起點:http://dynamic.codeplex.com/ – bobbymcr 2011-12-27 05:29:35

回答

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