Saturday, June 09, 2007

DataSnap to the rescue

I'm maintaining a custom setup program used to install applications. In some cases it needs to perform actions which require administrative privileges, e.g. writing into Program Files directory, or modifying the registry under HKEY_LOCAL_MACHINE.

On Windows Vista, the privileges can be acquired by elevation. However, elevation actually switches the user context; but the setup program also needs to perform some actions within the context of the current user: create registry entries under HKEY_CURRENT_USER, copy files into their "My Documents" folder, etc. This means that I cannot simply use manifest to request administrator privileges statically.

Therefore, elevation needs to take place dynamically at runtime. COM elevation moniker can be used for this purpose. This blog post is very useful in that regard. (Thanks, Aleksander!)

So I've written an ActiveX DLL, an automation object with methods to expose file streams, create and delete directories, access registry and so on - things which need to be done under an administrator's account.

But then I realized that this would require two UAC prompts: first to register the ActiveX DLL (and it does have to be in the registry, registration-free activation doesn't seem to suffice in this case), and second to actually use the elevation moniker.

To show only one UAC prompt, I decided to use two separate processes: server running elevated (via manifest) and client running under the current user's account. Since I already had written the automation object, I decided to reuse it. For the inter-process communication, I decided to use DataSnap with TSharedMemoryConnection (a memory-mapped file transport) which I've written for the purpose.

To reduce unnecessary registry clutter, instantiation of the COM class in the server process is simplified, no CoCreateObject is called and therefore no registration is required. Instead, the type information needed to dispatch method calls is used from the type library resource directly.

The client has the server linked in as a resource; if it detects that it's not running with sufficient privileges it extracts it to a temporary directory and launches it from there.

It all works very nicely and, IMHO, is another example of how powerful and flexible DataSnap is.