Saturday, January 22, 2005

Unicode (Unicows.dll) and ActiveX

I have been wrestling this week with how to get an ActiveX control which has to invoke Microsoft Unicode APIs (as provided via Unicows.dll) to install on Windows 98. The use of Unicode APIs is forced on me by required code that is autogenerated (so I have to treat it as immutable); and given constraints on installer effort, I was limited to not having multiple .CAB files on the web server.

The .CAB file I started with contains unicows.dll and my DLL all in what I believe to be the correct order; both in the CABARC call and the .INF file (Unicows, then my DLL). The resulting .CAB installs OK on NT derived platforms; but on Win9x and WinMe you get to see the "Do you want to install this ActiveX" dialog - and nothing happens when you say yes. There may be some partial install into the \Windows\Downloaded Program Files folder if you visit it in a DOS box, but nothing shows up in Explorer.

The cause seemed to be that the registration is called before Unicows.dll is installed.

So I thought - instead of "unicows.dll control.dll", put "unicows.dll control.dll helper.dll", tell the CAB file to register through helper.dll, and implement DLLRegisterServer and DllUnregisterServer helper.dll to manually un/register the appropriate CLSID on behalf of control.dll, without actually invoking it.

No dice. However instrumenting helper.dll showed that the DllMain was called to attach and detach, without calling any entrypoint; so I suspect that libraries are tested in some temporary folder with LoadLibrary() before being installed - and control.dll was failing there, every time (assuming that unicows.dll wasn't on the path somewhere).

Now if I'd been able to put all the files I wanted on the server, and was able to figure out where the control was being loaded from, I could have done my first idea, which was to download the bootstrap helper.dll and then fetch the rest. Constrained as I was, I would just have to smuggle the code inside the same .CAB file; indeed inside the helper.dll library, as the only place I could be certain of having full control of what was happening.

So, I thought, let's build control.dll, then add it as an RT_RCDATA resource into helper.dll, and just put "unicows.dll helper.dll" in the .INF and .CAB. Then I could make DllRegisterServer unpack the resource into a file (deleting it in DllUnregisterServer).

Success!

Now when you say yes to the control, the helper.dll library is loaded and unloaded from a process, then loaded again to call DllRegisterServer, and that unpacks control.dll and registers it, and the control executes, and all is well in the garden.

If only there were a simpler way than this.

[LATER] The issue is that even if unicows comes first in the .cab and .inf files, the implicit "." in the path that works everywhere else doesn't work here. You have to put unicows into a folder that is on the path - which in practice means c:\windows (destDir=10). You discover this when trying the above strategy and then trying to be clever by doing a LoadLibrary on the file you've just unpacked and calling its DllRegisterServer.

At least on Win9x, there's no worry about needing admin privilege to drop things into the system folders.

No comments :