-
Notifications
You must be signed in to change notification settings - Fork 3
Dynamic Loading of 7 Zip Library
Because the project can work with either 7z.dll
, 7za.dll
or 7zxa.dll
, it presents some issues with using the exported function. The original version used Declare
statement which worked OK when it was just one function being used. However, the DLLs exports more than just one function so having to write Declare
statement for n * 3 exported functions becomes unwieldy quickly. In the original version, we were already using the LoadLibrary
to help us avoid hard-coding the path to the DLL in the Declare
statement. But we cannot actually change the Lib
parameters to match the filename, a different approach is needed.
There is an existing project that demonstrates how to call a library's function without using a Declare
statement at all; cUniversalCall
. This leverages the DispCallFunc
but with some wrapping to handle the arguments being passed to the function.
Because it was written for 32-bit VB6, it was necessary to update to update it so that it can run on both 32-bit and 64-bit. Furthermore, in the original version, the library was cached on the first used. This way, subsequent use of same library will not require another LoadLibrary
. However, if a different DLL is used, the original handle is freed with FreeLibrary
then LoadLibrary
called. Within the comments in the original project, it is suggested to creating an instance for each library loaded. This provided an opportunity to demonstrate how twinBASIC's support for parameterized constructors can be used to self-describe the API. Instead of having to write a separate instruction telling the potential consumers to first create a class, then call a Init
method, the consumer simply calls the parameterized constructor Sub New(LibraryPath As String)
which becomes obvious that the library path is a pre-requisite for the class to function and errors are returned if the parameter is not valid (e.g. the library doesn't exist at that path or cannot be loaded).
The other difference is that in the original class, a string representing the procedure name was passed in, which had to be then looked up and converted into a function pointer. Caching the function pointer made more sense but this is not the class' responsibility to do so since it won't know the shape of the function. Instead, the encapsulating class should cache the function pointer, and provide a stub to call the function. As an example, consider the GetHandlerProperty
function in the LibraryManager
:
'STDAPI GetHandlerProperty(PROPID propID, PROPVARIANT *value);
Public Function GetHandlerProperty( _
ByVal propID As ItemPropId, _
ByRef value As Variant _
) As Long
Dim hr As Long = This.Caller.CallDllFunction( _
This.Functions.GetHandlerProperty, STR_NONE, CR_LONG, CC_STDCALL, CLng(propID), VarPtr(value))
CastPropVariantToVariant(value)
Return hr
End Function
The stub handles the required conversions to properly call the function, so it knows more about the function's shape and thus can reference the cached function pointer.