It is generally well-known that an ActiveX DLL can be made to export selected entrypoints, allowing one to call these DLL's directly via the API.
This is achieved by customising the final step (the link step) in the MAKE process, which produces the DLL from an ActiveX project.
This method, both well-documented and widely used for some years now, only solves one aspect the general VB6 API DLL problem, however - a VB6 DLL called directly is still very restricted in what it can do, and will often abort the client process, because it will not perform some critical runtime initialisation which a normally-deployed ActiveX DLL does automatically.
That's simply because the required runtime initialisation is not embedded in the DLL's entrypoint function (DllMain
), but is done by the DLL's DllGetClassObject
function. For ActiveX DLL's that are built conventionally, the DLL is of course a class library, and this function gets called whenever a client application requests the creation of a new object instance from that DLL.
This initialisation is COM-related, so I call it "DLL COM Initialisation", even though the DLL's we wish to build are not providing objects (they are intended to provide functions that are directly callable via the Win32 API).
The key point is that this COM-initialisation is always
required in any application involving a VB6 module. If the appn is a VB6 exe, it's done by the app itself. If the appn is a non-VB client, then it would normally only involve a VB6 DLL if it was using CreateObject
to get an object instance from a registered ActiveX DLL, and the required initialisation is performed automatically, as I described above.
If a non-VB client calls the DLL directly via the API, then the DLL must itself perform the COM initialisation, otherwise the client application will invariably crash.
About 12 months ago, I wrote a technical paper in which I described this process, and how a custom-linked DLL could be made to do this COM initialisation (a surprisingly simple process).
I went one step further, also, showing that it was also possible for such a DLL to support a GUI - that is, to display VB6 Forms - just so long as they were managed properly.
Attached to this thread are the technical paper itself, together with a copy of the link-customisation tool used in the examples.
Here is the "abstract" entry from the document:
VB6 programs at runtime make significant use of COM interfaces, even if the source code does not use COM objects explicitly. Some critical runtime support mechanisms, including error handling, are COM-based (eg. the Err object). VB6 applications (EXE’s) automatically initialise their COM environment when executed, but VB6 DLL’s only initialise COM when the client application uses the DLL via a COM interface, not as an API (a shared library).
If a client application calls a VB6 DLL function directly, the DLL’s runtime state has no COM capability, and so the DLL will abort if it makes any sort of COM object reference. This has fatal consequences for the entire client process.
We present a method for building a VB6 DLL that can initialise its COM environment automatically. This allows VB6 DLL’s to be deployed as shared libraries, so they can be serve as API’s for clients written in other languages.
We begin by showing how a client can manually initialise a VB6 library, via the DLL’s DllGetClassObject function. Then we show how this method can be automated by implementing it in the DLL’s own entrypoint function.
Finally, we look at the issue of VB6 Form display, and show that a VB6 library can indeed provide a non-modal GUI, even with a non-VB client, with some careful programming.