BackgroundNote: For the purpose of brevity, this blog post is going to assume that the reader already has a strong understanding of the Windows Installer architecture and philosophy. I will not attempt to fully cover the declarative and transactional design goals of
MSI or the sordid details of the pro’s and con’s of different custom action types. If you do not have this knowledge, I highly advise that you obtain it prior to writing custom actions for Windows Installer packages.
Despite Windows Installer rich feature set of Standard Actions, there often comes a time when a custom action is required to accomplish the deployment goals for an application. Windows Installer originally wanted to be 100% declarative, but it came to be understood that this was not achievable. Thus the Windows Installer team created 3 primary mechanisms for developers to inject code into the installation process: Win32
DLL,
ActiveScript (
VBS/
JS) and
EXE. As the years went by, there came to be an understanding that the Script and
EXE hosting models had some serious design limitations and points of failure. These CA types were then largely discouraged. The sole remaining technique of C++ Win32 exported
DLL functions made perfect sense back in the mid 1990’s when
MSI was created but with today’s generation of developers and platform capabilities exposed solely in .NET base class libraries, it simply no longer could meet application deployment needs in the 2000’s. At first the community struggled with this because there was still a strong desire to return back to a 100% declarative design and there was some technical problems with consuming managed code from within the Windows Installer native
unmanaged engine.
Deployment Tools Foundation: A Strong Solution
First made available to the public in
WiX weekly release 3.0.4116.0 (
http://wix.sourceforge.net/releases/3.0.4116.0/ ),
DTF provides a framework for easily and reliably writing managed code custom actions for the Windows Installer. In a nutshell, it provides a robust set of
interop classes to simplify communicating with
MSI and a hosting model to abstract the
CLR code from the
MSI process. At
runtime,
MSI thinks it’s calling a Win32
DLL in it’s own sandbox but in reality the
CLR is being fired up out of process and communicated with through a named pipe. From your managed codes perspective, you are simply communicating with
MSI through the
interop classes with no need (generally) to be concerned with all of the nightmares of
unmanaged code.
Simple Example:
Consider this code snippet that assigns a random number to a public property.

Notice the Microsoft.Deployment.WindowsInstaller reference, the [CustomAction] attribute and the Session class. Let's build the assembly and look at it in Dependency Walker:

There's a couple problems. The first is that there aren't any exported functions that MSI could understand, the second is the reference to MSCOREE.dll reference that would cause MSI to be stuck on a particular CLR version if it had been ran. We can fix this though by running a postbuild step with a DTF utility called MakeSfxCA.exe. This will package will parse the assembly for methods with the [CustomAction] attribute, package it and the dependencies into a near self extracting wrapper for consumption by MSI. Let's look at it again in Depends:

Notice the exported stdcall function now exists, there is a dependency on MSI and MSCOREE is no longer required (in this context).
Debugging
DTF supports two ways of debugging your managed code custom action. One is to attach a debugger to the process via a MessageBox and the later is a new environment variable called MMsiBreak ( not to be confused with MsiBreak ). I tried it and it worked the first time.
Things To Come
In DTF SDK has an expiremental namespace that provides LINQ capabilities to Custom Actions. This promises to greatly simplfy the process of querying the MSI database and should encourage developers in creating data driven CA's. For example, in my simple example I merely assign a random number to a single property. An improved custom action would have a RandomNumbers table with n number of rows specifying properties to be assigned a random number and perhaps a Condition column for evaluating if the assignment should occur.
Summary
If you don't mind adding the .NET framework as a dependency to your install and you want to write custom actions in managed code, DTF rocks. I really don't know any other way to put it. There were some bugs that I encountered ( mostly release defects with missing files and mismatches of strong name keys ) but nothing that I couldn't easily overcome to create this sample project. The resulting custom action can then be consumed in virtually any MSI editing tool. This is clearly a best in class solution for managed code custom actions that is long overdo.
On the other hand, if you still aren't sold on .NET and also don't want to resort to C++, then my best of class reccomendation for unmanaged code custom actions continues to be InstallScript from Acresso.