I am using some interesting code to perform dynamic unmanaged dll calling:
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
I want to be able to change the parameter signature and type of the delegate to void or to a function returning integer, string, or boolean.
Basically, I want my program (interpreter) to be able to call upon any method in any unmanaged dll that the programmer has access to... since I cannot predict what method the programmer will want to have access to - I'd like to enable them to have access to any useful method.
This seems like it's possible in vb.net - perhaps with reflection? - but I'm just not sure how to do it.
--- EDIT: Here's what I've come up with:
<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
This raises a complaint on the line with 'Dim theResult = ...' on it. The error is "Object of type 'System.Int32' cannot be converted to type 'System.Object[]'."
I seem to be getting somewhere but... where? I don't really know.
I've got it. I can now dynamically call methods:
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