Running unmanaged code using as much managed code as possible
A.E.Veltstra
Feb. 28, 2007
Imagine you (or a third party) created an API, and published it in a dynamic link library (or dll for short) which works perfectly. Imagine you were taught about Windows API and wish to continue using that knowledge. A lot of time was invested into creating the library, and there's no real need to invest more time to convert it to .Net. However, you now program in Visual Basic.Net and you wish to use some API methods from that old dll. How?
In the C language, calling an api method was relatively easy. Using the LoadLibrary() and GetProcAddress() api functions from the kernel32 library we fetch a pointer to the method, put that pointer into a variable and then execute it. It simply works.
In Visual Basic.Net, this technique doesn't work. Why? Because in VB, the pointer to a method is an integer... not a method. We need to do something extra with that pointer before we can execute anything. In the .Net Framework 1.0 and 1.1 this tended to be quite cumbersome. People solved it using helper dlls that were written in C and published to .Net. It works, but there's a problem: it uses too much unmanaged code.
What's the big deal about unmanaged code? Aren't you going to run a method from an unmanaged library anyway? Yeah, sure. But I'd like to keep my code execution as managed as possible, and only resort to unmanaged code if there is no other way.
But since I already know the library and the method, can't I simply create a reference to the library and add its methods to my project? Doesn't that immediately provide as much unmanaged code as possible? Sure that works... if it's a COM or a .Net library, and you already know the file path of the library. But what if you that isn't the case? What if, for instance, the dll is neither COM-visible nor a .Net assembly? What if the dll were part of an installation package, and thus you had no knowledge of its final path? In those cases you can't reference the libraries in design time. You need something more dynamic. Something that works in run time.
Luckily the .Net Framework 2.0 and up support Marshalling, and the function that is of interest to us is Marshal.GetDelegateForFunctionPointer(). Remember that function pointer we retrieved using GetProcAddress()? Using GetDelegateForFunctionPointer() we can go the extra mile: we can convert it into a delegate which then can be invoked dynamically. Et voilĂ , it simply works. Dynamically.
I successfully applied this method inside a custom Setup Application, in order to register and unregister dlls and ocx's using their built-in DllRegisterServer() and DllUnregisterServer() functions. Obviously, at design time I wouldn't know which dlls will be registered, nor at what location they will be placed. I only know the apis.
Example
Obviously I left a few things out for sake of simplicity. For instance, what happens if the api method expects parameters? (Have a close look at the DynamicInvoke() function and the delegate function declaration.) And, what happens if the api or its library couldn't be located? My example includes no error handling. So there's plenty of work left.
Now I know I said I currently use this technique in a custom Setup Application to register dlls and ocx's. So why didn't I simply use System.Diagnostics.Process or VisualBasic.Interaction.Shell to create a new process and run RegSvr32.exe? Because that application does exactly the same thing: it loads and executes the library's DllRegisterServer() method. Why shell out a task that we can perform ourselves?
Tags
.net, vb.net, visual basic, framework 2.0, managed, unmanaged, dll, library, com, com+, loadlibrary, call, getprocaddress, interop, marshal, getdelegateforfunctionpointer, dynamicinvoke, runtime, run time, run-time, design time, designtime, design-time, dynamic
In the C language, calling an api method was relatively easy. Using the LoadLibrary() and GetProcAddress() api functions from the kernel32 library we fetch a pointer to the method, put that pointer into a variable and then execute it. It simply works.
In Visual Basic.Net, this technique doesn't work. Why? Because in VB, the pointer to a method is an integer... not a method. We need to do something extra with that pointer before we can execute anything. In the .Net Framework 1.0 and 1.1 this tended to be quite cumbersome. People solved it using helper dlls that were written in C and published to .Net. It works, but there's a problem: it uses too much unmanaged code.
What's the big deal about unmanaged code? Aren't you going to run a method from an unmanaged library anyway? Yeah, sure. But I'd like to keep my code execution as managed as possible, and only resort to unmanaged code if there is no other way.
But since I already know the library and the method, can't I simply create a reference to the library and add its methods to my project? Doesn't that immediately provide as much unmanaged code as possible? Sure that works... if it's a COM or a .Net library, and you already know the file path of the library. But what if you that isn't the case? What if, for instance, the dll is neither COM-visible nor a .Net assembly? What if the dll were part of an installation package, and thus you had no knowledge of its final path? In those cases you can't reference the libraries in design time. You need something more dynamic. Something that works in run time.
Luckily the .Net Framework 2.0 and up support Marshalling, and the function that is of interest to us is Marshal.GetDelegateForFunctionPointer(). Remember that function pointer we retrieved using GetProcAddress()? Using GetDelegateForFunctionPointer() we can go the extra mile: we can convert it into a delegate which then can be invoked dynamically. Et voilĂ , it simply works. Dynamically.
I successfully applied this method inside a custom Setup Application, in order to register and unregister dlls and ocx's using their built-in DllRegisterServer() and DllUnregisterServer() functions. Obviously, at design time I wouldn't know which dlls will be registered, nor at what location they will be placed. I only know the apis.
Example
''''''''''''''''''''''''''''''''''''''''''''''''''''
''
'' Copyright A.E.Veltstra, Feb. 28, 2007
'' Free to reuse with this copyright notice intact.
''
'' Requires .Net Framework 2.0
''
'' Usage: To dynamically execute api calls from dlls of
'' which no information is available at design time,
'' except for the api itself.
''
''''''''''''''''''''''''''''''''''''''''''''''''''''
Private Declare Auto Function LoadLibrary Lib "kernel32" _
(ByVal LibFilePath As String) As Integer
Private Declare Function GetProcAddress Lib "kernel32" _
(ByVal ModuleHandle As Integer, ByVal ProcName As String) _
As Integer
Private Declare Function FreeLibrary Lib "kernel32" _
(ByVal ModuleHandle As Integer) As Integer
Private Delegate Function DllSelfRegister() As Integer
'' delegate function acts as a typed instance of the api method
''----------------------------------------------------
'' example function to register a dll or ocx
Public Function DllRegister(ByVal strFileName As String) _
As Boolean
Return (ApiInvoke(strFileName, _
"DllRegisterServer", GetType(DllSelfRegister)
) = 0)
End Function
'' Dynamic invocation of unmanaged api method
Private Function ApiInvoke(ByVal LibFilePath As String, _
ByVal MethodName As String, _
ByVal DelegateFunctionType As System.Type
) As Object
Dim MethodPointer As Integer, ModuleHandle As Integer
Dim ret As Object
ModuleHandle = LoadLibrary(LibFilePath)
MethodPointer = GetProcAddress(ModuleHandle, MethodName)
ret = Marshal.GetDelegateForFunctionPointer( _
MethodPointer, DelegateFunctionType _
).DynamicInvoke()
FreeLibrary(ModuleHandle)
Return ret
End Function
Obviously I left a few things out for sake of simplicity. For instance, what happens if the api method expects parameters? (Have a close look at the DynamicInvoke() function and the delegate function declaration.) And, what happens if the api or its library couldn't be located? My example includes no error handling. So there's plenty of work left.
Now I know I said I currently use this technique in a custom Setup Application to register dlls and ocx's. So why didn't I simply use System.Diagnostics.Process or VisualBasic.Interaction.Shell to create a new process and run RegSvr32.exe? Because that application does exactly the same thing: it loads and executes the library's DllRegisterServer() method. Why shell out a task that we can perform ourselves?
Tags
.net, vb.net, visual basic, framework 2.0, managed, unmanaged, dll, library, com, com+, loadlibrary, call, getprocaddress, interop, marshal, getdelegateforfunctionpointer, dynamicinvoke, runtime, run time, run-time, design time, designtime, design-time, dynamic
Need problem solving?
Talk to me. Let's meet for coffee or over lunch. Mail me at “omegajunior at protonmail dot com”.