This is the story of the day I discovered that there’s something simple about Windows Installer.
When it comes to creating installer packages, the thought of using the Windows Installer service fills the average developer with dread. Calling Windows Installer unapproachable and unintuitive is an understatement. It is only thanks to Rob Mensching and the gang working on the Windows Installer XML (WiX) toolkit that it is even approachable. Unfortunately the progress on WiX is slow, although admittedly not quite as slow as Window Clippings. As John Robbins said to me the other day, don’t get me started on how no one at Microsoft cares about install and just leaves poor Rob out there on his own.
I have tried to streamline the Window Clippings installer such that all you see is a simple progress window (and possibly a security prompt) which takes just a few seconds to complete.
That’s it. No wizard with ten pages of meaningless junk you have to get through. It just gets on with it so that you can get on with using Window Clippings.
To streamline the update process I made sure that Window Clippings supports the Restart Manager (RM) so that it can be automatically restarted via the RM API that the Windows Installer service uses. For some reason the Window Installer service doesn’t trust the Restart Manager to do the right thing and wants the user to take some responsibility, so it throws this window at the user:
Of course I’ve already made sure that Window Clippings can be safely restarted so I really didn’t want this ugly prompt to stop the update process in its tracks.
I had read the page in the Windows Installer SDK about using Windows Installer with the Restart Manager and figured I could use the MsiSetExternalUI function to suppress the prompt. But then I read the fineprint:
MsiSetExternalUI should only be called from a Bootstrapping application. You cannot call MsiSetExternalUI from a custom action.
It’s at this point that I realized that WiX can’t help me with this problem and I was filled with trepidation. I asked John and he mentioned the work on Burn. That sounded complicated. After a while I collected myself and turned back to the Windows Installer SDK and realized something: not everything about Windows Installer is complicated! I know, it’s hard to believe but it’s true. Writing a bootstrapping application for Windows Installer can, at least for my simple needs, be accomplished with very little code. The MsiInstallProduct function takes the place of msiexec.exe to install the package. All that’s left is to call MsiSetExternalUI with a simple callback that always returns IDOK and the Restart Manager prompt goes away. Here is the entire bootstrap application:
#pragma comment(lib, "msi.lib")
static int CALLBACK ExternalCallback(void * /*context*/,
int WINAPI wWinMain(HINSTANCE /*instance*/,
nullptr); // context
return MsiInstallProduct(L"path or URL to .msi file",
nullptr); // command line
And that’s it. I now have a streamlined install and update process without any unnecessary prompts. In future I can even use the bootstrap application to easily skin the progress window but this will do for now.
As an aside, I then compiled a release build and was disappointed that this simple application took a staggering 38KB so I checked with Steve Miller and he showed me how simple it is to get rid of the C Runtime for simple applications that don’t rely on the runtime’s initialization code. You just need to manually set your entry point to wWinMain and the linker will throw away it’s stub code. You can find this in your project’s advanced linker settings. That got me down to under 10KB (including the VeriSign digital signature and common controls manifest).
I hope this story saves you some unnecessary anxiety. And as Rob always says, keep coding… you know I am!