Note: 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.
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).
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.
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.