Friday, February 15, 2008 11:47 PM bart

The managed installer custom actions cookbook

While I'm in the mood of writing up those cookbook posts:

let's do another one. Custom actions are a powerful way to extend MSI-based installers with custom code. Maybe you've run into these before when you created for example a managed Windows Service and hooked it up in the setup project (essentially installutil-able components can be added to an installer by means of a custom action). In this post we'll write our own (dummy) custom action and show how to debug it nicely, something that seems to be a barrier for quite some developers to consider writing a custom action.

 

Step 1 - Create a class library project

Once more, Class Library is your friend:

image

 

Step 2 - Add references

Choose Add References from the context menu on the project node in Solution Explorer. In there, select System.Configuration.Install:

image

 

Step 3 - Plumbing the wires

Custom actions in managed code are wrapped in Installer-subclasses, so derive your class from Installer:

image

and import the System.Configuration.Install namespace. In order for the installer class to be picked up at runtime, you need to attribute it with RunInstaller(true):

image 

This is the result:

image

 

Step 4 - Add functionality

In order to make the custom action do something, you need to override some methods of the base class. I've indicated the most common ones:

image

These correspond to the actions taken by MSI. Let's just override Install and Commit and play a little with state that's passed around (let's not go in depth, more information is available in MSDN):

image

 

Step 5 - Make it debuggable

Our goal is to attach a debugger but custom actions are launched somewhere in the middle of some MSI process. How can we allow for easy debugging? Here's a way to do it: instrument your code with some wait mechanisms to allow for attaching the debugger. A nice way is to use MessageBox. To do this, you'll need to add System.Windows.Forms to the references of the project (as in step 2) and import the namespace:

image

By wrapping these in DEBUG #if's we make sure the code doesn't make it to Release builds, which is good.

 

Step 6 - The setup project

Time to build the setup project. Add a new project to the solution and choose for a Setup Project:

image

The project will open in File System view. Go to the Application Folder, right-click in the right-hand side pane and choose Add Project Output:

image

Select Primary Output for the MyCustomAction project created above:

image

This will add the DLL file to the installation folder:

image

Now it's time to add the Custom Action to the installer (in technical terms to the to-be-built MSI database). Make sure the setup project is selected in Solution Explorer and select Custom Actions Editor from the toolbar:

image

Adding the actions is simple:

image

and select the Primary output from MyCustomAction from above:

image

Do the same for the Commit node.

image

 

Step 7 - Build and debug

That's it. Time for a test run. First, build the MyCustomAction project. Next, right-click the setup project node and choose Build. This is not done when building the solution (since it takes quite some time and you likely do it only sporadically in a bigger solution):

image

Next, right-click the project again and choose Install:

image

Here we go. Click your way through the installer and wait. On Vista you'll need to give UAC consent. After a while you'll see:

image

Don't click OK yet. Switch back to Visual Studio and choose Debug, Attach to Process:

image

You won't find the Attach debugger here dialog in the list at first sight, that's because the custom action is running under the context of the Installer Service in an msiexec.exe instance running under a different (service account) user. Mark 'Show processes from all users' to see it:

image

and click Attach. On Vista you might see the following when you didn't start Visual Studio elevated:

image

Elevation is needed because you're about to debug something with more privileges and rights (running under SYSTEM after all). Choose Restart under different credentials and accept the UAC prompt. Visual Studio will come back in the same project but you'll have to repeat the previous steps to attach the debugger. Notice the user name is now displayed in the dialog:

image

Set breakpoints on the instructions right below the #if DEBUG section:

image

and click OK on the dialog:

image

You'll see the breakpoint being hit:

image

Woohoo! Feel free to step though the (one) line(s) of the custom action and finally hit F5. Now the commit dialog appears and when dismissing it, we'll end up on the next breakpoint:

image

notice the state from the install phase was recovered:

image

 

Enjoy!

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Comments

# Debug di Custom Actions

Saturday, February 16, 2008 2:01 AM by BabbaBlog

Debug di Custom Actions

# Link Listing - February 15, 2008

Saturday, February 16, 2008 5:01 AM by Christopher Steen

MSBuild The custom MSBuild task cookbook [Via: bart ] ASP.NET Simplifying ASP.NET ListView Control...

# Link Listing - February 15, 2008

Saturday, February 16, 2008 5:01 AM by Christopher Steen

Link Listing - February 15, 2008

# re: The managed installer custom actions cookbook

Saturday, February 16, 2008 7:24 AM by Tom

Better to use Debugger.Break() to attach the debugger directly...

# re: The managed installer custom actions cookbook

Saturday, February 16, 2008 1:51 PM by bart

Hi Tom,

Thanks for the feedback. The approach you suggest works as well and I guess it's a matter of taste. On Vista, it's an interesting case of how far the system goes to preserve the right isolation boundaries. Actually, what you end up doing is causing a faked failure condition in the Windows Installer process running the custom action (under SYSTEM), so you'll see setup "crash" with the message in the line of "(process) has hit a user-defined breakpoint". The thing I slightly dislike about it is the pause caused by the "search for solutions" before one can hit "Debug" - but that of course is subjective too.

Thanks,

-Bart

# re: The managed installer custom actions cookbook

Monday, February 18, 2008 2:13 AM by Steve

There's only one problem with this -- managed code is sticky; once you've loaded one version of the run-time that's it for that process.

Gory details here:

robmensching.com/.../Managed-Code-CustomActions-no-support-on-the-way-and-heres.aspx

# re: The managed installer custom actions cookbook

Monday, February 18, 2008 2:45 PM by bart

Hi Steve,

Sure - this is a general .NET limitation concerning the way the EE is loaded in the process space. The teams are definitely aware of this.

Although this is a real implementation, it shouldn't hit people too much if all you're doing is customizing the application setup with a few CA's which are typically tightly related to the app you're deploying so most likely you're bound to a certain .NET FX release.

Nevertheless this article points out how to debug such a custom action in case you want to go around and create such a thing in managed code, without considering downsides such as the one you bring up. There are btw many places where CA's are used by the platform itself such a for managed service installers, PowerShell snap-ins, etc.

Thanks,

-Bart

# The managed MMC 3.0 snap-in cookbook

Wednesday, February 27, 2008 12:04 AM by B# .NET Blog

My recent series of "cookbook" posts has been very well-received and coincidentally I got mail