[VB6] Call Functions By Pointer (Universall DLL Calls)
I was recently made aware that a function I've used from time to time for calling virtual functions of COM objects was perfectly adept at calling functions from just about any standard DLL out there. So, I whipped up a 'generic' class that can call both standard DLL functions & COM VTable functions. No thunks are used, just a couple of supporting API calls in the main class, including the low-level core API function: DispCallFunc
What does this mean for you? Well, it does allow you to call DLL functions from nearly 10 different calling conventions, including the two most common: StdCall & CDecl. It also allows you to call virtual functions from COM objects. And if you wish, it means you do not have to declare a single API function declaration in your VB project. Though, personally, I'd use it for calling DLL conventions other than StdCall.
I'd consider this topic advanced for one reason only. This is very low level. If you provide incorrect parameter information to the class, your project is likely to crash. For advanced coders, we have no problem doing the research to understand what parameter information is required, be it variable type, a pointer, a pointer to a pointer, function return types, etc, etc. Not-so-advanced coders just want to plug in values & play, but when playing at such a low level, that usually results in crashes, frustration.
The attachment includes very simple examples of calling DLL functions and calling a COM virtual function. You will notice that the form has no API function declarations, though several DLL functions are called & executed correctly. A sample call to the class might look like:
For DLL calls, the class takes the DLL name and function name to be called. Technically, you aren't passing the function pointer to the class. However, the class does make the call to the pointer, not via declared API functions. Just thought I'd throw this comment in, should someone suggest we aren't really calling functions by pointer. The class is, the user calling the class is not, but can be if inclined to modify the code a bit.
Limitations: Callbacks from non-StdCall DLLs/functions
If whatever function you are calling requires a callback pointer, then stack corruption is likely from all calling conventions where you pass a VB function pointer as the callback address. The exceptions are stdCall DLLs and also CDecl calls, if the thunk/patch option in the class is used.
Tip: If you really like this class, you may want to instantiate one for each DLL you will be calling quite often. This could speed things up a bit when making subsequent calls. As is, the class will load the requested DLL into memory if it isn't already. Once class is called again, for a different DLL, then the previous DLL is unloaded if needed & the new DLL loaded as needed. So, if you created cUser32, cShell32, cKernel32 instances, less code is executed in the class if it doesn't have to drop & load DLLs.
vb Code:
' top of form
Private cUser32 As cUniversalDLLCalls
Private cKernel32 As cUniversalDLLCalls
Private cShell32 As cUniversalDLLCalls
' in form load
Set cUser32 = New cUniversalDLLCalls
Set cKernel32 = New cUniversalDLLCalls
Set cShell32 = New cUniversalDLLCalls
' now use cUser32 for all user32.dll calls, cKernel32 for kernel32, cShell32 for shell32, etc
Tip: When using the STR_ANSI flag to indicate the passed parameters include string values destined for ANSI functions, the class will convert the passed string to ANSI before calling the function. Doing so, default Locale is used for string conversion. If this is a problem, you should ensure you convert the string(s) to ANSI before passing it to the class. If you do this conversion, use STR_NONE & pass the string via StrPtr(). FYI: strings used strictly as a buffer for return values should always be passed via StrPtr() and the flag STR_NONE used; regardless if destined for ANSI or unicode functions. ANSI strings are never passed to COM interfaces. Always use StrPtr(theString) for any string parameters to those COM methods.
Tip: If you ever need to call a private COM interface function by its pointer/address, post #24 below shows how that can be done. A slight modification to the attached class is required.
Change History
- 28 Nov 2014. Added thunk/patch/workaround for passing a VB function address to a CDECL dll that expects a CDECL callback address. Sample found in post #10 below
See this thread for more info. Win10 update broke paramarrays in some scenarios. Within the CallFunction_DLL & CallFunction_COM methods of the enclosed class, modify these lines of code in both methods:
Code:
Change this:
vParams() = FunctionParameters() ' copy passed parameters, if any
pCount = Abs(UBound(vParams) - LBound(vParams) + 1&)
To this:
If UBound(FunctionParameters) <> -1 Then
vParams() = FunctionParameters() ' copy passed parameters, if any
pCount = Abs(UBound(vParams) - LBound(vParams) + 1&)
End If
Last edited by LaVolpe; Sep 2nd, 2019 at 10:12 AM.
Reason: updated comments
Insomnia is just a byproduct of, "It can't be done"