Thursday, July 2, 2009

Crossing the Barrier

Although the Win16 API is reasonably complete for the simple things you might want to do, there comes a time where you really wish you had access to the Win32 API to get functionality like threading, modern common dialogs, named pipes and all those goodies.

There are a number of ways to get access to the Win32 API from a 16-bit application and they each have their pros and cons. I'll detail one here and another in a followup post as they are both reasonably involved.

ActiveX EXE
VB3 has an inbuilt ability to call into 32-bit out-of-process ActiveX components. Out-of-process means that the component runs in its own 32-bit process and communicates to the 16-bit application via some sort of IPC that I don't care about. If you have the knowledge and wish to fill me in, please do so!
Example (VB3):

Dim oActiveX As Object
oActiveX = CreateObject("MyObjectPackage.MyObjectName")
oActiveX.SomeFunctionName("Parameter1", 2, "3")

This looks very close to how VBScript instantiates and uses ActiveX components because it is exactly the same mechanism.

Note that when accessing the component from 16-bit code, it has to be an EXE as 32-bit DLLs cannot be loaded into 16-bit address space. This is the method we started off using - writing the ActiveX EXEs in VB6 and accessing them from VB3 - and it was very smooth until we were made aware of the fact that users like to use our application in a "thin client" setup.

I've termed this setup "thin client" for want of a better term. In a standalone install, the application (around 4 GB) would be installed on the target machine and used by one user at a time. Our users cottoned on to the fact that they could share the application directory and use the application from any machine on the (Windows) network by mapping a network drive to that location and creating a shortcut to the main executable. Smart.

The reason this is a popular setup is because the application contains critical business information which is updated on a regular basis- monthly at least. So every month we stamp thousands of DVDs/CDs and courier them to our customers who dutifully install them or use them as coasters. The install on an old machine - Pentium 3 and 4 with 4x or 8x CD-ROMs are the average - takes around 45 minutes. A client that has a dozen or more machines to install the application on quickly looks for a faster solution, hence the "thin client" approach. (By the way if you think our business model is begging for a web application, so do we and we're in the midst of building it).

This approach totally hamstrings our use of 32-bit ActiveX EXEs because of how the mechanism works. When VB3 requests an ActiveX object to be created and passes its ID (known as an AppID I think), Windows will look in the registry under HKEY_CLASSES_ROOT\{AppID} for the ID. If it finds it, it will retrieve the CLSID (a GUID) and look up HKEY_CLASSES_ROOT\CLSID\{CLSID} to find the EXE/DLL which implements the object. If a user is executing the application from a remote machine, they won't have the AppID or CLSID of our object in their registry so the application will fail.

I have come up with a stop-gap solution by writing a script to distribute with the application that will register the ActiveX EXE locally if it is being run across the network but this is an extra step our users have to take (albeit a small one) but any extra step they have to take is a personal failure to me.

A cleaner solution must be found! See my post "All That Thunk" for my cleaner solution although it makes you feel dirty inside (coming soon).