<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
	xmlns:media="http://search.yahoo.com/mrss/"
>

<channel>
	<title>dev{shaped} &#187; custom actions</title>
	<atom:link href="http://devshaped.com/tag/custom-actions/feed/" rel="self" type="application/rss+xml" />
	<link>http://devshaped.com</link>
	<description></description>
	<lastBuildDate>Mon, 13 Jul 2009 14:47:13 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.6</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
		<!-- podcast_generator="podPress/8.8" -->
		<copyright>&#xA9; </copyright>
		<managingEditor>derek@webradius.com ()</managingEditor>
		<webMaster>derek@webradius.com()</webMaster>
		<category></category>
		<itunes:keywords></itunes:keywords>
		<itunes:subtitle></itunes:subtitle>
		<itunes:summary></itunes:summary>
		<itunes:author></itunes:author>
		<itunes:category text="Society &amp; Culture"/>
		<itunes:owner>
			<itunes:name></itunes:name>
			<itunes:email>derek@webradius.com</itunes:email>
		</itunes:owner>
		<itunes:block>No</itunes:block>
		<itunes:explicit>no</itunes:explicit>
		<itunes:image href="http://devshaped.com/wp-content/plugins/podpress/images/powered_by_podpress_large.jpg" />
		<image>
			<url>http://devshaped.com/wp-content/plugins/podpress/images/powered_by_podpress.jpg</url>
			<title>dev{shaped}</title>
			<link>http://devshaped.com</link>
			<width>144</width>
			<height>144</height>
		</image>
		<item>
		<title>Creating Useful Installers with Custom Actions</title>
		<link>http://devshaped.com/2009/04/creating-useful-installers-with-custom-actions/</link>
		<comments>http://devshaped.com/2009/04/creating-useful-installers-with-custom-actions/#comments</comments>
		<pubDate>Wed, 01 Apr 2009 09:00:00 +0000</pubDate>
		<dc:creator>Derek</dc:creator>
				<category><![CDATA[Practical Programming]]></category>
		<category><![CDATA[custom actions]]></category>
		<category><![CDATA[installers]]></category>
		<category><![CDATA[programming]]></category>

		<guid isPermaLink="false">http://devshaped.com/2009/04/creating-useful-installers-with-custom-actions/</guid>
		<description><![CDATA[by Christian Jacob
 So you are almost done with implementing a ground breaking, state of the art, super duper plugin-enabled calculator application with syntax highlighting and now you are wondering how to get it deployed? In case you are already trying to put some arguments together to convince your boss for buying a suite to [...]]]></description>
			<content:encoded><![CDATA[<p>by <a href="http://www.trimagination.info/" target="_blank">Christian Jacob</a></p>
<p><img style="border-bottom: 0px; border-left: 0px; margin: 0px 0px 22px 22px; display: inline; border-top: 0px; border-right: 0px" title="christian-jacob" border="0" alt="christian-jacob" align="right" src="http://devshaped.com/wp-content/uploads/2009/03/christianjacob.jpg" width="85" height="57" /> So you are almost done with implementing a ground breaking, state of the art, super duper plugin-enabled calculator application with syntax highlighting and now you are wondering how to get it deployed? In case you are already trying to put some arguments together to convince your boss for buying a suite to building Microsoft Installer packages worth several thousand dollars: <b>Stop! Don’t do that.</b> Well, at least not if your application is not as complex as let’s say your development environment.</p>
<p>Using Microsoft Visual Studio you should be able to create an MSI in literally no time by using the predefined Setup Project template and if you need to accomplish additional tasks that Windows Installer does not support out of the box, read on to find out how to extend your installer with own custom actions.</p>
<p> <span id="more-121"></span><br />
<h2>The Vision</h2>
<p>A couple of years ago market researcher Evans Data noticed a massive drop of C++ developers and also forecasted a continuous decrease. Bjarne Stroustrup on the other hand said that the number of C++ developers is higher than ever (he’s the inventor of C++ by the way). Whoever you trust, you are most probably reading this because you want to know how to use C# or VB.NET for writing custom actions in Visual Studio instead of using good old C++. So let’s dive into that topic by creating a not so fictional case scenario: The first task you would want to accomplish is creating an eventlog source for your application (because you read that the installation of an application is the suggested moment for doing just that)</p>
<p>Note: We will do a lot more than that, so I have included a small sample solution for you to easily reproduce what I am talking about.  You can download it from <a href="http://devshaped.com/files/MyCalcSln.zip">http://devshaped.com/files/MyCalcSln.zip</a>.</p>
<h2>The Solution</h2>
<p>As I said earlier, you could use C++ to implement a native custom action. However, the easiest solution for getting this done using C# or VB.NET is deriving a class from the <i>Installer Class</i> and overriding the <i>Install</i> method. Then you would add your custom action to the <i>Custom Action Table</i> of the installer. </p>
<p>So let’s start!</p>
<h2>Create the Custom Action</h2>
<p>Add an <i>Installer </i>by right clicking your project and select Add &gt; New Item &gt; Installer Class. Name it CreateEventSource.cs for now.</p>
<p><a href="http://devshaped.com/wp-content/uploads/2009/03/clip-image002.jpg"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image002" border="0" alt="clip_image002" src="http://devshaped.com/wp-content/uploads/2009/03/clip-image002-thumb.jpg" width="500" height="308" /></a></p>
<p><em>Figure 1- Adding an Installer Class</em></p>
<p>Next right click the CreateEventSource.cs file in the solution explorer and select <i>View Code</i>. Replace it with the following (you may want to adjust the namespace and the actual event source name):</p>
<div style="font-family: courier new; font-size: 9pt"><span style="color: blue">namespace</span> MyCalcWin.Custom_Actions     <br />{&#160; <br />&#160; <span style="color: blue">using</span> System;&#160; <br />&#160; <span style="color: blue">using</span> System.ComponentModel;&#160; <br />&#160; <span style="color: blue">using</span> System.Configuration.Install;&#160; <br />&#160; <span style="color: blue">using</span> System.Diagnostics;&#160; </p>
<p>&#160; [RunInstaller(<span style="color: maroon">true</span>)]&#160; <br />&#160; <span style="color: blue">public</span>&#160;<span style="color: blue">partial</span>&#160;<span style="color: blue">class</span> CreateEventSource : Installer&#160; <br />&#160; {&#160; <br />&#160;&#160;&#160; <span style="color: blue">const</span>&#160;<span style="color: blue">string</span> EventSourceName = <span style="color: maroon">&quot;MyCalc&quot;</span>;&#160; </p>
<p>&#160;&#160;&#160; <span style="color: blue">public</span> CreateEventSource()&#160; <br />&#160;&#160;&#160; {&#160; <br />&#160;&#160;&#160;&#160;&#160; InitializeComponent();&#160; <br />&#160;&#160;&#160; }&#160; <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <br />&#160;&#160;&#160; <span style="color: blue">public</span>&#160;<span style="color: blue">override</span>&#160;<span style="color: blue">void</span> Install(System.Collections.IDictionary stateSaver)&#160; <br />&#160;&#160;&#160; {&#160; <br />&#160;&#160;&#160;&#160;&#160; <span style="color: blue">try</span>&#160; <br />&#160;&#160;&#160;&#160;&#160; {&#160; <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: blue">base</span>.Install(stateSaver);&#160; <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; EventLog.CreateEventSource(EventSourceName, <span style="color: maroon">&quot;Application&quot;</span>);&#160; <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; EventLog.WriteEntry(EventSourceName, <span style="color: maroon">&quot;Eventsource created&quot;</span>);&#160; <br />&#160;&#160;&#160;&#160;&#160; }&#160; <br />&#160;&#160;&#160;&#160;&#160; <span style="color: blue">catch</span> (Exception ex)&#160; <br />&#160;&#160;&#160;&#160;&#160; {&#160; <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.Windows.Forms.MessageBox.Show(&#160; <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: blue">string</span>.Format(<span style="color: maroon">&quot;Error performing installer tasks: {0}&quot;</span>, ex.Message));&#160; <br />&#160;&#160;&#160;&#160;&#160; }&#160; <br />&#160;&#160;&#160; }&#160; </p>
<p>&#160;&#160;&#160; <span style="color: blue">public</span>&#160;<span style="color: blue">override</span>&#160;<span style="color: blue">void</span> Uninstall(System.Collections.IDictionary savedState)&#160; <br />&#160;&#160;&#160; {&#160; <br />&#160;&#160;&#160;&#160;&#160; <span style="color: blue">try</span>&#160; <br />&#160;&#160;&#160;&#160;&#160; {&#160; <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: blue">base</span>.Uninstall(savedState);&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; EventLog.DeleteEventSource(EventSourceName);&#160; <br />&#160;&#160;&#160;&#160;&#160; }&#160; <br />&#160;&#160;&#160;&#160;&#160; <span style="color: blue">catch</span> (Exception ex)&#160; <br />&#160;&#160;&#160;&#160;&#160; {&#160; <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.Windows.Forms.MessageBox.Show(&#160; <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: blue">string</span>.Format(<span style="color: maroon">&quot;Error permorming uninstaller task: {0}&quot;</span>, ex.Message));&#160; <br />&#160;&#160;&#160;&#160;&#160; }&#160; <br />&#160;&#160;&#160; }&#160; <br />&#160; }     <br />}     </div>
<p>As you can see, deriving from the <i>Installer</i> base class is not enough. You need to add the attribute <i>RunInstaller</i> to the class and initialize it with <i>true</i> as shown above. This basically marks the <i>CreateEventSource</i> class as an <i>Installer</i> class and makes it visible to reflection.</p>
<h2>Create the setup project</h2>
<p>Simply add a new <i>Setup Project</i> to your solution by right clicking your solution in the solution explorer and then Add &gt; New Project &gt; Other Project Types &gt; Setup Project as shown in the following figure:</p>
<p><a href="http://devshaped.com/wp-content/uploads/2009/03/clip-image004.jpg"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image004" border="0" alt="clip_image004" src="http://devshaped.com/wp-content/uploads/2009/03/clip-image004-thumb.jpg" width="500" height="325" /></a></p>
<p><em>Figure 2 &#8211; Adding a Setup Project</em></p>
<p>To let the <i>Setup Project</i> know what to install, right click the project and select Add &gt; Project Output… Make sure your application project is selected as <i>Project</i> and choose <i>Primary Output</i>. Click <i>OK</i>. </p>
<p><a href="http://devshaped.com/wp-content/uploads/2009/03/clip-image006.jpg"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image006" border="0" alt="clip_image006" src="http://devshaped.com/wp-content/uploads/2009/03/clip-image006-thumb.jpg" width="280" height="313" /></a></p>
<p><em>Figure 3 &#8211; Adding Project Output to Setup Project</em></p>
<p>With these simple steps you just created a setup that is able to install your application. Now you would make sure your custom actions are called and configure some simple settings. Right click the <i>Setup Project</i> in the solution explorer and select View &gt; Custom Actions.</p>
<p>Now right click the <i>Custom Actions</i> node and select <i>Add Custom Action</i>. Look in <i>File System on Target Machine</i> and double click <i>Application Folder</i>. Select the <i>Primary Output</i> from your application project and click <i>OK</i>.</p>
<p><a href="http://devshaped.com/wp-content/uploads/2009/03/clip-image008.jpg"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image008" border="0" alt="clip_image008" src="http://devshaped.com/wp-content/uploads/2009/03/clip-image008-thumb.jpg" width="221" height="135" /></a></p>
<p><em>Figure 4 &#8211; Custom Action View</em></p>
<p>Easy. This will create the eventlog source for you by making use of the <i>Installer Class</i> you created earlier. If you would build your setup now and install it, the application will be defaulted to [ProgramFilesFolder][Manufacturer]\[ProductName]. Of course it is up to you to change the default target path. In my case, C:\Program Files\TOP TECHNOLOGIES CONSULTING GmbH\MyCalc would be a little like breaking a fly on the wheel. So I right click the <i>Setup Project</i> in the solution explorer and select View &gt; File System and choose <i>Application Folder</i>. In the properties pane, I can configure the <i>DefaultLocation</i> to something like [ProgramFilesFolder][ProductName]. That’s better.</p>
<h2>Install your application</h2>
<p>Now let’s finally make a real job of it. Right click the <i>Setup Project</i> in the solution explorer and select <i>Build</i>. After being built successfully, right click the project again and select <i>Install</i>.</p>
<p>You should be familiar with the dialogs that follow. However, after your setup successfully installed your top notch calculator, have a look at the event viewer by clicking <i>Start</i> and entering <i>eventvwr</i>. </p>
<p>You will find the following entry in the application log:</p>
<p><a href="http://devshaped.com/wp-content/uploads/2009/03/clip-image010.jpg"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image010" border="0" alt="clip_image010" src="http://devshaped.com/wp-content/uploads/2009/03/clip-image010-thumb.jpg" width="495" height="344" /></a></p>
<p><em>Figure 5 &#8211; Eventlog Entry</em></p>
<h2>Observations</h2>
<p>When using a fully featured environment for creating and using <i>Custom Actions</i> such as <i>InstallShield</i> from Acresso or <i>Windows Installer Xml</i> from Microsoft, you have absolute control over when and how your custom action is actually called. You can start it during the interview phase of your setup (aka UI sequence), during the immediate phase of the execution sequence when the actual installation script is generated or use it as a deferred custom action that is run in within the system context while the system changes are really applied. </p>
<p><i>Custom Actions</i> implemented as <i>Installer Classes</i> are <b>only run deferred in the system context</b>. That means that your application is already successfully installed at that time. You have full access to all your files and also the configuration. You could implement a <i>Custom Action</i> for instance that alters configuration settings of your application based upon properties set by the user during the UI sequence (I will show you how later).</p>
<p>If you want to dive into the details of the Microsoft Windows Installer installation process, go ahead and read Alex Shevchuck’s TechNet entry <a href="http://blogs.technet.com/alexshev/archive/2008/02/21/how-windows-installer-engine-installs-the-installation-package.aspx">here</a>. This is a great resource for a decent understanding about what’s going on under the hood.</p>
<h2>Potentialities (or: What the heck is CustomActionData?)</h2>
<p>When looking at the choices in the <i>View</i> context menu of your <i>Setup Project</i>, you already noticed the following entries:</p>
<ul>
<li>File System </li>
<li>Registry </li>
<li>File Type </li>
<li>User Interface </li>
<li>Custom Actions </li>
<li>Launch Conditions </li>
</ul>
<p>What most certainly caught your attention is the <i>User Interface</i> entry. Yes! You can really add custom UI dialogs to a <i>Visual Studio Setup Project</i>. Although it is possible creating real custom dialogs, you can instantly start using own dialogs by choosing from the provided templates. Since we already agreed that we are talking about relatively simple applications here, in most cases that will get you started immediately (if you really really need more than that, look <a href="http://www.codeproject.com/KB/install/vsSetupCustomDialogs.aspx">here</a>).</p>
<h2>Changing Application Settings During Installation </h2>
<p>Let’s have some fun with custom dialogs to do something really useful here you most probably won’t instantly find by poking keywords into a search engine. Looking into the functional specification of your calculator you notice that one of your bosses (the one who usually has the most intriguing ideas of all) demanded that the calculator should welcome the user with his/her name. You heard him mumbling something about “Unique Selling Point” while he left the conference room…</p>
<p>So here is what you do:</p>
<h3>Create a splash screen</h3>
<p>Right click your calculator application and select Add &gt; Windows Form. Name the form Welcome.cs and set its properties to the following values: </p>
<p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td valign="top" width="123">
<p><b>Property</b></p>
</td>
<td valign="top" width="102">
<p><b>Value</b></p>
</td>
</tr>
<tr>
<td valign="top" width="123">
<p>FormBorderStyle</p>
</td>
<td valign="top" width="102">
<p>None</p>
</td>
</tr>
<tr>
<td valign="top" width="123">
<p>ShowInTaskbar</p>
</td>
<td valign="top" width="102">
<p>False</p>
</td>
</tr>
<tr>
<td valign="top" width="123">
<p>TopMost</p>
</td>
<td valign="top" width="102">
<p>True</p>
</td>
</tr>
<tr>
<td valign="top" width="123">
<p>StartPosition</p>
</td>
<td valign="top" width="102">
<p>CenterScreen</p>
</td>
</tr>
</tbody>
</table>
<p>Drag and drop three label controls into the form like this:</p>
<p><a href="http://devshaped.com/wp-content/uploads/2009/03/clip-image012.jpg"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image012" border="0" alt="clip_image012" src="http://devshaped.com/wp-content/uploads/2009/03/clip-image012-thumb.jpg" width="170" height="79" /></a></p>
<p><em>Figure 6 &#8211; Borderless splash screen</em></p>
<h3>Create and bind an application setting to a label</h3>
<p>Now configure the UserNameLabel label. In the property pane expand the ApplicationSettings node and click the ellipsis button (…) to the right of the Property Binding setting. Select the Text property, expand the drop down list and select New… Enter UserName as Name and Application as Scope (we&#8217;ll assume that the user who installs the calculator won&#8217;t let anyone else start it).</p>
<p>At this moment, Visual Studio automatically creates an app.config file for you that should basically look like this:</p>
<pre style="font-size: 9pt"><span style="color: blue">&lt;?</span><span style="color: maroon">xml</span> <span style="color: red">version</span>=&quot;<span style="color: blue">1.0</span>&quot; <span style="color: red">encoding</span>=&quot;<span style="color: blue">utf-8</span>&quot; <span style="color: blue">?&gt;</span>
<span style="color: blue">&lt;</span><span style="color: maroon">configuration</span><span style="color: blue">&gt;</span>
  <span style="color: blue">&lt;</span><span style="color: maroon">configSections</span><span style="color: blue">&gt;</span>
      <span style="color: blue">&lt;</span><span style="color: maroon">sectionGroup</span> <span style="color: red">name</span>=&quot;<span style="color: blue">applicationSettings</span>&quot; <span style="color: red">type</span>=&quot;<span style="color: blue">System.Configuration.ApplicationSettingsGroup,
          System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</span>&quot; <span style="color: blue">&gt;</span>
          <span style="color: blue">&lt;</span><span style="color: maroon">section</span> <span style="color: red">name</span>=&quot;<span style="color: blue">MyCalcWin.Properties.Settings</span>&quot;
              <span style="color: red">type</span>=&quot;<span style="color: blue">System.Configuration.ClientSettingsSection, System, Version=2.0.0.0,
              Culture=neutral, PublicKeyToken=b77a5c561934e089</span>&quot; <span style="color: red">requirePermission</span>=&quot;<span style="color: blue">false</span>&quot; /<span style="color: blue">&gt;</span>
      <span style="color: blue">&lt;</span>/<span style="color: maroon">sectionGroup</span><span style="color: blue">&gt;</span>
  <span style="color: blue">&lt;</span>/<span style="color: maroon">configSections</span><span style="color: blue">&gt;</span>
  <span style="color: blue">&lt;</span><span style="color: maroon">applicationSettings</span><span style="color: blue">&gt;</span>
      <span style="color: blue">&lt;</span><span style="color: maroon">MyCalcWin.Properties.Settings</span><span style="color: blue">&gt;</span>
          <span style="color: blue">&lt;</span><span style="color: maroon">setting</span> <span style="color: red">name</span>=&quot;<span style="color: blue">UserName</span>&quot; <span style="color: red">serializeAs</span>=&quot;<span style="color: blue">String</span>&quot;<span style="color: blue">&gt;</span>
              <span style="color: blue">&lt;</span><span style="color: maroon">value</span> /<span style="color: blue">&gt;</span>
          <span style="color: blue">&lt;</span>/<span style="color: maroon">setting</span><span style="color: blue">&gt;</span>
      <span style="color: blue">&lt;</span>/<span style="color: maroon">MyCalcWin.Properties.Settings</span><span style="color: blue">&gt;</span>
  <span style="color: blue">&lt;</span>/<span style="color: maroon">applicationSettings</span><span style="color: blue">&gt;</span>
<span style="color: blue">&lt;</span>/<span style="color: maroon">configuration</span><span style="color: blue">&gt;</span></pre>
<p>If you have a close look at the Welcome.Designer.cs file you will find the following line that does the binding for you:</p>
<div style="font-family: courier new; font-size: 9pt"><span style="color: blue">this</span>.UserNameLabel.Text = global::MyCalcWin.Properties.Settings.Default.UserName;</div>
<h3>Let the splash screen appear at startup</h3>
<p>To make the splash screen appear, adjust the Load method of your main form like this:</p>
<div style="font-family: courier new; font-size: 9pt"><span style="color: blue">private</span>&#160;<span style="color: blue">void</span> Form1_Load(<span style="color: blue">object</span> sender, EventArgs e) </p>
<p>{ </p>
<p>&#160; <span style="color: blue">this</span>.Hide(); </p>
<p>&#160; var splashScreen = <span style="color: blue">new</span> Welcome(); </p>
<p>&#160; splashScreen.Show(); </p>
<p>&#160; splashScreen.Update(); </p>
<p>&#160; System.Threading.Thread.Sleep(<span style="color: maroon">5000</span>); </p>
<p>&#160; splashScreen.Close(); </p>
<p>&#160; <span style="color: blue">this</span>.Visible = <span style="color: maroon">true</span>; </p>
<p>} </p>
<p></div>
<p>Now let’s finally wire things up with the setup project.</p>
<h3>Create a custom dialog</h3>
<p>Right click the Setup Project in the solution explorer and select View &gt; User Interface. Right click the Start node in the Install tree and select Add dialog. Choose Textboxes (A) and click OK.</p>
<p><a href="http://devshaped.com/wp-content/uploads/2009/03/clip-image014.jpg"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image014" border="0" alt="clip_image014" src="http://devshaped.com/wp-content/uploads/2009/03/clip-image014-thumb.jpg" width="380" height="279" /></a></p>
<p><em>Figure 7 &#8211; Adding a Custom Dialog</em></p>
<p>Drag and drop the new dialog in front of the Confirm Installation node. Select it and change its properties to the following values: </p>
<p><table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td valign="top" width="104">
<p><b>Property</b></p>
</td>
<td valign="top" width="515">
<p><b>Value</b></p>
</td>
</tr>
<tr>
<td valign="top" width="104">
<p>BannerText</p>
</td>
<td valign="top" width="515">
<p>Enter your name</p>
</td>
</tr>
<tr>
<td valign="top" width="104">
<p>BodyText</p>
</td>
<td valign="top" width="515">
<p>MyCalc is the only calculator that will welcome you personally on each startup! Just tell it who you are.</p>
</td>
</tr>
<tr>
<td valign="top" width="104">
<p>Edit1Label</p>
</td>
<td valign="top" width="515">
<p>Your name:</p>
</td>
</tr>
<tr>
<td valign="top" width="104">
<p>Edit1Property</p>
</td>
<td valign="top" width="515">
<p>USERNAME</p>
</td>
</tr>
<tr>
<td valign="top" width="104">
<p>Edit2Visible</p>
</td>
<td valign="top" width="515">
<p>False</p>
</td>
</tr>
<tr>
<td valign="top" width="104">
<p>Edit3Visible</p>
</td>
<td valign="top" width="515">
<p>False</p>
</td>
</tr>
<tr>
<td valign="top" width="104">
<p>Edit4Visible</p>
</td>
<td valign="top" width="515">
<p>False</p>
</td>
</tr>
</tbody>
</table>
<p>Now right click again the Setup Project in the solution explorer and select View &gt; Custom Actions. Select Primary Output from MyCalcWin (Active) right under the Install node and change its CustomActionData property value to /userName=&quot;[USERNAME]&quot;.</p>
<h3>Create a new Custom Action</h3>
<p>Now comes the tricky part. We are talking here of at least a .NET 2.0 application. As we already saw, we are working with real type safe and <i>supposedly</i> easy to use settings utilizing the data bound Settings.settings file. Fortunately, .NET already generated a type safe wrapper for us. Unfortunately you cannot access the settings as easily as from within the application due to the fact that the installer simply does not have a mapper for it (and we won’t supply it with one).</p>
<p>What you can do though is this:</p>
<p>Create another custom action as you already did before, call it RegisterUser and replace the generated code with the following:</p>
<div style="font-family: courier new; font-size: 9pt"><span style="color: blue">namespace</span> MyCalcWin.Custom_Actions </p>
<p>{ </p>
<p>&#160; <span style="color: blue">using</span> System.Collections; </p>
<p>&#160; <span style="color: blue">using</span> System.ComponentModel; </p>
<p>&#160; <span style="color: blue">using</span> System.Configuration; </p>
<p>&#160; <span style="color: blue">using</span> System.Configuration.Install; </p>
<p>&#160; <span style="color: blue">using</span> System.Xml; </p>
<p>&#160; [RunInstaller(<span style="color: maroon">true</span>)] </p>
<p>&#160; <span style="color: blue">public</span>&#160;<span style="color: blue">partial</span>&#160;<span style="color: blue">class</span> RegisterUser : Installer </p>
<p>&#160; { </p>
<p>&#160;&#160;&#160; <span style="color: blue">public</span> RegisterUser() </p>
<p>&#160;&#160;&#160; { </p>
<p>&#160;&#160;&#160;&#160;&#160; InitializeComponent(); </p>
<p>&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160; <span style="color: blue">public</span>&#160;<span style="color: blue">override</span>&#160;<span style="color: blue">void</span> Install(IDictionary stateSaver) </p>
<p>&#160;&#160;&#160; { </p>
<p>&#160;&#160;&#160;&#160;&#160; <span style="color: blue">base</span>.Install(stateSaver); </p>
<p>&#160;&#160;&#160;&#160;&#160; <span style="color: green">// Get username from context</span> </p>
<p>&#160;&#160;&#160;&#160;&#160; var userName = <span style="color: blue">this</span>.Context.Parameters[<span style="color: maroon">&quot;userName&quot;</span>]; </p>
<p>&#160;&#160;&#160;&#160;&#160; <span style="color: green">// Context contains assemblypath by default</span> </p>
<p>&#160;&#160;&#160;&#160;&#160; var assemblyPath = <span style="color: blue">this</span>.Context.Parameters[<span style="color: maroon">&quot;assemblypath&quot;</span>]; </p>
<p>&#160;&#160;&#160;&#160;&#160; <span style="color: green">// Get UserName setting in app.config</span> </p>
<p>&#160;&#160;&#160;&#160;&#160; var config = ConfigurationManager.OpenExeConfiguration(assemblyPath); </p>
<p>&#160;&#160;&#160;&#160;&#160; var sectionGroup = config.GetSectionGroup(<span style="color: maroon">&quot;applicationSettings&quot;</span>); </p>
<p>&#160;&#160;&#160;&#160;&#160; var section = sectionGroup.Sections[<span style="color: maroon">&quot;MyCalcWin.Properties.Settings&quot;</span>]<span style="color: blue">&#160;</span></div>
<div style="font-family: courier new; font-size: 9pt"><span style="color: blue">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; as</span> ClientSettingsSection; </p>
<p>&#160;&#160;&#160;&#160;&#160; var settingElement = section.Settings.Get(<span style="color: maroon">&quot;UserName&quot;</span>); </p>
<p>&#160;&#160;&#160;&#160;&#160; <span style="color: green">// Create new value node</span> </p>
<p>&#160;&#160;&#160;&#160;&#160; var doc = <span style="color: blue">new</span> XmlDocument(); </p>
<p>&#160;&#160;&#160;&#160;&#160; var newValue = doc.CreateElement(<span style="color: maroon">&quot;value&quot;</span>); </p>
<p>&#160;&#160;&#160;&#160;&#160; newValue.InnerText = userName; </p>
<p>&#160;&#160;&#160;&#160;&#160; <span style="color: green">// Set new UserName value</span> </p>
<p>&#160;&#160;&#160;&#160;&#160; settingElement.Value.ValueXml = newValue; </p>
<p>&#160;&#160;&#160;&#160;&#160; <span style="color: green">// Save changes</span> </p>
<p>&#160;&#160;&#160;&#160;&#160; section.SectionInformation.ForceSave = <span style="color: maroon">true</span>; </p>
<p>&#160;&#160;&#160;&#160;&#160; config.Save(ConfigurationSaveMode.Modified); </p>
<p>&#160;&#160;&#160; } </p>
<p>&#160; } </p>
<p>} </div>
<p>As you can see, we can access the Custom Action Data we prepared earlier through the InstallContext Parameters collection. Also the InstallContext already contains a couple of default parameters such as the <i>assemblypath</i> (which we need to access the applications’ configuration file).</p>
<p>What we are doing here is loading the .exe.config file of our application, digging through to the UserName setting, creating a new Xml node containing the username value and finally putting the value into the configuration before we are saving the changes. It is really as easy as that. Wasn’t that obvious?</p>
<p>Now compile and install your application. </p>
<p>The setup will ask you for your username:</p>
<p><a href="http://devshaped.com/wp-content/uploads/2009/03/clip-image016.jpg"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image016" border="0" alt="clip_image016" src="http://devshaped.com/wp-content/uploads/2009/03/clip-image016-thumb.jpg" width="402" height="330" /></a></p>
<p><em>Figure 8 &#8211; User registration dialog</em></p>
<p>When starting the calculator, the splash screen welcomes you:</p>
<p><a href="http://devshaped.com/wp-content/uploads/2009/03/clip-image018.jpg"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image018" border="0" alt="clip_image018" src="http://devshaped.com/wp-content/uploads/2009/03/clip-image018-thumb.jpg" width="220" height="102" /></a></p>
<p><em>Figure 9 &#8211; Actual splash screen</em> </p>
<h2>Alternatives</h2>
<p>Over and above the samples you saw so far there are several alternatives to using the Installer Class provided by Visual Studio. One is using native C++ (the languageWindows Installer is actually developed in, which is why it is the most powerful way of implementing custom actions). Explaining how that works is out of the scope of this article, but you can read about it for example on Steven Bone’s Blog who published three absolutely awesome articles about it <a href="http://bonemanblog.blogspot.com/2005/10/custom-action-tutorial-part-i-custom.html">here</a>.</p>
<p>A native custom action that accesses the MSI log for instance basically looks like this:</p>
<pre style="font-size: 9pt">

<span style="color: blue">extern</span> <span style="color: maroon">&quot;C&quot;</span> UINT __stdcall Install(MSIHANDLE hInstall)
{
  PMSIHANDLE hRecord = MsiCreateRecord(<span style="color: maroon">0</span>);
  MsiRecordSetString(hRecord, <span style="color: maroon">0</span>, TEXT(<span style="color: maroon">&quot;Native C++ is better than Installer Class!&quot;</span>));
  MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_INFO), hRecord);
  <span style="color: blue">return</span> ERROR_SUCCESS;
}
</pre>
<p>Using DTF (Deployment Tools Foundation), which is part of the Windows Installer Xml Toolset, enables you to implement Custom Actions with managed code. However, you need to make sure .NET Framework is already installed on the computer you are running the setup on and also have a couple of prerequisites to fulfill.</p>
<p><table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td valign="top" width="242">
<p><b>Action</b></p>
</td>
<td valign="top" width="105">
<p align="center"><b>Installer Class</b></p>
</td>
<td valign="top" width="112">
<p align="center"><b>Native C++ DLL</b></p>
</td>
<td valign="top" width="73">
<p align="center"><b>WiX DTF</b></p>
</td>
</tr>
<tr>
<td valign="top" width="242">
<p><b>Run in UI sequence</b></p>
</td>
<td valign="top" width="105">&#160;</td>
<td valign="top" width="112">
<p align="center">X</p>
</td>
<td valign="top" width="73">
<p align="center">X</p>
</td>
</tr>
<tr>
<td valign="top" width="242">
<p><b>Run immediate in execute sequence</b></p>
</td>
<td valign="top" width="105">&#160;</td>
<td valign="top" width="112">
<p align="center">X</p>
</td>
<td valign="top" width="73">
<p align="center">X</p>
</td>
</tr>
<tr>
<td valign="top" width="242">
<p><b>Run deferred in execute sequence</b></p>
</td>
<td valign="top" width="105">
<p align="center">X</p>
</td>
<td valign="top" width="112">
<p align="center">X</p>
</td>
<td valign="top" width="73">
<p align="center">X</p>
</td>
</tr>
<tr>
<td valign="top" width="242">
<p><b>Read MSI properties</b></p>
</td>
<td valign="top" width="105">
<p align="center">X<a href="#_ftn1_1368" name="_ftnref1_1368">[1]</a></p>
</td>
<td valign="top" width="112">
<p align="center">X</p>
</td>
<td valign="top" width="73">
<p align="center">X</p>
</td>
</tr>
<tr>
<td valign="top" width="242">
<p><b>Write MSI properties</b></p>
</td>
<td valign="top" width="105">&#160;</td>
<td valign="top" width="112">
<p align="center">X</p>
</td>
<td valign="top" width="73">
<p align="center">X</p>
</td>
</tr>
<tr>
<td valign="top" width="242">
<p><b>Debuggable</b></p>
</td>
<td valign="top" width="105">
<p align="center">X</p>
</td>
<td valign="top" width="112">
<p align="center">X</p>
</td>
<td valign="top" width="73">
<p align="center">X <a href="#_ftn2_1368" name="_ftnref2_1368">[2]</a></p>
</td>
</tr>
<tr>
<td valign="top" width="242">
<p><b>Access MSI Logfile</b></p>
</td>
<td valign="top" width="105">&#160;</td>
<td valign="top" width="112">
<p align="center">X</p>
</td>
<td valign="top" width="73">
<p align="center">X</p>
</td>
</tr>
<tr>
<td valign="top" width="242">
<p><b>Access Installstate log</b></p>
</td>
<td valign="top" width="105">
<p align="center">X</p>
</td>
<td valign="top" width="112">&#160;</td>
<td valign="top" width="73">&#160;</td>
</tr>
<tr>
<td valign="top" width="242">
<p><b>.NET Framework must be installed</b></p>
</td>
<td valign="top" width="105">
<p align="center">X</p>
</td>
<td valign="top" width="112">&#160;</td>
<td valign="top" width="73">
<p align="center">X</p>
</td>
</tr>
<tr>
<td valign="top" width="242">
<p><b>Windows Installer Xml must be installed</b></p>
</td>
<td valign="top" width="105">&#160;</td>
<td valign="top" width="112">&#160;</td>
<td valign="top" width="73">
<p align="center">X</p>
</td>
</tr>
</tbody>
</table>
<h2>Conclusion</h2>
<p>In many cases, installation needs are straightforward enough that it is not worth the effort to implement anything more complex than the Custom Actions that run deferred in system context. Although you gain more control over the installation process when you choose to implement <i>Windows Installer Xml</i> based <i>Custom Actions</i> or native CA’s, <em>Installer </em>class based <i>Custom Actions</i> can be a great way to extend your Visual Studio based <i>Setup Project</i> without the need to invest in additional installer solutions.</p>
<hr align="left" size="1" width="33%" />
<p><a href="#_ftnref1_1368" name="_ftn1_1368">[1]</a> Through stateSaver dictionary collection</p>
<p><a href="#_ftnref2_1368" name="_ftn2_1368">[2]</a> Simple unit testing possibility via wrapping the MSI session</p>
<p>&#8212;&#8211;</p>
<p>Christian Jacob is an IT Consultant with TOP TECHNOLOGIES CONSULTING, a Microsoft Gold Partner with focus on infrastructures, security and business solutions. He primarily uses C# and VB.NET in nowadays development and&#160; has his roots in C++ based game development. He spends a lot of time in researching and creating innovative solutions around team development, Windows Installer, and TOP TECHNOLOGIES’ main software products. He currently resides nearby Hamburg in North Germany.</p>
]]></content:encoded>
			<wfw:commentRss>http://devshaped.com/2009/04/creating-useful-installers-with-custom-actions/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
	</channel>
</rss>
