The good, the bad, and the ugly - custom action types

I'd like to add my thoughts to the recent discussion about how bad custom actions are, and which types are better or worse than others.

First of all, I believe there's a general consent that custom actions shouldn't be used for tasks that Windows Installer supports natively. Or, as Rob Mensching puts it: "Custom actions are (generally) an admission of failure" - either by the setup developer, or in the application, or by Windows Installer itself, because it's lacking some commonly required functionality.

Rob is the project leader of the open source WiX Toolset for creating Windows Installer setups. Part of the WiX project is a set of custom actions for common tasks such as interacting with IIS and SQL Server. Some people wish that such functionality would be added to Windows Installer itself, but I believe that won't happen. Alternatively the SQL, IIS, etc. teams could provide custom actions for their respective platforms, like the DIFx people did, but in most cases this hasn't happened either.

So we'll have to use custom actions, to retrieve information and set properties in the install, and to modify the target system. In general, the latter is more dangerous than the first, and requires additional effort such as creating a corresponding roll-back action.

There are even harmless types of so-called "custom actions" which are actually more like standard action: type 51 (set a property), type 35 (set a directory) and type 19 (exit with an error message).

But the discussion really is about whether EXE is worse than DLL and which programming language should be used to create custom actions. Windows Installer natively supports the following types of custom actions:

  • VBScript, JScript - These don't work on some machines, if scripting is disabled. They can also make your setup look very evil if a virus scanner flags it as potential malware (think of Office macro viruses). If the script becomes complex, it's also hard to debug.
  • EXE - Works, but doesn't have a way to communicate back to the install. So you can't return information in properties, you can't increment the progress bar and the only return values recognized by Windows Installer are success or failure. Robert Flaming of the Windows Installer team at Microsoft lists some caveats for EXE custom actions. Note that some of the problems he lists apply to "bad EXEs", so there is a chance to make it better. Also there are valid reasons to use EXE custom actions, e.g. you may already have a working and tested configuration tool which you want to re-use instead of rewriting it as DLL.
  • DLL - Typically a very robust and versatile way to create a custom action. However you should avoid runtime dependencies.

There are third party extensions that enable you to use other types of custom actions. Christopher Painter argues that "InstallScript Custom Actions are GOOD". To the Windows Installer engine they appear as DLL (the InstallScript engine) and InstallScript is a language particularly for setup related tasks. However I believe that the tasks of a custom action often aren't typical installation tasks (copying files, accessing the registry) but require calling some APIs to interact with other applications. On the other hand, it's tempting to use InstallScript to perform tasks that actually should be left to Windows Installer's standard actions. So InstallScript custom actions, while they may be useful, should be used with care. And they add the overhead, in package size and runtime, of the InstallScript engine. In general, I think it's useful to write custom actions in the programming language you know best.

As mentioned in the beginning of this article, a custom action should never be used for tasks that Windows Installer supports natively. Unfortunately this still happens. Sometimes because the setup developer is lacking knowledge about the functionality included in the MSI engine, but probably more often because the authoring tool he's using doesn't expose the full Windows Installer functionality. This leads the developer to re-invent the hidden functionality using custom actions. Namely Setup & Deployment projects in Visual Studio suffer from this problem.

The ultimate form of such a custom action is a "wrapper msi": a msi setup that mainly consist of one custom action that runs an existing setup program. Instead of re-building the setup as true Windows Installer setup, the legacy install package is just wrapped in a msi. While this enables you to deploy the wrapped setup using corporate deployment tools, you don't get the benefits of a real Windows Installer setup, such as a transacted install with rollback, customizability with transforms, robustness by auto-repair, etc. A recent example is the MSI package created by the Internet Explorer Administration Kit.

Here are some pointers to built-in Windows Installer functionality which can help you to avoid unnecessary custom actions:

[Edit 2007-11-25:] Additional information and mitigation tips for EXE custom actions can be found in Heath Stewart's blog.

Published Wednesday, November 21, 2007 4:15 PM by stefan
Filed under: ,

Comments

# re: The good, the bad, and the ugly - custom action types

I completely agree with what you have said above.

Christopher Painter is wrong.  InstallScript MSIs are never appropriate.  Leveraging the language into MSIs was dreamed up by InstallShield's marketing team to con people into 'upgrading' to InstallShield 7 saying that "you can upgrade to MSI and your old scripted package will still work"

It had no place in an MSI then, and it has none now.

Personally I like VBScript.  It has the advantages of being easy to read, open source and can be debugged from within the MSI using the MSOffice debugger.

Wednesday, November 21, 2007 10:16 AM by carl

# re: The good, the bad, and the ugly - custom action types

carl - yes, the "InstallScript MSI" (or "Standard" as it was called originally) project type has been causing lots of trouble and should be avoided. However Christopher talks about "Basic MSI" projects that may include call custom actions written in InstallScript. This can be useful, if you keep in mind that you shouldn't use it to replace native functionality. But you have to consider the overhead (and potential for bugs in the engine) opposed to a DLL written in C for instance. As I said, it will also depend on your knowledge and experience in other programming languages.

Wednesday, November 21, 2007 10:33 AM by stefan

# re: The good, the bad, and the ugly - custom action types

Carl, the main problem I have with VBScript for scripted custom actions (besides what Stefan mentioned about AV software marking it as potentially malicious) is the error handling.  Unwinding all the way out of a nested call chain to be able to return the appropriate error code to Windows Installer is tedious (but doable) in VBScript.  It requires that you daisy-chain the return value out of every called function that you write.  JScript has an exception handling mechanism that allows this to function much cleaner.

One thing that hasn't been mentioned in any of the discussions of custom actions is unit testing.  I think unit testing is going to be the best way to test all the nooks and crannies of your custom action, particularly in exercising all the error paths.  I'm not sure how you would do that in VBScript; for instance, how do you create a "fake" or "mock" object for vbscript functions that immitates the global Session variable?  You could probably do it by passing an object as the first parameter to all of your vbscript code, but its too easy to access Session in the middle and destroy that carefully laid groundwork.

There are mechanisms for doing this with C++ in such a manner that its transparent to you code -- your code never knows if you're talking to the "real" MSI API as in a production environment or if you're talking to a fake MSI API as in a testing environment.  I am working on an example in the context of updating the DirectX runtime and plan on publishing a white paper/blog entry on this in the next few months.

With adequate unit testing, I think you can avoid the unexpected pitfalls of custom actions, but that doesn't make them any easier to write properly.  It just gives you more chances to find your bugs before your product ships.

Wednesday, November 21, 2007 5:42 PM by Richard

# re: The good, the bad, and the ugly - custom action types

I don't condone using legacy InstallScript functions to intentionally reinvent the wheel instead of using native MSI patterns.  I also try to always leverage vendor provided quasi-standard action patterns ( for example InstallShield XML Search/Modify, SQL, IIS and so on).

I also recognize that including an InstallScript CA takes about a 2mb hit on a package ( last time I checked ).  That may be an important consideration for very small packages.  However for the large packages that I tend to work on, the package bloat is nominal and will never be a real issue.

The reason I like InstallScript is it does have a large repository of built-in functions for dealing with setup related tasks, the MSI API, Win32 API, COM and .NET Interop.

I also believe in writing data driven transactional custom action patterns and all of this built in robustness really helps.   I sometimes read newsgroup snippets of equivilant C++ functionality and I really have to laugh at how much simpler InstallScript is.  Then again, I also agree with Stefan, that it's probably just a matter of the posters being more comfortable in C++ and I'm more comfortable in InstallScript and C#.

Saturday, November 24, 2007 9:17 PM by Christopher Painter

# Internet Explorer Script Error » Blog Archive » The good, the bad, and the ugly - custom action types

Pingback from  Internet Explorer Script Error  » Blog Archive   » The good, the bad, and the ugly - custom action types