<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:pingback="http://madskills.com/public/xml/rss/module/pingback/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
  <channel>
    <title>Jelle Druyts - Blog|Programming|.NET|GuidanceAutomation</title>
    <link>http://jelle.druyts.net/</link>
    <description>Reflection.Emit()</description>
    <language>en-us</language>
    <copyright>Jelle Druyts</copyright>
    <lastBuildDate>Mon, 03 Mar 2008 09:39:16 GMT</lastBuildDate>
    <generator>newtelligence dasBlog 2.0.7226.0</generator>
    <managingEditor>blog@druyts.net</managingEditor>
    <webMaster>blog@druyts.net</webMaster>
    <item>
      <trackback:ping>http://jelle.druyts.net/Trackback.aspx?guid=4ae2894a-8c87-4ee2-8da2-0e94dd199b11</trackback:ping>
      <pingback:server>http://jelle.druyts.net/pingback.aspx</pingback:server>
      <pingback:target>http://jelle.druyts.net/PermaLink.aspx?guid=4ae2894a-8c87-4ee2-8da2-0e94dd199b11</pingback:target>
      <dc:creator>Jelle Druyts</dc:creator>
      <wfw:comment>http://jelle.druyts.net/CommentView.aspx?guid=4ae2894a-8c87-4ee2-8da2-0e94dd199b11</wfw:comment>
      <wfw:commentRss>http://jelle.druyts.net/SyndicationService.asmx/GetEntryCommentsRss?guid=4ae2894a-8c87-4ee2-8da2-0e94dd199b11</wfw:commentRss>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
        </p>
        <p>
          <a href="http://blogs.msdn.com/tommer">Tom</a>'s team has been kind enough to put
my session of last year's <a href="http://www.techdays.be/">TechDays</a> (then still
known as the Developer &amp; IT Pro Days) online on <a href="http://www.microsoft.com/belux/msdn/nl/chopsticks/default.aspx">MSDN
Chopsticks</a>. You can find my "<a href="http://www.microsoft.com/belux/msdn/nl/chopsticks/default.aspx?id=10">Deep
Dive Into The Guidance Automation Toolkit</a>" presentation at <a title="http://www.microsoft.com/belux/msdn/nl/chopsticks/default.aspx?id=10" href="http://www.microsoft.com/belux/msdn/nl/chopsticks/default.aspx?id=10">http://www.microsoft.com/belux/msdn/nl/chopsticks/default.aspx?id=10</a>.
Everything I said back then is still relevant today, so if you missed it last year
you can now catch up for free :-)
</p>
        <p>
And in the light of Software Factory technologies, it also makes a nice preparation
for my talk on <a href="http://jelle.druyts.net/2008/02/18/DSLToolsSessionAtTechDaysInBelgium.aspx">Domain-Specific
Development with Visual Studio DSL Tools</a> next week. My session is <a href="http://www.microsoft.com/belux/heroeshappenhere/program.aspx">scheduled
on Thursday March 13 at 10:45</a>. I'm really looking forward to it, and I hope to
see you there!
</p>
        <p>
          <a title="TechDays 2008" href="http://www.techdays.be/">
            <img src="http://jelle.druyts.net/content/binary/WindowsLiveWriter/DSLToolssessionatTechDaysinBelgium_7C86/signature_speaker_3.gif" />
          </a>
        </p>
        <img width="0" height="0" src="http://jelle.druyts.net/aggbug.ashx?id=4ae2894a-8c87-4ee2-8da2-0e94dd199b11" />
      </body>
      <title>My &amp;quot;Deep Dive Into The Guidance Automation Toolkit&amp;quot; presentation now online!</title>
      <guid isPermaLink="false">http://jelle.druyts.net/PermaLink.aspx?guid=4ae2894a-8c87-4ee2-8da2-0e94dd199b11</guid>
      <link>http://jelle.druyts.net/2008/03/03/MyQuotDeepDiveIntoTheGuidanceAutomationToolkitquotPresentationNowOnline.aspx</link>
      <pubDate>Mon, 03 Mar 2008 09:39:16 GMT</pubDate>
      <description>&lt;p&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://blogs.msdn.com/tommer"&gt;Tom&lt;/a&gt;'s team has been kind enough to put
my session of last year's &lt;a href="http://www.techdays.be/"&gt;TechDays&lt;/a&gt; (then still
known as the Developer &amp;amp; IT Pro Days) online on &lt;a href="http://www.microsoft.com/belux/msdn/nl/chopsticks/default.aspx"&gt;MSDN
Chopsticks&lt;/a&gt;. You can find my "&lt;a href="http://www.microsoft.com/belux/msdn/nl/chopsticks/default.aspx?id=10"&gt;Deep
Dive Into The Guidance Automation Toolkit&lt;/a&gt;" presentation at &lt;a title="http://www.microsoft.com/belux/msdn/nl/chopsticks/default.aspx?id=10" href="http://www.microsoft.com/belux/msdn/nl/chopsticks/default.aspx?id=10"&gt;http://www.microsoft.com/belux/msdn/nl/chopsticks/default.aspx?id=10&lt;/a&gt;.
Everything I said back then is still relevant today, so if you missed it last year
you can now catch up for free :-)
&lt;/p&gt;
&lt;p&gt;
And in the light of Software Factory technologies, it also makes a nice preparation
for my talk on &lt;a href="http://jelle.druyts.net/2008/02/18/DSLToolsSessionAtTechDaysInBelgium.aspx"&gt;Domain-Specific
Development with Visual Studio DSL Tools&lt;/a&gt; next week. My session is &lt;a href="http://www.microsoft.com/belux/heroeshappenhere/program.aspx"&gt;scheduled
on Thursday March 13 at 10:45&lt;/a&gt;. I'm really looking forward to it, and I hope to
see you there!
&lt;/p&gt;
&lt;p&gt;
&lt;a title="TechDays 2008" href="http://www.techdays.be/"&gt;&lt;img src="http://jelle.druyts.net/content/binary/WindowsLiveWriter/DSLToolssessionatTechDaysinBelgium_7C86/signature_speaker_3.gif"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;img width="0" height="0" src="http://jelle.druyts.net/aggbug.ashx?id=4ae2894a-8c87-4ee2-8da2-0e94dd199b11" /&gt;</description>
      <comments>http://jelle.druyts.net/CommentView.aspx?guid=4ae2894a-8c87-4ee2-8da2-0e94dd199b11</comments>
      <category>Blog</category>
      <category>Blog/General</category>
      <category>Blog/Programming</category>
      <category>Blog/Programming/.NET</category>
      <category>Blog/Programming/.NET/DSLs</category>
      <category>Blog/Programming/.NET/GuidanceAutomation</category>
    </item>
    <item>
      <trackback:ping>http://jelle.druyts.net/Trackback.aspx?guid=5244194c-e970-421c-8bd5-1447ca298a3c</trackback:ping>
      <pingback:server>http://jelle.druyts.net/pingback.aspx</pingback:server>
      <pingback:target>http://jelle.druyts.net/PermaLink.aspx?guid=5244194c-e970-421c-8bd5-1447ca298a3c</pingback:target>
      <dc:creator>Jelle Druyts</dc:creator>
      <wfw:comment>http://jelle.druyts.net/CommentView.aspx?guid=5244194c-e970-421c-8bd5-1447ca298a3c</wfw:comment>
      <wfw:commentRss>http://jelle.druyts.net/SyndicationService.asmx/GetEntryCommentsRss?guid=5244194c-e970-421c-8bd5-1447ca298a3c</wfw:commentRss>
      <slash:comments>3</slash:comments>
      <title>Interview on Guidance Automation Toolkit</title>
      <guid isPermaLink="false">http://jelle.druyts.net/PermaLink.aspx?guid=5244194c-e970-421c-8bd5-1447ca298a3c</guid>
      <link>http://jelle.druyts.net/2007/04/04/InterviewOnGuidanceAutomationToolkit.aspx</link>
      <pubDate>Wed, 04 Apr 2007 10:45:36 GMT</pubDate>
      <description>&lt;p&gt;
Right after my session at the Developer &amp;amp; IT Pro days last week, I was interviewed
by the &lt;a href="http://www.wygwam.com/"&gt;Wygwam team&lt;/a&gt; - which was pretty cool :-)&amp;nbsp;It
also serves as a pretty good 5-minute introduction to Guidance Automation Toolkit,
so check it out!
&lt;/p&gt;
&lt;p&gt;
&lt;embed src="http://images.soapbox.msn.com/flash/soapbox1_1.swf" quality="high" width="412" height="362" wmode="transparent" name="msn_soapbox" type="application/x-shockwave-flash" pluginspage="http://macromedia.com/go/getflashplayer" flashvars="c=v&amp;v=bb9ddebf-1426-485a-a713-42a734d3e3af"&gt;&lt;/embed&gt;
&lt;br&gt;
&lt;a href="http://soapbox.msn.com/video.aspx?vid=bb9ddebf-1426-485a-a713-42a734d3e3af" target="_new" title="DevITProDays - Jelle"&gt;Video:
DevITProDays - Jelle&lt;/a&gt;
&lt;/p&gt;
&lt;img width="0" height="0" src="http://jelle.druyts.net/aggbug.ashx?id=5244194c-e970-421c-8bd5-1447ca298a3c" /&gt;</description>
      <comments>http://jelle.druyts.net/CommentView.aspx?guid=5244194c-e970-421c-8bd5-1447ca298a3c</comments>
      <category>Blog</category>
      <category>Blog/General</category>
      <category>Blog/Programming</category>
      <category>Blog/Programming/.NET</category>
      <category>Blog/Programming/.NET/GuidanceAutomation</category>
    </item>
    <item>
      <trackback:ping>http://jelle.druyts.net/Trackback.aspx?guid=1af437fa-5a28-4151-9dfe-23241a459acd</trackback:ping>
      <pingback:server>http://jelle.druyts.net/pingback.aspx</pingback:server>
      <pingback:target>http://jelle.druyts.net/PermaLink.aspx?guid=1af437fa-5a28-4151-9dfe-23241a459acd</pingback:target>
      <dc:creator>Jelle Druyts</dc:creator>
      <wfw:comment>http://jelle.druyts.net/CommentView.aspx?guid=1af437fa-5a28-4151-9dfe-23241a459acd</wfw:comment>
      <wfw:commentRss>http://jelle.druyts.net/SyndicationService.asmx/GetEntryCommentsRss?guid=1af437fa-5a28-4151-9dfe-23241a459acd</wfw:commentRss>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
I had a great time at the <a href="http://www.dev-itprodays.be/">Belgian Developer
&amp; IT Pro Days</a> in Ghent last week, many thanks to the organizing team (great
job <a href="http://www.vsdotnet.be/blogs/tommer/">Tom</a>, <a href="http://blogs.msdn.com/davbosch/">David</a>, <a href="http://blogs.technet.com/aralves/">Arlindo</a>, <a href="http://blogs.technet.com/rho_weblog/">Ritchie</a>, <a href="http://www.wiver.com/blog/">Wim</a> and
everybody else)!
</p>
        <p>
If you came to my sessions last year, you might remember that I ended a little early
(25 minutes!) on my Framework Design talk and a little late (10 minutes!) on the Application
Development talk with <a href="http://steven.wilssens.net/">Steven Wilssens</a>, so
I was pretty happy that I finished right on the minute this year :-)
</p>
        <p>
You can download the slides here: <a href="http://jelle.druyts.net/content/binary/A%20Deep%20Dive%20Into%20The%20Guidance%20Automation%20Toolkit.ppt">A
Deep Dive Into The Guidance Automation Toolkit (3 MB)</a>. And don't forget about
the <a href="http://jelle.druyts.net/CategoryView.aspx?category=Blog%7CProgramming%7C.NET%7CGuidanceAutomation">Guidance
Automation Series</a> that goes into much more detail.
</p>
        <p>
Now for a quick shout out to my fellow community members: congratulations <a href="http://weblogs.asp.net/jan/">Jan</a> on
the birth of your first child <a href="http://neleenjan.net/baby">Fran</a>! From the <a href="http://www.flickr.com/photos/neleenjan/444798757/in/set-72157600030220327/">pictures</a>,
it looks like she's already well into the Microsoft spirit :-) And congratulations <a href="http://community.bartdesmet.net/blogs/bart/">Bart</a> on <a href="http://community.bartdesmet.net/blogs/bart/archive/2007/03/30/talking-about-dev-it-pro-days-2007-iis-7-0-and-moving-to-redmond.aspx">joining
Microsoft Corp on the WPF team</a>! I'm sure you'll fit right in with the rest of
the brainiacs on campus! Say hi to <a href="http://steven.wilssens.net/">Steven</a> from
me :-)
</p>
        <img width="0" height="0" src="http://jelle.druyts.net/aggbug.ashx?id=1af437fa-5a28-4151-9dfe-23241a459acd" />
      </body>
      <title>Developer &amp; IT Pro Days wrap-up</title>
      <guid isPermaLink="false">http://jelle.druyts.net/PermaLink.aspx?guid=1af437fa-5a28-4151-9dfe-23241a459acd</guid>
      <link>http://jelle.druyts.net/2007/04/04/DeveloperITProDaysWrapup.aspx</link>
      <pubDate>Wed, 04 Apr 2007 10:06:24 GMT</pubDate>
      <description>&lt;p&gt;
I had a great time at the &lt;a href="http://www.dev-itprodays.be/"&gt;Belgian Developer
&amp;amp; IT Pro Days&lt;/a&gt; in Ghent last week, many thanks to the organizing team (great
job &lt;a href="http://www.vsdotnet.be/blogs/tommer/"&gt;Tom&lt;/a&gt;, &lt;a href="http://blogs.msdn.com/davbosch/"&gt;David&lt;/a&gt;, &lt;a href="http://blogs.technet.com/aralves/"&gt;Arlindo&lt;/a&gt;, &lt;a href="http://blogs.technet.com/rho_weblog/"&gt;Ritchie&lt;/a&gt;, &lt;a href="http://www.wiver.com/blog/"&gt;Wim&lt;/a&gt;&amp;nbsp;and
everybody else)!
&lt;/p&gt;
&lt;p&gt;
If you came to my sessions last year, you might remember that I ended a little early
(25 minutes!) on my Framework Design talk and a little late (10 minutes!) on the Application
Development talk with &lt;a href="http://steven.wilssens.net/"&gt;Steven Wilssens&lt;/a&gt;, so
I was pretty happy that I finished right on the minute this year :-)
&lt;/p&gt;
&lt;p&gt;
You can download the slides here: &lt;a href="http://jelle.druyts.net/content/binary/A%20Deep%20Dive%20Into%20The%20Guidance%20Automation%20Toolkit.ppt"&gt;A
Deep Dive Into The Guidance Automation Toolkit (3 MB)&lt;/a&gt;. And don't forget about
the &lt;a href="http://jelle.druyts.net/CategoryView.aspx?category=Blog%7CProgramming%7C.NET%7CGuidanceAutomation"&gt;Guidance
Automation Series&lt;/a&gt; that goes into much more detail.
&lt;/p&gt;
&lt;p&gt;
Now for a quick shout out to my fellow community members: congratulations &lt;a href="http://weblogs.asp.net/jan/"&gt;Jan&lt;/a&gt; on
the birth of your first child &lt;a href="http://neleenjan.net/baby"&gt;Fran&lt;/a&gt;! From the &lt;a href="http://www.flickr.com/photos/neleenjan/444798757/in/set-72157600030220327/"&gt;pictures&lt;/a&gt;,
it looks like she's already well into the Microsoft spirit :-) And congratulations &lt;a href="http://community.bartdesmet.net/blogs/bart/"&gt;Bart&lt;/a&gt; on &lt;a href="http://community.bartdesmet.net/blogs/bart/archive/2007/03/30/talking-about-dev-it-pro-days-2007-iis-7-0-and-moving-to-redmond.aspx"&gt;joining
Microsoft Corp on the WPF team&lt;/a&gt;! I'm sure you'll fit right in with the rest of
the brainiacs on campus! Say hi to &lt;a href="http://steven.wilssens.net/"&gt;Steven&lt;/a&gt; from
me :-)
&lt;/p&gt;
&lt;img width="0" height="0" src="http://jelle.druyts.net/aggbug.ashx?id=1af437fa-5a28-4151-9dfe-23241a459acd" /&gt;</description>
      <comments>http://jelle.druyts.net/CommentView.aspx?guid=1af437fa-5a28-4151-9dfe-23241a459acd</comments>
      <category>Blog</category>
      <category>Blog/General</category>
      <category>Blog/Programming</category>
      <category>Blog/Programming/.NET</category>
      <category>Blog/Programming/.NET/GuidanceAutomation</category>
    </item>
    <item>
      <trackback:ping>http://jelle.druyts.net/Trackback.aspx?guid=f0748290-8f2e-4dae-b439-c95a3c9e730a</trackback:ping>
      <pingback:server>http://jelle.druyts.net/pingback.aspx</pingback:server>
      <pingback:target>http://jelle.druyts.net/PermaLink.aspx?guid=f0748290-8f2e-4dae-b439-c95a3c9e730a</pingback:target>
      <dc:creator>Jelle Druyts</dc:creator>
      <wfw:comment>http://jelle.druyts.net/CommentView.aspx?guid=f0748290-8f2e-4dae-b439-c95a3c9e730a</wfw:comment>
      <wfw:commentRss>http://jelle.druyts.net/SyndicationService.asmx/GetEntryCommentsRss?guid=f0748290-8f2e-4dae-b439-c95a3c9e730a</wfw:commentRss>
      <slash:comments>5</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
[This is episode 7 of the <a href="http://jelle.druyts.net/CategoryView.aspx?category=Blog%7CProgramming%7C.NET%7CGuidanceAutomation">Guidance
Automation Series</a>]
</p>
        <p>
It's been a while since the last episode, but I've finally found the time to finish
the (<a href="http://blogs.msdn.com/davbosch/archive/2006/08/10/694687.aspx">long
awaited</a>) grand finale of the Guidance Automation Series.
</p>
        <p>
Last time, we looked at class generation using the powerful T4 text templating engine.
Armed with all the knowledge up to here, it's already perfectly possible to build
an entire guidance package that allows the developer to create Enterprise Library
Application Blocks with a minimal amount of effort. As I said in the <a href="http://jelle.druyts.net/2006/06/26/GAS01IntroductionToTheGuidanceAutomationJune2006CTP.aspx">introduction
to the Guidance Automation Series</a>, I'm not going to build that guidance package
here. I'll just show you some more tricks you can teach your guidance package to perform...
This time, we'll be adding a strong name to our assemblies by signing it with a key
and see how we can rename the solution's file name.
</p>
        <p>
          <strong>Renaming The Solution</strong>
        </p>
        <p>
We'll start off with the relatively easy task of renaming the solution. Although the
developer has already specified a solution name, in a lot of organizations it's required
that the solution also adheres to a certain naming convention. And possibly, this
depends on some fields that the user can define in the wizard belonging to the Guidance
Package, such as a namespace. The only problem is, our Guidance Package doesn't kick
in until <i>after</i> the developer has specified a name for the solution file - and
now it's obviously too late to rename it. With a little trick, however, it's perfectly
possible to change the solution name, e.g. to <code>ApplicationBlockNamespace.ApplicationBlockName.sln</code> in
our Guidance Package for Enterprise Library application blocks. 
</p>
        <p>
To do this, we will need a custom action class that performs this magic. We can create
a custom action by deriving from the <code>Microsoft.Practices.RecipeFramework.Action</code> class.
An action can take inputs and create outputs by defining properties that are decorated
with the <code>Input</code> or <code>Output</code> attributes:
</p>
        <pre>public class RenameSolutionAction : Action
{
    /// &lt;summary&gt;
    /// The new name of the solution.
    /// &lt;/summary&gt;
    private string m_NewSolutionName;

    /// &lt;summary&gt;
    /// Gets or sets the new name of the solution.
    /// &lt;/summary&gt;
    [Input(Required = true)]
    public string NewSolutionName
    {
        get
        {
            return m_NewSolutionName;
        }
        set
        {
            m_NewSolutionName = value;
        }
    }

    /// &lt;summary&gt;
    /// Executes the action.
    /// &lt;/summary&gt;
    public override void Execute()
    {
    }

    /// &lt;summary&gt;
    /// Performs an undo of the action.
    /// &lt;/summary&gt;
    public override void Undo()
    {
    }
}</pre>
        <p>
At this point, our custom action doesn't really do anything but you can see that it
overrides the <code>Execute</code> and <code>Undo</code> methods that are called when
the action is run and when it needs to be undone. 
</p>
        <p>
The actual work of renaming the solution can now be performed in the <code>Execute</code> method
by using the EnvDTE objects of the Visual Studio SDK. In our case, we'll perform the
rename by simply doing a "Save As" of the solution using the new name, and then deleting
the original solution file and its associated suo file:
</p>
        <pre>public override void Execute()
{
    DTE vs = this.GetService&lt;DTE&gt;(true);

    string newFilename = this.NewSolutionName;
    string originalSolutionPath = (string)vs.Solution.Properties.Item("Path").Value;
    string originalSolutionDir = Path.GetDirectoryName(originalSolutionPath);

    // Check if it is an absolute or relative path.
    if (!Path.IsPathRooted(this.NewSolutionName))
    {
        // Relative path from the current solution root.
        newFilename = Path.Combine(originalSolutionDir, this.NewSolutionName);
    }

    // Make sure the destination directory exists.
    Directory.CreateDirectory(Path.GetDirectoryName(newFilename));

    // Save the current solution as the new file name.
    vs.Solution.SaveAs(newFilename);

    // Delete the old solution.
    File.Delete(originalSolutionPath);
    string suoFile = Path.GetFileNameWithoutExtension(originalSolutionPath) + ".suo";
    string suoFilePath = Path.Combine(originalSolutionDir, suoFile);
    File.Delete(suoFilePath);
}</pre>
        <p>
          <em>
            <strong>Guidance Automation Tip #3:</strong> The <code>vs.Solution.FileName</code> or <code>vs.Solution.FullName</code> properties
do not return a value while the solution is being created. You can use <code>"string
solutionPath = (string)vs.Solution.Properties.Item("Path").Value;"</code> to get the
full path of the solution.</em>
        </p>
        <p>
Now that our action is created, we can register it in the Guidance Package's main
XML configuration file in the <code>&lt;Actions&gt;</code> section: 
</p>
        <pre>&lt;Action Name="RenameSolution" Type="JelleDruyts.EnterpriseLibraryGuidance.Actions.RenameSolutionAction, JelleDruyts.EnterpriseLibraryGuidance"&gt;
  &lt;Input Name="NewSolutionName" RecipeArgument="FinalSolutionName" /&gt;
&lt;/Action&gt;
</pre>
        <p>
The action takes an input argument named <code>NewSolutionName</code> (as defined
in the <code>RenameSolutionAction</code> class), which is taken from a Recipe Argument
named <code>FinalSolutionName</code>. This argument is again a "derived" argument,
which is composed of the application block namespace and name through an evaluator: 
</p>
        <pre>&lt;Argument Name="FinalSolutionName"&gt;
  &lt;ValueProvider Type="Evaluator" Expression="$(ApplicationBlockNamespace).$(ApplicationBlockName).sln"&gt;
    &lt;MonitorArgument Name="ApplicationBlockNamespace" /&gt;
    &lt;MonitorArgument Name="ApplicationBlockName" /&gt;
  &lt;/ValueProvider&gt;
&lt;/Argument&gt;
</pre>
        <p>
          <strong>Signing The Projects</strong>
        </p>
        <p>
As a last step, we want to enable the developer to sign the generated projects with
a strong-name key. This means we will need to <i>generate</i> a key-file dynamically
(by using the <code>sn.exe</code> tool that ships with the .NET SDK), and that we
need to configure the generated projects to <i>use</i> the key to sign the assemblies.
</p>
        <p>
In our specific case, signing the Enterprise Library Application Block assemblies
has an extra risk: this will only work if Enterprise Library itself is signed because
a signed assembly cannot reference an unsigned assembly. Because Enterprise Library
is not signed by default, we will need to make this step optional so the developer
can choose whether or not to sign the projects. This means we will need an extra argument
(that defaults to false) and a field in the wizard:
</p>
        <pre>&lt;Argument Name="EnableStrongNaming" Required="true" Type="System.Boolean"&gt;
  &lt;ValueProvider Type="Evaluator" Expression="false" /&gt;
&lt;/Argument&gt;
...
&lt;Field ValueName="EnableStrongNaming" Label="Enable strong-naming"&gt;
  &lt;Tooltip&gt;Only enable strong naming if you have a build of Enterprise Library that is itself strong-named.&lt;/Tooltip&gt;
&lt;/Field&gt;</pre>
        <p>
          <img src="http://jelle.druyts.net/content/binary/GAS%20-%20NewProjectWithStrongNaming.png" />
        </p>
        <p>
Now the trick to signing the projects when they are generated is actually quite simple:
there are two settings in the C# project file that are required: <code>SignAssembly</code> must
be set to <code>true</code>, and <code>AssemblyOriginatorKeyFile</code> must be set
to the path to the key file. We can do this by using recipe arguments in the .csproj
template files as such:
</p>
        <pre>&lt;SignAssembly&gt;$EnableStrongNaming$&lt;/SignAssembly&gt;
&lt;AssemblyOriginatorKeyFile&gt;$AssemblyOriginatorKeyFile$&lt;/AssemblyOriginatorKeyFile&gt;
</pre>
        <p>
The developer has already specified the <code>EnableStrongNaming</code> argument through
a field in the wizard. Now the <code>AssemblyOriginatorKeyFile</code> needs to point
to a valid path to a key file in case strong naming is enabled, or it should be an
empty string if strong naming is not enabled. This can be performed by using a custom
value provider that checks the <code>EnableStrongNaming</code> argument and determines
which string to return: either a string containing the path to the key-file, or an
empty string. I won't go into the details here, since Value Providers were already
covered in a previous article in this series, but I just want to stress one important
point when developing them:
</p>
        <p>
          <em>
            <strong>Guidance Automation Tip #4:</strong> When writing a Value Provider, returning <code>null</code> means
"don't use this value".</em>
        </p>
        <p>
This means that if you would return <code>null</code> in the Value Provider that provides
a value for the <code>AssemblyOriginatorKeyFile</code> argument and the developer
chose not to strong-name the assemblies, the <code>$AssemblyOriginatorKeyFile$</code> would
not get replaced at all in any template! If you want the argument to be replaced with
an empty string, be sure to return an empty string from the Value Provider as well:
</p>
        <pre>if (!enabled)
{
    newValue = string.Empty; // Not null!
}
else
{
    newValue = string.Format(@"..\{0}", keyFileName);
}</pre>
        <p>
Now that we have a Value Provider for the path to the key-file that also works when
the developer chose not to use strong-naming, we can register the following recipe
arguments to define the <code>KeyFileName</code> (again based on the Application Block
namespace and name) and the <code>AssemblyOriginatorKeyFile</code> arguments:
</p>
        <pre>&lt;Argument Name="KeyFileName"&gt;
  &lt;ValueProvider Type="Evaluator" Expression="$(ApplicationBlockNamespace).$(ApplicationBlockName).snk"&gt;
    &lt;MonitorArgument Name="ApplicationBlockNamespace" /&gt;
    &lt;MonitorArgument Name="ApplicationBlockName" /&gt;
  &lt;/ValueProvider&gt;
&lt;/Argument&gt;
&lt;Argument Name="AssemblyOriginatorKeyFile"&gt;
  &lt;ValueProvider Type="JelleDruyts.EnterpriseLibraryGuidance.ValueProviders.KeyFilePathValueProvider, JelleDruyts.EnterpriseLibraryGuidance"
                 KeyFileNameArgument="KeyFileName" EnableStrongNamingArgument="EnableStrongNaming"&gt;
    &lt;MonitorArgument Name="KeyFileName" /&gt;
    &lt;MonitorArgument Name="EnableStrongNaming" /&gt;
  &lt;/ValueProvider&gt;
&lt;/Argument&gt;</pre>
        <p>
The only remaining work is to actually generate the key file itself and add it to
the Visual Studio solution. This can be implemented as a custom action again, in which
we will perform the following work:
</p>
        <pre>public override void Execute()
{
    if (this.EnableStrongNaming)
    {
        EnvDTE.DTE vs = this.GetService&lt;EnvDTE.DTE&gt;(true);
        string solutionPath = (string)vs.Solution.Properties.Item("Path").Value;
        string solutionDir = Path.GetDirectoryName(solutionPath);
        string keyFilePath = Path.Combine(solutionDir, this.KeyFileName);

        // Find the .NET SDK directory to execute the sn.exe command.
        string visualStudioDir = Path.GetDirectoryName(vs.Application.FileName);
        string sdkDir = Path.Combine(visualStudioDir, @"..\..\SDK\v2.0\Bin");
        string snPath = Path.Combine(sdkDir, "sn.exe");
        if (!File.Exists(snPath))
        {
            throw new InvalidOperationException("Could not find the Visual Studio SDK directory to execute the sn.exe command.");
        }

        // Make sure the directory exists.
        Directory.CreateDirectory(Path.GetDirectoryName(keyFilePath));

        // Launch the process and wait until it's done (with a 10 second timeout).
        ProcessStartInfo startInfo = new ProcessStartInfo(snPath, string.Format("-k \"{0}\"", keyFilePath));
        startInfo.CreateNoWindow = true;
        startInfo.UseShellExecute = false;
        Process snProcess = Process.Start(startInfo);
        snProcess.WaitForExit(10000);

        // Add the key file to the Solution Items.
        DteHelper.SelectSolution(vs);
        vs.ItemOperations.AddExistingItem(keyFilePath);

        // The AddExistingItem operation also shows the item in a new window, close that.
        vs.ActiveWindow.Close(EnvDTE.vsSaveChanges.vsSaveChangesNo);
    }
}</pre>
        <p>
This custom action uses an input parameter named <code>KeyFileName</code> which is
the recipe argument we defined before. We have also passed in the <code>EnableStrongNaming</code> argument
to know if we actually <i>need</i> to generate the key file. We then find the path
to the .NET SDK by first looking at the application directory of Visual Studio (<code>vs.Application.FileName</code>)
and then using a relative path to the SDK. We check for the presence of the <code>sn.exe</code> application,
and start a process (without a window to avoid the DOS box from popping up) to generate
the key file. When it's done, we add the key file to the solution. Note the special
way of adding a file to the "Solution Items" special folder: we're first selecting
the solution in Solution Explorer (through the <code>DteHelper</code> class), and
then calling the <code>AddExistingItem</code> method with the path to the item. This
seems to be the easiest and most robust way of doing this, since the "Solution Items"
folder is handled quite differently and it wouldn't be enough to just create a folder
with that name. In the end, we also close the active window, because by default the
added item is shown in a new window - which doesn't make a lot of sense for a binary
key file. 
</p>
        <p>
Now that we have all this infrastructure in place, running the Guidance Package with
strong naming enabled results in the following solution, properly renamed, containing
the strong name key-file and with both projects enabled for signing:
</p>
        <p>
          <img src="http://jelle.druyts.net/content/binary/GAS%20-%20ApplicationBlockWithStrongNaming.png" />
        </p>
        <p>
          <strong>Showing Documentation</strong>
        </p>
        <p>
One of the nice added features in the latest version of the Guidance Automation Toolkit
is the Guidance Navigator, which provides an overview of the currently loaded Guidance
Packages and the recipes they provide. It can also show an HTML page containing more
information and links to online resources, to provide continuous guidance to developers
using the package.
</p>
        <p>
Showing an HTML page in this Guidance Navigator window is as simple as adding the
following XML section to the top of the Guidance Package's main XML file, as the first
node inside the main <code>&lt;GuidancePackage&gt;</code> tag: 
</p>
        <pre>&lt;GuidancePackage xmlns="http://schemas.microsoft.com/pag/gax-core" ...&gt;
  &lt;Overview Url="Templates\Doc\Overview.html" /&gt;</pre>
        <p>
The Url points to the HTML page to be shown, of course, and I've chosen to store this
in the Templates\Doc directory. When the solution is created or opened, the Guidance
Navigator window will show the page as such:
</p>
        <p>
          <img src="http://jelle.druyts.net/content/binary/GAS%20-%20GuidanceNavigator.png" />
        </p>
        <p>
          <strong>Where Are We</strong>
        </p>
        <p>
At this point, we've added some custom actions to rename the solution and we've also
given the developer the option to sign the assemblies with a strong name.
</p>
        <p>
I think I've covered quite a lot of scenarios already to end the <a href="http://jelle.druyts.net/CategoryView.aspx?category=Blog%7CProgramming%7C.NET%7CGuidanceAutomation">Guidance
Automation Series</a> right here. Any additional tips and tricks I discover will of
course be posted here, so keep an eye on my blog if you're interested in the Guidance
Automation Toolkit!
</p>
        <p>
          <a href="http://jelle.druyts.net/content/binary/GAS%20-%2006%20-%20Custom%20Actions%20and%20Documentation.zip">Download
the source code for the current state of the Guidance Package.</a>
        </p>
        <img width="0" height="0" src="http://jelle.druyts.net/aggbug.ashx?id=f0748290-8f2e-4dae-b439-c95a3c9e730a" />
      </body>
      <title>GAS07: Renaming the solution, signing projects and showing documentation</title>
      <guid isPermaLink="false">http://jelle.druyts.net/PermaLink.aspx?guid=f0748290-8f2e-4dae-b439-c95a3c9e730a</guid>
      <link>http://jelle.druyts.net/2006/09/18/GAS07RenamingTheSolutionSigningProjectsAndShowingDocumentation.aspx</link>
      <pubDate>Mon, 18 Sep 2006 18:00:28 GMT</pubDate>
      <description>&lt;p&gt;
[This is episode 7 of the &lt;a href="http://jelle.druyts.net/CategoryView.aspx?category=Blog%7CProgramming%7C.NET%7CGuidanceAutomation"&gt;Guidance
Automation Series&lt;/a&gt;]
&lt;/p&gt;
&lt;p&gt;
It's been a while since the last episode, but I've finally found the time to finish
the (&lt;a href="http://blogs.msdn.com/davbosch/archive/2006/08/10/694687.aspx"&gt;long
awaited&lt;/a&gt;) grand finale of the Guidance Automation Series.
&lt;/p&gt;
&lt;p&gt;
Last time, we looked at class generation using the powerful T4 text templating engine.
Armed with all the knowledge up to here, it's already perfectly possible to build
an entire guidance package that allows the developer to create Enterprise Library
Application Blocks with a minimal amount of effort. As I said in the &lt;a href="http://jelle.druyts.net/2006/06/26/GAS01IntroductionToTheGuidanceAutomationJune2006CTP.aspx"&gt;introduction
to the Guidance Automation Series&lt;/a&gt;, I'm not going to build that guidance package
here. I'll just show you some more tricks you can teach your guidance package to perform...
This time, we'll be adding a strong name to our assemblies by signing it with a key
and see how we can rename the solution's file name.
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;Renaming The Solution&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;
We'll start off with the relatively easy task of renaming the solution. Although the
developer has already specified a solution name, in a lot of organizations it's required
that the solution also adheres to a certain naming convention. And possibly, this
depends on some fields that the user can define in the wizard belonging to the Guidance
Package, such as a namespace. The only problem is, our Guidance Package doesn't kick
in until &lt;i&gt;after&lt;/i&gt; the developer has specified a name for the solution file - and
now it's obviously too late to rename it. With a little trick, however, it's perfectly
possible to change the solution name, e.g. to &lt;code&gt;ApplicationBlockNamespace.ApplicationBlockName.sln&lt;/code&gt; in
our Guidance Package for Enterprise Library application blocks. 
&lt;/p&gt;
&lt;p&gt;
To do this, we will need a custom action class that performs this magic. We can create
a custom action by deriving from the &lt;code&gt;Microsoft.Practices.RecipeFramework.Action&lt;/code&gt; class.
An action can take inputs and create outputs by defining properties that are decorated
with the &lt;code&gt;Input&lt;/code&gt; or &lt;code&gt;Output&lt;/code&gt; attributes:
&lt;/p&gt;
&lt;pre&gt;public class RenameSolutionAction : Action
{
    /// &amp;lt;summary&amp;gt;
    /// The new name of the solution.
    /// &amp;lt;/summary&amp;gt;
    private string m_NewSolutionName;

    /// &amp;lt;summary&amp;gt;
    /// Gets or sets the new name of the solution.
    /// &amp;lt;/summary&amp;gt;
    [Input(Required = true)]
    public string NewSolutionName
    {
        get
        {
            return m_NewSolutionName;
        }
        set
        {
            m_NewSolutionName = value;
        }
    }

    /// &amp;lt;summary&amp;gt;
    /// Executes the action.
    /// &amp;lt;/summary&amp;gt;
    public override void Execute()
    {
    }

    /// &amp;lt;summary&amp;gt;
    /// Performs an undo of the action.
    /// &amp;lt;/summary&amp;gt;
    public override void Undo()
    {
    }
}&lt;/pre&gt;
&lt;p&gt;
At this point, our custom action doesn't really do anything but you can see that it
overrides the &lt;code&gt;Execute&lt;/code&gt; and &lt;code&gt;Undo&lt;/code&gt; methods that are called when
the action is run and when it needs to be undone. 
&lt;/p&gt;
&lt;p&gt;
The actual work of renaming the solution can now be performed in the &lt;code&gt;Execute&lt;/code&gt; method
by using the EnvDTE objects of the Visual Studio SDK. In our case, we'll perform the
rename by simply doing a "Save As" of the solution using the new name, and then deleting
the original solution file and its associated suo file:
&lt;/p&gt;
&lt;pre&gt;public override void Execute()
{
    DTE vs = this.GetService&amp;lt;DTE&amp;gt;(true);

    string newFilename = this.NewSolutionName;
    string originalSolutionPath = (string)vs.Solution.Properties.Item("Path").Value;
    string originalSolutionDir = Path.GetDirectoryName(originalSolutionPath);

    // Check if it is an absolute or relative path.
    if (!Path.IsPathRooted(this.NewSolutionName))
    {
        // Relative path from the current solution root.
        newFilename = Path.Combine(originalSolutionDir, this.NewSolutionName);
    }

    // Make sure the destination directory exists.
    Directory.CreateDirectory(Path.GetDirectoryName(newFilename));

    // Save the current solution as the new file name.
    vs.Solution.SaveAs(newFilename);

    // Delete the old solution.
    File.Delete(originalSolutionPath);
    string suoFile = Path.GetFileNameWithoutExtension(originalSolutionPath) + ".suo";
    string suoFilePath = Path.Combine(originalSolutionDir, suoFile);
    File.Delete(suoFilePath);
}&lt;/pre&gt;
&lt;p&gt;
&lt;em&gt;&lt;strong&gt;Guidance Automation Tip #3:&lt;/strong&gt; The &lt;code&gt;vs.Solution.FileName&lt;/code&gt; or &lt;code&gt;vs.Solution.FullName&lt;/code&gt; properties
do not return a value while the solution is being created. You can use &lt;code&gt;"string
solutionPath = (string)vs.Solution.Properties.Item("Path").Value;"&lt;/code&gt; to get the
full path of the solution.&lt;/em&gt;
&lt;/p&gt;
&lt;p&gt;
Now that our action is created, we can register it in the Guidance Package's main
XML configuration file in the &lt;code&gt;&amp;lt;Actions&amp;gt;&lt;/code&gt; section: 
&lt;/p&gt;
&lt;pre&gt;&amp;lt;Action Name="RenameSolution" Type="JelleDruyts.EnterpriseLibraryGuidance.Actions.RenameSolutionAction, JelleDruyts.EnterpriseLibraryGuidance"&amp;gt;
  &amp;lt;Input Name="NewSolutionName" RecipeArgument="FinalSolutionName" /&amp;gt;
&amp;lt;/Action&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
The action takes an input argument named &lt;code&gt;NewSolutionName&lt;/code&gt; (as defined
in the &lt;code&gt;RenameSolutionAction&lt;/code&gt; class), which is taken from a Recipe Argument
named &lt;code&gt;FinalSolutionName&lt;/code&gt;. This argument is again a "derived" argument,
which is composed of the application block namespace and name through an evaluator: 
&lt;/p&gt;
&lt;pre&gt;&amp;lt;Argument Name="FinalSolutionName"&amp;gt;
  &amp;lt;ValueProvider Type="Evaluator" Expression="$(ApplicationBlockNamespace).$(ApplicationBlockName).sln"&amp;gt;
    &amp;lt;MonitorArgument Name="ApplicationBlockNamespace" /&amp;gt;
    &amp;lt;MonitorArgument Name="ApplicationBlockName" /&amp;gt;
  &amp;lt;/ValueProvider&amp;gt;
&amp;lt;/Argument&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
&lt;strong&gt;Signing The Projects&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;
As a last step, we want to enable the developer to sign the generated projects with
a strong-name key. This means we will need to &lt;i&gt;generate&lt;/i&gt; a key-file dynamically
(by using the &lt;code&gt;sn.exe&lt;/code&gt; tool that ships with the .NET SDK), and that we
need to configure the generated projects to &lt;i&gt;use&lt;/i&gt; the key to sign the assemblies.
&lt;/p&gt;
&lt;p&gt;
In our specific case, signing the Enterprise Library Application Block assemblies
has an extra risk: this will only work if Enterprise Library itself is signed because
a signed assembly cannot reference an unsigned assembly. Because Enterprise Library
is not signed by default, we will need to make this step optional so the developer
can choose whether or not to sign the projects. This means we will need an extra argument
(that defaults to false) and a field in the wizard:
&lt;/p&gt;
&lt;pre&gt;&amp;lt;Argument Name="EnableStrongNaming" Required="true" Type="System.Boolean"&amp;gt;
  &amp;lt;ValueProvider Type="Evaluator" Expression="false" /&amp;gt;
&amp;lt;/Argument&amp;gt;
...
&amp;lt;Field ValueName="EnableStrongNaming" Label="Enable strong-naming"&amp;gt;
  &amp;lt;Tooltip&amp;gt;Only enable strong naming if you have a build of Enterprise Library that is itself strong-named.&amp;lt;/Tooltip&amp;gt;
&amp;lt;/Field&amp;gt;&lt;/pre&gt;
&lt;p&gt;
&lt;img src="http://jelle.druyts.net/content/binary/GAS%20-%20NewProjectWithStrongNaming.png"&gt;
&lt;/p&gt;
&lt;p&gt;
Now the trick to signing the projects when they are generated is actually quite simple:
there are two settings in the C# project file that are required: &lt;code&gt;SignAssembly&lt;/code&gt; must
be set to &lt;code&gt;true&lt;/code&gt;, and &lt;code&gt;AssemblyOriginatorKeyFile&lt;/code&gt; must be set
to the path to the key file. We can do this by using recipe arguments in the .csproj
template files as such:
&lt;/p&gt;
&lt;pre&gt;&amp;lt;SignAssembly&amp;gt;$EnableStrongNaming$&amp;lt;/SignAssembly&amp;gt;
&amp;lt;AssemblyOriginatorKeyFile&amp;gt;$AssemblyOriginatorKeyFile$&amp;lt;/AssemblyOriginatorKeyFile&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
The developer has already specified the &lt;code&gt;EnableStrongNaming&lt;/code&gt; argument through
a field in the wizard. Now the &lt;code&gt;AssemblyOriginatorKeyFile&lt;/code&gt; needs to point
to a valid path to a key file in case strong naming is enabled, or it should be an
empty string if strong naming is not enabled. This can be performed by using a custom
value provider that checks the &lt;code&gt;EnableStrongNaming&lt;/code&gt; argument and determines
which string to return: either a string containing the path to the key-file, or an
empty string. I won't go into the details here, since Value Providers were already
covered in a previous article in this series, but I just want to stress one important
point when developing them:
&lt;/p&gt;
&lt;p&gt;
&lt;em&gt;&lt;strong&gt;Guidance Automation Tip #4:&lt;/strong&gt; When writing a Value Provider, returning &lt;code&gt;null&lt;/code&gt; means
"don't use this value".&lt;/em&gt;
&lt;/p&gt;
&lt;p&gt;
This means that if you would return &lt;code&gt;null&lt;/code&gt; in the Value Provider that provides
a value for the &lt;code&gt;AssemblyOriginatorKeyFile&lt;/code&gt; argument and the developer
chose not to strong-name the assemblies, the &lt;code&gt;$AssemblyOriginatorKeyFile$&lt;/code&gt; would
not get replaced at all in any template! If you want the argument to be replaced with
an empty string, be sure to return an empty string from the Value Provider as well:
&lt;/p&gt;
&lt;pre&gt;if (!enabled)
{
    newValue = string.Empty; // Not null!
}
else
{
    newValue = string.Format(@"..\{0}", keyFileName);
}&lt;/pre&gt;
&lt;p&gt;
Now that we have a Value Provider for the path to the key-file that also works when
the developer chose not to use strong-naming, we can register the following recipe
arguments to define the &lt;code&gt;KeyFileName&lt;/code&gt; (again based on the Application Block
namespace and name) and the &lt;code&gt;AssemblyOriginatorKeyFile&lt;/code&gt; arguments:
&lt;/p&gt;
&lt;pre&gt;&amp;lt;Argument Name="KeyFileName"&amp;gt;
  &amp;lt;ValueProvider Type="Evaluator" Expression="$(ApplicationBlockNamespace).$(ApplicationBlockName).snk"&amp;gt;
    &amp;lt;MonitorArgument Name="ApplicationBlockNamespace" /&amp;gt;
    &amp;lt;MonitorArgument Name="ApplicationBlockName" /&amp;gt;
  &amp;lt;/ValueProvider&amp;gt;
&amp;lt;/Argument&amp;gt;
&amp;lt;Argument Name="AssemblyOriginatorKeyFile"&amp;gt;
  &amp;lt;ValueProvider Type="JelleDruyts.EnterpriseLibraryGuidance.ValueProviders.KeyFilePathValueProvider, JelleDruyts.EnterpriseLibraryGuidance"
                 KeyFileNameArgument="KeyFileName" EnableStrongNamingArgument="EnableStrongNaming"&amp;gt;
    &amp;lt;MonitorArgument Name="KeyFileName" /&amp;gt;
    &amp;lt;MonitorArgument Name="EnableStrongNaming" /&amp;gt;
  &amp;lt;/ValueProvider&amp;gt;
&amp;lt;/Argument&amp;gt;&lt;/pre&gt;
&lt;p&gt;
The only remaining work is to actually generate the key file itself and add it to
the Visual Studio solution. This can be implemented as a custom action again, in which
we will perform the following work:
&lt;/p&gt;
&lt;pre&gt;public override void Execute()
{
    if (this.EnableStrongNaming)
    {
        EnvDTE.DTE vs = this.GetService&amp;lt;EnvDTE.DTE&amp;gt;(true);
        string solutionPath = (string)vs.Solution.Properties.Item("Path").Value;
        string solutionDir = Path.GetDirectoryName(solutionPath);
        string keyFilePath = Path.Combine(solutionDir, this.KeyFileName);

        // Find the .NET SDK directory to execute the sn.exe command.
        string visualStudioDir = Path.GetDirectoryName(vs.Application.FileName);
        string sdkDir = Path.Combine(visualStudioDir, @"..\..\SDK\v2.0\Bin");
        string snPath = Path.Combine(sdkDir, "sn.exe");
        if (!File.Exists(snPath))
        {
            throw new InvalidOperationException("Could not find the Visual Studio SDK directory to execute the sn.exe command.");
        }

        // Make sure the directory exists.
        Directory.CreateDirectory(Path.GetDirectoryName(keyFilePath));

        // Launch the process and wait until it's done (with a 10 second timeout).
        ProcessStartInfo startInfo = new ProcessStartInfo(snPath, string.Format("-k \"{0}\"", keyFilePath));
        startInfo.CreateNoWindow = true;
        startInfo.UseShellExecute = false;
        Process snProcess = Process.Start(startInfo);
        snProcess.WaitForExit(10000);

        // Add the key file to the Solution Items.
        DteHelper.SelectSolution(vs);
        vs.ItemOperations.AddExistingItem(keyFilePath);

        // The AddExistingItem operation also shows the item in a new window, close that.
        vs.ActiveWindow.Close(EnvDTE.vsSaveChanges.vsSaveChangesNo);
    }
}&lt;/pre&gt;
&lt;p&gt;
This custom action uses an input parameter named &lt;code&gt;KeyFileName&lt;/code&gt; which is
the recipe argument we defined before. We have also passed in the &lt;code&gt;EnableStrongNaming&lt;/code&gt; argument
to know if we actually &lt;i&gt;need&lt;/i&gt; to generate the key file. We then find the path
to the .NET SDK by first looking at the application directory of Visual Studio (&lt;code&gt;vs.Application.FileName&lt;/code&gt;)
and then using a relative path to the SDK. We check for the presence of the &lt;code&gt;sn.exe&lt;/code&gt; application,
and start a process (without a window to avoid the DOS box from popping up) to generate
the key file. When it's done, we add the key file to the solution. Note the special
way of adding a file to the "Solution Items" special folder: we're first selecting
the solution in Solution Explorer (through the &lt;code&gt;DteHelper&lt;/code&gt; class), and
then calling the &lt;code&gt;AddExistingItem&lt;/code&gt; method with the path to the item. This
seems to be the easiest and most robust way of doing this, since the "Solution Items"
folder is handled quite differently and it wouldn't be enough to just create a folder
with that name. In the end, we also close the active window, because by default the
added item is shown in a new window - which doesn't make a lot of sense for a binary
key file. 
&lt;/p&gt;
&lt;p&gt;
Now that we have all this infrastructure in place, running the Guidance Package with
strong naming enabled results in the following solution, properly renamed, containing
the strong name key-file and with both projects enabled for signing:
&lt;/p&gt;
&lt;p&gt;
&lt;img src="http://jelle.druyts.net/content/binary/GAS%20-%20ApplicationBlockWithStrongNaming.png"&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;Showing Documentation&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;
One of the nice added features in the latest version of the Guidance Automation Toolkit
is the Guidance Navigator, which provides an overview of the currently loaded Guidance
Packages and the recipes they provide. It can also show an HTML page containing more
information and links to online resources, to provide continuous guidance to developers
using the package.
&lt;/p&gt;
&lt;p&gt;
Showing an HTML page in this Guidance Navigator window is as simple as adding the
following XML section to the top of the Guidance Package's main XML file, as the first
node inside the main &lt;code&gt;&amp;lt;GuidancePackage&amp;gt;&lt;/code&gt; tag: 
&lt;/p&gt;
&lt;pre&gt;&amp;lt;GuidancePackage xmlns="http://schemas.microsoft.com/pag/gax-core" ...&amp;gt;
  &amp;lt;Overview Url="Templates\Doc\Overview.html" /&amp;gt;&lt;/pre&gt;
&lt;p&gt;
The Url points to the HTML page to be shown, of course, and I've chosen to store this
in the Templates\Doc directory. When the solution is created or opened, the Guidance
Navigator window will show the page as such:
&lt;/p&gt;
&lt;p&gt;
&lt;img src="http://jelle.druyts.net/content/binary/GAS%20-%20GuidanceNavigator.png"&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;Where Are We&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;
At this point, we've added some custom actions to rename the solution and we've also
given the developer the option to sign the assemblies with a strong name.
&lt;/p&gt;
&lt;p&gt;
I think I've covered quite a lot of scenarios already to end the &lt;a href="http://jelle.druyts.net/CategoryView.aspx?category=Blog%7CProgramming%7C.NET%7CGuidanceAutomation"&gt;Guidance
Automation Series&lt;/a&gt; right here. Any additional tips and tricks I discover will of
course be posted here, so keep an eye on my blog if you're interested in the Guidance
Automation Toolkit!
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://jelle.druyts.net/content/binary/GAS%20-%2006%20-%20Custom%20Actions%20and%20Documentation.zip"&gt;Download
the source code for the current state of the Guidance Package.&lt;/a&gt;
&lt;/p&gt;
&lt;img width="0" height="0" src="http://jelle.druyts.net/aggbug.ashx?id=f0748290-8f2e-4dae-b439-c95a3c9e730a" /&gt;</description>
      <comments>http://jelle.druyts.net/CommentView.aspx?guid=f0748290-8f2e-4dae-b439-c95a3c9e730a</comments>
      <category>Blog</category>
      <category>Blog/Programming</category>
      <category>Blog/Programming/.NET</category>
      <category>Blog/Programming/.NET/GuidanceAutomation</category>
      <category>Blog/Programming/.NET/VS.NET</category>
    </item>
    <item>
      <trackback:ping>http://jelle.druyts.net/Trackback.aspx?guid=3c816861-aa8c-4f67-8243-736a5c2b2cfe</trackback:ping>
      <pingback:server>http://jelle.druyts.net/pingback.aspx</pingback:server>
      <pingback:target>http://jelle.druyts.net/PermaLink.aspx?guid=3c816861-aa8c-4f67-8243-736a5c2b2cfe</pingback:target>
      <dc:creator>Jelle Druyts</dc:creator>
      <wfw:comment>http://jelle.druyts.net/CommentView.aspx?guid=3c816861-aa8c-4f67-8243-736a5c2b2cfe</wfw:comment>
      <wfw:commentRss>http://jelle.druyts.net/SyndicationService.asmx/GetEntryCommentsRss?guid=3c816861-aa8c-4f67-8243-736a5c2b2cfe</wfw:commentRss>
      <slash:comments>1</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
Thanks to everyone that showed up yesterday at the <a href="http://www.visug.be/">Visug</a> session,
where I tried to <a href="http://jelle.druyts.net/2006/07/25/VisugSessionGuidanceAutomationToolkit.aspx">show
you the power of the Guidance Automation Toolkit</a>. It was great to see such an
interested crowd, and to get some excellent questions. The meat of the presentation
was in the demos, but if you want to take a look at the slides, here you go:
</p>
        <p>
          <a href="http://jelle.druyts.net/content/binary/Guidance%20Automation%20Toolkit.ppt">Guidance
Automation Toolkit.ppt (1,25 MB)</a>
        </p>
        <img width="0" height="0" src="http://jelle.druyts.net/aggbug.ashx?id=3c816861-aa8c-4f67-8243-736a5c2b2cfe" />
      </body>
      <title>Slides for my Visug session on Guidance Automation Toolkit</title>
      <guid isPermaLink="false">http://jelle.druyts.net/PermaLink.aspx?guid=3c816861-aa8c-4f67-8243-736a5c2b2cfe</guid>
      <link>http://jelle.druyts.net/2006/08/10/SlidesForMyVisugSessionOnGuidanceAutomationToolkit.aspx</link>
      <pubDate>Thu, 10 Aug 2006 07:47:36 GMT</pubDate>
      <description>&lt;p&gt;
Thanks to everyone that showed up yesterday at the &lt;a href="http://www.visug.be/"&gt;Visug&lt;/a&gt; session,
where I tried to &lt;a href="http://jelle.druyts.net/2006/07/25/VisugSessionGuidanceAutomationToolkit.aspx"&gt;show
you the power of the Guidance Automation Toolkit&lt;/a&gt;. It was great to see such an
interested crowd, and to get some excellent questions. The meat of the presentation
was in the demos, but if you want to take a look at the slides, here you go:
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://jelle.druyts.net/content/binary/Guidance%20Automation%20Toolkit.ppt"&gt;Guidance
Automation Toolkit.ppt (1,25 MB)&lt;/a&gt;
&lt;/p&gt;
&lt;img width="0" height="0" src="http://jelle.druyts.net/aggbug.ashx?id=3c816861-aa8c-4f67-8243-736a5c2b2cfe" /&gt;</description>
      <comments>http://jelle.druyts.net/CommentView.aspx?guid=3c816861-aa8c-4f67-8243-736a5c2b2cfe</comments>
      <category>Blog</category>
      <category>Blog/Programming</category>
      <category>Blog/Programming/.NET</category>
      <category>Blog/Programming/.NET/GuidanceAutomation</category>
    </item>
    <item>
      <trackback:ping>http://jelle.druyts.net/Trackback.aspx?guid=a549ff37-536c-40b6-87d3-9cecff09f4ca</trackback:ping>
      <pingback:server>http://jelle.druyts.net/pingback.aspx</pingback:server>
      <pingback:target>http://jelle.druyts.net/PermaLink.aspx?guid=a549ff37-536c-40b6-87d3-9cecff09f4ca</pingback:target>
      <dc:creator>Jelle Druyts</dc:creator>
      <wfw:comment>http://jelle.druyts.net/CommentView.aspx?guid=a549ff37-536c-40b6-87d3-9cecff09f4ca</wfw:comment>
      <wfw:commentRss>http://jelle.druyts.net/SyndicationService.asmx/GetEntryCommentsRss?guid=a549ff37-536c-40b6-87d3-9cecff09f4ca</wfw:commentRss>
      <slash:comments>4</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
[This is episode 6 of the <a href="http://jelle.druyts.net/CategoryView.aspx?category=Blog%7CProgramming%7C.NET%7CGuidanceAutomation">Guidance
Automation Series</a>]
</p>
        <p>
Last time, we added references to Enterprise Library and made sure the build output
was copied to the configuration tool directory. Now to make the application block
show up in this configuration tool, it needs to register some classes and commands
so that the tool knows how to display it. Without implementing the full works here,
let's see how we can already generate some classes that will serve as an excellent
starting point to implement the rest of the application block.
</p>
        <p>
          <strong>Adding Default Classes</strong>
        </p>
        <p>
Actually, we've already seen how to add classes in the first episode of this series:
the AssemblyInfo.cs file was added to the project by including it in a <code>ProjectItem</code> inside
the project's vstemplate file <em>and</em> by including it in the C# project file
(so that MSBuild will build it). The only extra feature we'll be using here is to
change the target file name of each file to include the name of the application block.
</p>
        <p>
To make it possible to add the application block in the configuration tool, we have
to define a <code>ConfigurationDesignManager</code> class and register it in the AssemblyInfo.cs
file. So we need to update that file and add a bunch more, including a <code>CommandRegistrar</code>,
a <code>NodeMapRegistrar</code> and a root settings node. For the purpose of all these
classes and how they should be implemented, I'll gladly refer to <a href="http://blogs.msdn.com/ploeh/">Mark
Seemann</a>'s excellent article on "<a href="http://msdn.microsoft.com/msdnmag/issues/06/07/PatternsAndPractices/default.aspx">Speed
Development With Custom Application Blocks For Enterprise Library</a>" again. All
we need to do here is provide the minimum amount of information to get it registered
with the configuration tool and leave the rest up to the actual developer of the application
block.
</p>
        <p>
So we add all the necessary files to the project template, include them in the C#
project file and register them in the vstemplate file as such:
</p>
        <pre>&lt;ProjectItem ReplaceParameters="true" TargetFileName="$ApplicationBlockName$CommandRegistrar.cs"&gt;CommandRegistrar.cs&lt;/ProjectItem&gt;
&lt;ProjectItem ReplaceParameters="true" TargetFileName="$ApplicationBlockName$ConfigurationDesignManager.cs"&gt;ConfigurationDesignManager.cs&lt;/ProjectItem&gt;
&lt;ProjectItem ReplaceParameters="true" TargetFileName="$ApplicationBlockName$NodeMapRegistrar.cs"&gt;NodeMapRegistrar.cs&lt;/ProjectItem&gt;
&lt;ProjectItem ReplaceParameters="true" TargetFileName="$ApplicationBlockName$SettingsNode.cs"&gt;SettingsNode.cs&lt;/ProjectItem&gt;
&lt;ProjectItem ReplaceParameters="false" TargetFileName="$ApplicationBlockName$SettingsNode.bmp"&gt;SettingsNode.bmp&lt;/ProjectItem&gt;
</pre>
        <p>
Notice the TargetFileName attribute here, to specify a new name for the file so that
it includes the name of the application block as it was entered by the user. Also
note that we're adding a bitmap file here (without replacing any parameters, to make
sure we're not accidentally messing with the binary data), which is also added in
the C# project file but then as an embedded resource:
</p>
        <pre>&lt;ItemGroup&gt;
  &lt;EmbeddedResource Include="$ApplicationBlockName$SettingsNode.bmp" /&gt;
&lt;/ItemGroup&gt;
</pre>
        <p>
That's all there is to it. If we now register and run the Guidance Package, we'll
get a project that includes all of these files. If we build it (without touching anything),
it will automatically get copied into the configuration tool's directory (because
of the post-build event), and we can already add the application block to an application
directly!
</p>
        <p>
          <img src="http://jelle.druyts.net/content/binary/GAS%20-%20ApplicationBlockInConfigurationTool.png" border="0" />
        </p>
        <p>
          <strong>Generating Classes</strong>
        </p>
        <p>
Now that we already have a root node in the project, suppose we want to add more nodes
for the configuration tool. The number of nodes in the application block depends on
what the developer has in mind and could even be unknown when the application block
is being created. So in stead of adding more classes at the time the solution is unfolded,
let's add a command to Visual Studio that allows us to generate a class on-demand.
This can be done by adding a RecipeReference to a Visual Studio Template or by adding
a separate template for the class in the Templates\Items directory.
</p>
        <ul>
          <li>
With a RecipeReference, you have full control over the process: when the recipe is
executed, you can choose to show a dialog box prompting for more information or you
can go directly to work by executing actions. A drawback is that you also need to
include the actual actions to generate the class content and add it to the project,
although this isn't all too hard.</li>
          <li>
With a Template Reference, you will always get the "Add new item" dialog box that
allows the developer to choose the new class' filename. This can be a drawback if
you need to control the filename as well, but this mechanism does make it easier to
generate the class and add it to the project.</li>
        </ul>
        <p>
Since adding the class as a Template Reference is very similar to adding a solution
as a Template Reference (like we've already been doing all along), let's implement
the new nodes as RecipeReferences. To allow the developer to add dynamically created
items to a project, we need to attach a reference to a recipe in that project's vstemplate
file. In this case, we'll add the following information to the vstemplate file of
the DesignTime project:
</p>
        <pre>&lt;WizardData&gt;
  &lt;Template xmlns="http://schemas.microsoft.com/pag/gax-template" SchemaVersion="1.0"&gt;
    &lt;References&gt;
      &lt;RecipeReference Name="AddConfigurationNode" Target="/" /&gt;
    &lt;/References&gt;
  &lt;/Template&gt;
&lt;/WizardData&gt;
</pre>
        <p>
The References node has been added to register the AddConfigurationNode recipe to
the root of the project (specified by the target). If there would be subdirectories,
you could scope the target down by setting <code>Target="/Nodes"</code>, for example.
</p>
        <p>
The AddConfigurationNode recipe needs to be defined in the package's main xml file
as such:
</p>
        <pre>&lt;Recipe Name="AddConfigurationNode"&gt;
  &lt;Caption&gt;Configuration Node...&lt;/Caption&gt;
  &lt;Description&gt;Adds a Configuration Node class.&lt;/Description&gt;
  &lt;HostData&gt;
    &lt;Icon Guid="FAE04EC1-301F-11d3-BF4B-00C04F79EFBC" ID="4542" /&gt;
    &lt;CommandBar Name="Project Add" /&gt;
  &lt;/HostData&gt;
  &lt;Arguments&gt;
    &lt;!-- User Input --&gt;
    &lt;Argument Name="NodeName" Required="true"&gt;
      &lt;Converter Type="Microsoft.Practices.RecipeFramework.Library.Converters.CodeIdentifierStringConverter, Microsoft.Practices.RecipeFramework.Library"/&gt;
    &lt;/Argument&gt;
    &lt;!-- Derived Arguments --&gt;
    &lt;Argument Name="CurrentProject" Type="EnvDTE.Project, EnvDTE, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"&gt;
      &lt;ValueProvider Type="Microsoft.Practices.RecipeFramework.Library.ValueProviders.FirstSelectedProject, Microsoft.Practices.RecipeFramework.Library" /&gt;
    &lt;/Argument&gt;
    &lt;Argument Name="TargetFile"&gt;
      &lt;ValueProvider Type="Evaluator" Expression="$(NodeName).cs"&gt;
        &lt;MonitorArgument Name="NodeName" /&gt;
      &lt;/ValueProvider&gt;
    &lt;/Argument&gt;
    &lt;Argument Name="TargetNamespace"&gt;
      &lt;Converter Type="Microsoft.Practices.RecipeFramework.Library.Converters.NamespaceStringConverter, Microsoft.Practices.RecipeFramework.Library"/&gt;
      &lt;ValueProvider Type="Evaluator" Expression="$(CurrentProject.Properties.Item('DefaultNamespace').Value)" /&gt;
    &lt;/Argument&gt;
  &lt;/Arguments&gt;
  &lt;GatheringServiceData&gt;
    &lt;Wizard xmlns="http://schemas.microsoft.com/pag/gax-wizards" SchemaVersion="1.0"&gt;
      &lt;Pages&gt;
        &lt;Page&gt;
          &lt;Title&gt;Configuration Node&lt;/Title&gt;
          &lt;Fields&gt;
            &lt;Field ValueName="NodeName" Label="Node Name" InvalidValueMessage="Must be a valid .NET identifier (e.g. it shouldn't contain spaces or special characters)." /&gt;
          &lt;/Fields&gt;
        &lt;/Page&gt;
      &lt;/Pages&gt;
    &lt;/Wizard&gt;
  &lt;/GatheringServiceData&gt;
  &lt;Actions&gt;
    &lt;Action Name="GenerateClass" Type="Microsoft.Practices.RecipeFramework.VisualStudio.Library.Templates.TextTemplateAction, Microsoft.Practices.RecipeFramework.VisualStudio.Library"
				Template="Text\ConfigurationNode.cs.t4" &gt;
      &lt;Input Name="TargetNamespace" RecipeArgument="TargetNamespace" /&gt;
      &lt;Input Name="NodeName" RecipeArgument="NodeName" /&gt;
      &lt;Output Name="Content" /&gt;
    &lt;/Action&gt;
    &lt;Action Name="AddClass" Type="Microsoft.Practices.RecipeFramework.Library.Actions.AddItemFromStringAction, Microsoft.Practices.RecipeFramework.Library"
				Open="true"&gt;
      &lt;Input Name="Content" ActionOutput="GenerateClass.Content" /&gt;
      &lt;Input Name="TargetFileName" RecipeArgument="TargetFile" /&gt;
      &lt;Input Name="Project" RecipeArgument="CurrentProject" /&gt;
    &lt;/Action&gt;
  &lt;/Actions&gt;
&lt;/Recipe&gt;</pre>
        <p>
The recipe definition starts off with the regular caption and description elements.
The <code>HostData</code> section defines where the recipe should be shown in Visual
Studio; in this case we add it only to the project's "Add" context menu (where the
"Add new item" and "Add existing item" menus also live) and supply it with an appropriate
icon from the C# Project dll again. Then, there's a regular argument that the developer
can fill in through the wizard, but the more interesting part is the definition of
the other arguments: we're using a new type of value provider that returns the currently
selected project in Visual Studio (so we can add the new class to it). There are also
some more uses for the <code>ExpressionEvaluatorValueProvider</code>: a simple one
to define the filename based on the class name, and a pretty complex one that finds
the default namespace from the current project by drilling down into its properties
(the nested expression is evaluated at runtime and executed through reflection). Finally,
the <code>actions</code> section defines two distinct stages: a first one to generate
the class content by executing the <code>TextTemplateAction</code>, which transforms
a T4 text template using a quite advanced text processing engine. Afterwards, the
generated class content is added to the currently selected project by using the <code>AddItemFromStringAction</code>.
</p>
        <p>
Finally, the template itself looks like this:
</p>
        <pre>&lt;#@ template language="C#" #&gt;
&lt;#@ assembly name="System.dll" #&gt;
&lt;#@ property processor="PropertyProcessor" name="TargetNamespace" #&gt;
&lt;#@ property processor="PropertyProcessor" name="NodeName" #&gt;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Text;

using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Configuration.Design;

namespace &lt;#= this.TargetNamespace #&gt;
{
    internal sealed class &lt;#= this.NodeName #&gt; : ConfigurationNode
    {
    }
}</pre>
        <p>
This templating syntax looks quite a lot like ASP.NET, so creating and understanding
these templates should be relatively straight-forward. We first declare some properties
that are inputs from the recipe declaration above. These will actually become strongly-typed
properties on the page template object, so we can refer to them later on by using <code>this.NodeName</code>,
for example. At this point, the generated class is extremely simple, but you can actually
perform very powerful code generation with this T4 templating engine by inlining arbitrary
C# or VB.NET code within these ASP.NET-like tags that will drive the code generation
process.
</p>
        <p>
Notice that you also need to register the assemblies you require for the code generation
process. In this case, we just register the System.dll assembly. If, for example,
we needed to use the Visual Studio automation dll (EnvDTE.dll) in the template, we
would need to add the following declarations:
</p>
        <pre>&lt;#@ assembly name="C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\PublicAssemblies\EnvDTE.dll" #&gt;
&lt;#@ import namespace="EnvDTE" #&gt;
</pre>
        <p>
Failing to do so will give you the following nasty error: "CS0246: Compiling transformation:
The type or namespace name 'EnvDTE' could not be found (are you missing a using directive
or an assembly reference?)"
</p>
        <p>
With this in place, it's now possible to add a new configuration node through the
project's context menu:
</p>
        <p>
          <img src="http://jelle.druyts.net/content/binary/GAS%20-%20ApplicationBlockProjectsWithCommand.png" border="0" />
        </p>
        <p>
          <strong>Where Are We</strong>
        </p>
        <p>
At this point, we know how to add default classes to a generated project that have
a customized name. Furthermore, we've touched the surface on the very powerful T4
text templating engine that's part of the Guidance Automation framework. Next time,
we'll define a custom action that generates a strong name key and configure the projects
to automatically be signed with it. We'll also automatically change the solution's
file name to something more meaningful.
</p>
        <p>
          <a href="http://jelle.druyts.net/content/binary/GAS%20-%2005%20-%20Adding%20and%20generating%20classes.zip">Download
the source code for the current state of the Guidance Package.</a>
        </p>
        <img width="0" height="0" src="http://jelle.druyts.net/aggbug.ashx?id=a549ff37-536c-40b6-87d3-9cecff09f4ca" />
      </body>
      <title>GAS06: Generating Classes</title>
      <guid isPermaLink="false">http://jelle.druyts.net/PermaLink.aspx?guid=a549ff37-536c-40b6-87d3-9cecff09f4ca</guid>
      <link>http://jelle.druyts.net/2006/07/03/GAS06GeneratingClasses.aspx</link>
      <pubDate>Mon, 03 Jul 2006 19:39:13 GMT</pubDate>
      <description>&lt;p&gt;
[This is episode 6 of the &lt;a href="http://jelle.druyts.net/CategoryView.aspx?category=Blog%7CProgramming%7C.NET%7CGuidanceAutomation"&gt;Guidance
Automation Series&lt;/a&gt;]
&lt;/p&gt;
&lt;p&gt;
Last time, we added references to Enterprise Library and made sure the build output
was copied to the configuration tool directory. Now to make the application block
show up in this configuration tool, it needs to register some classes and commands
so that the tool knows how to display it. Without implementing the full works here,
let's see how we can already generate some classes that will serve as an excellent
starting point to implement the rest of the application block.
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;Adding Default Classes&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;
Actually, we've already seen how to add classes in the first episode of this series:
the AssemblyInfo.cs file was added to the project by including it in a &lt;code&gt;ProjectItem&lt;/code&gt; inside
the project's vstemplate file &lt;em&gt;and&lt;/em&gt; by including it in the C# project file
(so that MSBuild will build it). The only extra feature we'll be using here is to
change the target file name of each file to include the name of the application block.
&lt;/p&gt;
&lt;p&gt;
To make it possible to add the application block in the configuration tool, we have
to define a &lt;code&gt;ConfigurationDesignManager&lt;/code&gt; class and register it in the AssemblyInfo.cs
file. So we need to update that file and add a bunch more, including a &lt;code&gt;CommandRegistrar&lt;/code&gt;,
a &lt;code&gt;NodeMapRegistrar&lt;/code&gt; and a root settings node. For the purpose of all these
classes and how they should be implemented, I'll gladly refer to &lt;a href="http://blogs.msdn.com/ploeh/"&gt;Mark
Seemann&lt;/a&gt;'s excellent article on "&lt;a href="http://msdn.microsoft.com/msdnmag/issues/06/07/PatternsAndPractices/default.aspx"&gt;Speed
Development With Custom Application Blocks For Enterprise Library&lt;/a&gt;" again. All
we need to do here is provide the minimum amount of information to get it registered
with the configuration tool and leave the rest up to the actual developer of the application
block.
&lt;/p&gt;
&lt;p&gt;
So we add all the necessary files to the project template, include them in the C#
project file and register them in the vstemplate file as such:
&lt;/p&gt;
&lt;pre&gt;&amp;lt;ProjectItem ReplaceParameters="true" TargetFileName="$ApplicationBlockName$CommandRegistrar.cs"&amp;gt;CommandRegistrar.cs&amp;lt;/ProjectItem&amp;gt;
&amp;lt;ProjectItem ReplaceParameters="true" TargetFileName="$ApplicationBlockName$ConfigurationDesignManager.cs"&amp;gt;ConfigurationDesignManager.cs&amp;lt;/ProjectItem&amp;gt;
&amp;lt;ProjectItem ReplaceParameters="true" TargetFileName="$ApplicationBlockName$NodeMapRegistrar.cs"&amp;gt;NodeMapRegistrar.cs&amp;lt;/ProjectItem&amp;gt;
&amp;lt;ProjectItem ReplaceParameters="true" TargetFileName="$ApplicationBlockName$SettingsNode.cs"&amp;gt;SettingsNode.cs&amp;lt;/ProjectItem&amp;gt;
&amp;lt;ProjectItem ReplaceParameters="false" TargetFileName="$ApplicationBlockName$SettingsNode.bmp"&amp;gt;SettingsNode.bmp&amp;lt;/ProjectItem&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
Notice the TargetFileName attribute here, to specify a new name for the file so that
it includes the name of the application block as it was entered by the user. Also
note that we're adding a bitmap file here (without replacing any parameters, to make
sure we're not accidentally messing with the binary data), which is also added in
the C# project file but then as an embedded resource:
&lt;/p&gt;
&lt;pre&gt;&amp;lt;ItemGroup&amp;gt;
  &amp;lt;EmbeddedResource Include="$ApplicationBlockName$SettingsNode.bmp" /&amp;gt;
&amp;lt;/ItemGroup&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
That's all there is to it. If we now register and run the Guidance Package, we'll
get a project that includes all of these files. If we build it (without touching anything),
it will automatically get copied into the configuration tool's directory (because
of the post-build event), and we can already add the application block to an application
directly!
&lt;/p&gt;
&lt;p&gt;
&lt;img src="http://jelle.druyts.net/content/binary/GAS%20-%20ApplicationBlockInConfigurationTool.png" border=0&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;Generating Classes&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;
Now that we already have a root node in the project, suppose we want to add more nodes
for the configuration tool. The number of nodes in the application block depends on
what the developer has in mind and could even be unknown when the application block
is being created. So in stead of adding more classes at the time the solution is unfolded,
let's add a command to Visual Studio that allows us to generate a class on-demand.
This can be done by adding a RecipeReference to a Visual Studio Template or by adding
a separate template for the class in the Templates\Items directory.
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
With a RecipeReference, you have full control over the process: when the recipe is
executed, you can choose to show a dialog box prompting for more information or you
can go directly to work by executing actions. A drawback is that you also need to
include the actual actions to generate the class content and add it to the project,
although this isn't all too hard.&lt;/li&gt;
&lt;li&gt;
With a Template Reference, you will always get the "Add new item" dialog box that
allows the developer to choose the new class' filename. This can be a drawback if
you need to control the filename as well, but this mechanism does make it easier to
generate the class and add it to the project.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
Since adding the class as a Template Reference is very similar to adding a solution
as a Template Reference (like we've already been doing all along), let's implement
the new nodes as RecipeReferences. To allow the developer to add dynamically created
items to a project, we need to attach a reference to a recipe in that project's vstemplate
file. In this case, we'll add the following information to the vstemplate file of
the DesignTime project:
&lt;/p&gt;
&lt;pre&gt;&amp;lt;WizardData&amp;gt;
  &amp;lt;Template xmlns="http://schemas.microsoft.com/pag/gax-template" SchemaVersion="1.0"&amp;gt;
    &amp;lt;References&amp;gt;
      &amp;lt;RecipeReference Name="AddConfigurationNode" Target="/" /&amp;gt;
    &amp;lt;/References&amp;gt;
  &amp;lt;/Template&amp;gt;
&amp;lt;/WizardData&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
The References node has been added to register the AddConfigurationNode recipe to
the root of the project (specified by the target). If there would be subdirectories,
you could scope the target down by setting &lt;code&gt;Target="/Nodes"&lt;/code&gt;, for example.
&lt;/p&gt;
&lt;p&gt;
The AddConfigurationNode recipe needs to be defined in the package's main xml file
as such:
&lt;/p&gt;
&lt;pre&gt;&amp;lt;Recipe Name="AddConfigurationNode"&amp;gt;
  &amp;lt;Caption&amp;gt;Configuration Node...&amp;lt;/Caption&amp;gt;
  &amp;lt;Description&amp;gt;Adds a Configuration Node class.&amp;lt;/Description&amp;gt;
  &amp;lt;HostData&amp;gt;
    &amp;lt;Icon Guid="FAE04EC1-301F-11d3-BF4B-00C04F79EFBC" ID="4542" /&amp;gt;
    &amp;lt;CommandBar Name="Project Add" /&amp;gt;
  &amp;lt;/HostData&amp;gt;
  &amp;lt;Arguments&amp;gt;
    &amp;lt;!-- User Input --&amp;gt;
    &amp;lt;Argument Name="NodeName" Required="true"&amp;gt;
      &amp;lt;Converter Type="Microsoft.Practices.RecipeFramework.Library.Converters.CodeIdentifierStringConverter, Microsoft.Practices.RecipeFramework.Library"/&amp;gt;
    &amp;lt;/Argument&amp;gt;
    &amp;lt;!-- Derived Arguments --&amp;gt;
    &amp;lt;Argument Name="CurrentProject" Type="EnvDTE.Project, EnvDTE, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"&amp;gt;
      &amp;lt;ValueProvider Type="Microsoft.Practices.RecipeFramework.Library.ValueProviders.FirstSelectedProject, Microsoft.Practices.RecipeFramework.Library" /&amp;gt;
    &amp;lt;/Argument&amp;gt;
    &amp;lt;Argument Name="TargetFile"&amp;gt;
      &amp;lt;ValueProvider Type="Evaluator" Expression="$(NodeName).cs"&amp;gt;
        &amp;lt;MonitorArgument Name="NodeName" /&amp;gt;
      &amp;lt;/ValueProvider&amp;gt;
    &amp;lt;/Argument&amp;gt;
    &amp;lt;Argument Name="TargetNamespace"&amp;gt;
      &amp;lt;Converter Type="Microsoft.Practices.RecipeFramework.Library.Converters.NamespaceStringConverter, Microsoft.Practices.RecipeFramework.Library"/&amp;gt;
      &amp;lt;ValueProvider Type="Evaluator" Expression="$(CurrentProject.Properties.Item('DefaultNamespace').Value)" /&amp;gt;
    &amp;lt;/Argument&amp;gt;
  &amp;lt;/Arguments&amp;gt;
  &amp;lt;GatheringServiceData&amp;gt;
    &amp;lt;Wizard xmlns="http://schemas.microsoft.com/pag/gax-wizards" SchemaVersion="1.0"&amp;gt;
      &amp;lt;Pages&amp;gt;
        &amp;lt;Page&amp;gt;
          &amp;lt;Title&amp;gt;Configuration Node&amp;lt;/Title&amp;gt;
          &amp;lt;Fields&amp;gt;
            &amp;lt;Field ValueName="NodeName" Label="Node Name" InvalidValueMessage="Must be a valid .NET identifier (e.g. it shouldn't contain spaces or special characters)." /&amp;gt;
          &amp;lt;/Fields&amp;gt;
        &amp;lt;/Page&amp;gt;
      &amp;lt;/Pages&amp;gt;
    &amp;lt;/Wizard&amp;gt;
  &amp;lt;/GatheringServiceData&amp;gt;
  &amp;lt;Actions&amp;gt;
    &amp;lt;Action Name="GenerateClass" Type="Microsoft.Practices.RecipeFramework.VisualStudio.Library.Templates.TextTemplateAction, Microsoft.Practices.RecipeFramework.VisualStudio.Library"
				Template="Text\ConfigurationNode.cs.t4" &amp;gt;
      &amp;lt;Input Name="TargetNamespace" RecipeArgument="TargetNamespace" /&amp;gt;
      &amp;lt;Input Name="NodeName" RecipeArgument="NodeName" /&amp;gt;
      &amp;lt;Output Name="Content" /&amp;gt;
    &amp;lt;/Action&amp;gt;
    &amp;lt;Action Name="AddClass" Type="Microsoft.Practices.RecipeFramework.Library.Actions.AddItemFromStringAction, Microsoft.Practices.RecipeFramework.Library"
				Open="true"&amp;gt;
      &amp;lt;Input Name="Content" ActionOutput="GenerateClass.Content" /&amp;gt;
      &amp;lt;Input Name="TargetFileName" RecipeArgument="TargetFile" /&amp;gt;
      &amp;lt;Input Name="Project" RecipeArgument="CurrentProject" /&amp;gt;
    &amp;lt;/Action&amp;gt;
  &amp;lt;/Actions&amp;gt;
&amp;lt;/Recipe&amp;gt;&lt;/pre&gt;
&lt;p&gt;
The recipe definition starts off with the regular caption and description elements.
The &lt;code&gt;HostData&lt;/code&gt; section defines where the recipe should be shown in Visual
Studio; in this case we add it only to the project's "Add" context menu (where the
"Add new item" and "Add existing item" menus also live) and supply it with an appropriate
icon from the C# Project dll again. Then, there's a regular argument that the developer
can fill in through the wizard, but the more interesting part is the definition of
the other arguments: we're using a new type of value provider that returns the currently
selected project in Visual Studio (so we can add the new class to it). There are also
some more uses for the &lt;code&gt;ExpressionEvaluatorValueProvider&lt;/code&gt;: a simple one
to define the filename based on the class name, and a pretty complex one that finds
the default namespace from the current project by drilling down into its properties
(the nested expression is evaluated at runtime and executed through reflection). Finally,
the &lt;code&gt;actions&lt;/code&gt; section defines two distinct stages: a first one to generate
the class content by executing the &lt;code&gt;TextTemplateAction&lt;/code&gt;, which transforms
a T4 text template using a quite advanced text processing engine. Afterwards, the
generated class content is added to the currently selected project by using the &lt;code&gt;AddItemFromStringAction&lt;/code&gt;.
&lt;/p&gt;
&lt;p&gt;
Finally, the template itself looks like this:
&lt;/p&gt;
&lt;pre&gt;&amp;lt;#@ template language="C#" #&amp;gt;
&amp;lt;#@ assembly name="System.dll" #&amp;gt;
&amp;lt;#@ property processor="PropertyProcessor" name="TargetNamespace" #&amp;gt;
&amp;lt;#@ property processor="PropertyProcessor" name="NodeName" #&amp;gt;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Text;

using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Configuration.Design;

namespace &amp;lt;#= this.TargetNamespace #&amp;gt;
{
    internal sealed class &amp;lt;#= this.NodeName #&amp;gt; : ConfigurationNode
    {
    }
}&lt;/pre&gt;
&lt;p&gt;
This templating syntax looks quite a lot like ASP.NET, so creating and understanding
these templates should be relatively straight-forward. We first declare some properties
that are inputs from the recipe declaration above. These will actually become strongly-typed
properties on the page template object, so we can refer to them later on by using &lt;code&gt;this.NodeName&lt;/code&gt;,
for example. At this point, the generated class is extremely simple, but you can actually
perform very powerful code generation with this T4 templating engine by inlining arbitrary
C# or VB.NET code within these ASP.NET-like tags that will drive the code generation
process.
&lt;/p&gt;
&lt;p&gt;
Notice that you also need to register the assemblies you require for the code generation
process. In this case, we just register the System.dll assembly. If, for example,
we needed to use the Visual Studio automation dll (EnvDTE.dll) in the template, we
would need to add the following declarations:
&lt;/p&gt;
&lt;pre&gt;&amp;lt;#@ assembly name="C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\PublicAssemblies\EnvDTE.dll" #&amp;gt;
&amp;lt;#@ import namespace="EnvDTE" #&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
Failing to do so will give you the following nasty error: "CS0246: Compiling transformation:
The type or namespace name 'EnvDTE' could not be found (are you missing a using directive
or an assembly reference?)"
&lt;/p&gt;
&lt;p&gt;
With this in place, it's now possible to add a new configuration node through the
project's context menu:
&lt;/p&gt;
&lt;p&gt;
&lt;img src="http://jelle.druyts.net/content/binary/GAS%20-%20ApplicationBlockProjectsWithCommand.png" border=0&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;Where Are We&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;
At this point, we know how to add default classes to a generated project that have
a customized name. Furthermore, we've touched the surface on the very powerful T4
text templating engine that's part of the Guidance Automation framework. Next time,
we'll define a custom action that generates a strong name key and configure the projects
to automatically be signed with it. We'll also automatically change the solution's
file name to something more meaningful.
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://jelle.druyts.net/content/binary/GAS%20-%2005%20-%20Adding%20and%20generating%20classes.zip"&gt;Download
the source code for the current state of the Guidance Package.&lt;/a&gt;
&lt;/p&gt;
&lt;img width="0" height="0" src="http://jelle.druyts.net/aggbug.ashx?id=a549ff37-536c-40b6-87d3-9cecff09f4ca" /&gt;</description>
      <comments>http://jelle.druyts.net/CommentView.aspx?guid=a549ff37-536c-40b6-87d3-9cecff09f4ca</comments>
      <category>Blog</category>
      <category>Blog/Programming</category>
      <category>Blog/Programming/.NET</category>
      <category>Blog/Programming/.NET/GuidanceAutomation</category>
      <category>Blog/Programming/.NET/VS.NET</category>
    </item>
    <item>
      <trackback:ping>http://jelle.druyts.net/Trackback.aspx?guid=114ab5d7-e912-4074-b36c-a5eae77beb46</trackback:ping>
      <pingback:server>http://jelle.druyts.net/pingback.aspx</pingback:server>
      <pingback:target>http://jelle.druyts.net/PermaLink.aspx?guid=114ab5d7-e912-4074-b36c-a5eae77beb46</pingback:target>
      <dc:creator>Jelle Druyts</dc:creator>
      <wfw:comment>http://jelle.druyts.net/CommentView.aspx?guid=114ab5d7-e912-4074-b36c-a5eae77beb46</wfw:comment>
      <wfw:commentRss>http://jelle.druyts.net/SyndicationService.asmx/GetEntryCommentsRss?guid=114ab5d7-e912-4074-b36c-a5eae77beb46</wfw:commentRss>
      <slash:comments>1</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
[This is episode 5 of the <a href="http://jelle.druyts.net/CategoryView.aspx?category=Blog%7CProgramming%7C.NET%7CGuidanceAutomation">Guidance
Automation Series</a>]
</p>
        <p>
Last time, we left off with two empty projects that had a project reference between
them. If you're still not too thrilled by all that, let's take it one step further:
we'll add binary references to Enterprise Library and make sure that the project output
is always copied to the Enterprise Library Configuration Tool directory (otherwise,
the application block won't show up in the configuration tool). The first one involves
knowing where to find those references, the second one involves executing a post-build
command that copies the project output to the proper location.
</p>
        <p>
          <strong>Adding Binary References</strong>
        </p>
        <p>
Adding a binary reference in itself is pretty straight-forward. The project references
are stored in the C# project file (which is an MSBuild file) and can therefore easily
be added in the project's template. The only minor difficulty is that we need to provide
the hint-path, i.e. the physical location where the assemblies can be found.
</p>
        <p>
In this case, we'll be adding references to Enterprise Library assemblies, so we need
to know the directory where Enterprise Library was installed. By default, this is
in C:\Program Files\Microsoft Enterprise Library January 2006\bin, but it could have
been installed elsewhere so we better verify this with the user. The easiest way,
of course, is to make this a recipe argument, just like with the application block's
name and namespace:
</p>
        <pre>&lt;Argument Name="EnterpriseLibraryPath" Required="true"&gt;
  &lt;ValueProvider Type="Evaluator" Expression="C:\Program Files\Microsoft Enterprise Library January 2006\bin" /&gt;
&lt;/Argument&gt;</pre>
        <p>
Notice that we're using the <code>ExpressionEvaluatorValueProvider</code> again, but
this time without a complex expression. Used in this way, it just sets a default value
for the argument that can be modified by the user later on. If we now want to add
a binary reference to the Microsoft.Practices.EnterpriseLibrary.Common.dll assembly,
for example, we can just add the following lines to the itemgroup containing the references:
</p>
        <pre>&lt;Reference Include="Microsoft.Practices.EnterpriseLibrary.Common, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null, processorArchitecture=MSIL"&gt;
  &lt;SpecificVersion&gt;False&lt;/SpecificVersion&gt;
  &lt;HintPath&gt;$EnterpriseLibraryPath$\Microsoft.Practices.EnterpriseLibrary.Common.dll&lt;/HintPath&gt;
&lt;/Reference&gt;
</pre>
        <p>
Notice that the <code>$EnterpriseLibraryPath$</code> parameter will automatically
be replaced by the directory that the user chose in the wizard. So finally, the wizard's
page definition will also need to include the new field:
</p>
        <pre>&lt;Field ValueName="EnterpriseLibraryPath" Label="Enterprise Library Path"&gt;
  &lt;Editor Type="System.Windows.Forms.Design.FolderNameEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/&gt;
&lt;/Field&gt;
</pre>
        <p>
This time, we're also providing an editor that allows the user to select the directory
more easily using a folder browser window.
</p>
        <p>
          <strong>Adding A Post-Build Command</strong>
        </p>
        <p>
Now that we've referenced Enterprise Library, it's also a good time to make sure that
the project output is always copied to the Enterprise Library directory. Otherwise,
the application block is quite unuseable. The easiest way to do this is to add a post-build
command to both projects that simply copy the project's assemblies to the EnterpriseLibraryPath
the user has already chosen. Just like with the binary references, this is just a
matter of modifying the MSBuild project file to include a new property group as such:
</p>
        <pre>&lt;PropertyGroup&gt;
  &lt;PostBuildEvent&gt;$PostBuildCommand$&lt;/PostBuildEvent&gt;
&lt;/PropertyGroup&gt;
</pre>
        <p>
Creating the post-build command itself is a little trickier: it depends on the EnterpriseLibraryPath
argument, and needs to build a more complex string based on that argument's value.
The post-build command itself (as defined in Visual Studio) is simply:
</p>
        <code>
          <p>
COPY /Y $(TargetPath) "&lt;Enterprise Library Path&gt;"
</p>
          <p>
          </p>
        </code>Where we need to replace &lt;Enterprise Library Path&gt; with the value
of the EnterpriseLibraryPath argument. So, how can we provide this value to the <code>$PostBuildCommand$</code> argument?
By building our own Value Provider, of course.
<p>
We start off by defining the argument itself:
</p><pre>&lt;Argument Name="PostBuildCommand"&gt;
  &lt;ValueProvider Type="JelleDruyts.EnterpriseLibraryGuidance.ValueProviders.PostBuildCommandValueProvider, JelleDruyts.EnterpriseLibraryGuidance"
                 EnterpriseLibraryPathArgument="EnterpriseLibraryPath"&gt;
    &lt;MonitorArgument Name="EnterpriseLibraryPath" /&gt;
  &lt;/ValueProvider&gt;
&lt;/Argument&gt;
</pre><p>
Notice that the value provider is now of our own type, namely the PostBuildCommandValueProvider
we'll be building, and that it monitors the EnterpriseLibraryPath argument (since
it depends on it). Furthermore, since the value provider needs access to the value
of the EnterpriseLibraryPath argument, we also need to specify that argument's name,
which is done through a custom EnterpriseLibraryPathArgument attribute on the <code>ValueProvider</code> node.
</p><p>
The code for the Value Provider isn't all that difficult, either. It's a class that
inherits from the <code>ValueProvider</code> base class and that also implements the <code>IAttributesConfigurable</code> interface
to support that custom EnterpriseLibraryPathArgument attribute. It can then override
the <code>OnArgumentChanged</code> method that gets called when a monitored argument
has changed, and re-generate the post-build command as such:
</p><pre>public override bool OnArgumentChanged(string changedArgumentName, object changedArgumentValue, object currentValue, out object newValue)
{
    // Get the name of the argument that defines the EnterpriseLibraryPath.
    string enterpriseLibraryPathArgumentName = m_Attributes[EnterpriseLibraryPathArgumentAttribute];
    
    // Get the dictionary that contains the recipe arguments.
    IDictionaryService dictionary = this.GetService&lt;IDictionaryService&gt;(true);

    // Get the current value of the EnterpriseLibraryPath argument.
    string enterpriseLibraryPath = (string)dictionary.GetValue(enterpriseLibraryPathArgumentName);

    // Create the post-build command.
    newValue = string.Format("COPY /Y \"$(TargetPath)\" \"{0}\"", enterpriseLibraryPath);
    return true;
}
</pre><p>
Now we have an argument containing the post-build command that automatically contains
the proper directory based on the Enterprise Library directory the user has chosen
in the wizard dialog:
</p><p><img src="http://jelle.druyts.net/content/binary/GAS%20-%20ApplicationBlockWizardWithEntLib.png" border="0" /></p><p><strong>Where Are We</strong></p><p>
We've seen how to add binary references to the C# projects by modifying the MSBuild
files and gathering user input through the wizard. We've also built a custom Value
Provider that depends on the value of another argument, and used that as a post-build
command. Next time, we'll see how we can generate some classes so that the application
block can already be added to the Enterprise Library Configuration Tool.
</p><p><a href="http://jelle.druyts.net/content/binary/GAS%20-%2004%20-%20Add%20binary%20references%20and%20a%20post-build%20command.zip">Download
the source code for the current state of the Guidance Package.</a></p><img width="0" height="0" src="http://jelle.druyts.net/aggbug.ashx?id=114ab5d7-e912-4074-b36c-a5eae77beb46" /></body>
      <title>GAS05: Tuning the C# projects</title>
      <guid isPermaLink="false">http://jelle.druyts.net/PermaLink.aspx?guid=114ab5d7-e912-4074-b36c-a5eae77beb46</guid>
      <link>http://jelle.druyts.net/2006/06/29/GAS05TuningTheCProjects.aspx</link>
      <pubDate>Thu, 29 Jun 2006 19:19:25 GMT</pubDate>
      <description>&lt;p&gt;
[This is episode 5 of the &lt;a href="http://jelle.druyts.net/CategoryView.aspx?category=Blog%7CProgramming%7C.NET%7CGuidanceAutomation"&gt;Guidance
Automation Series&lt;/a&gt;]
&lt;/p&gt;
&lt;p&gt;
Last time, we left off with two empty projects that had a project reference between
them. If you're still not too thrilled by all that, let's take it one step further:
we'll add binary references to Enterprise Library and make sure that the project output
is always copied to the Enterprise Library Configuration Tool directory (otherwise,
the application block won't show up in the configuration tool). The first one involves
knowing where to find those references, the second one involves executing a post-build
command that copies the project output to the proper location.
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;Adding Binary References&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;
Adding a binary reference in itself is pretty straight-forward. The project references
are stored in the C# project file (which is an MSBuild file) and can therefore easily
be added in the project's template. The only minor difficulty is that we need to provide
the hint-path, i.e. the physical location where the assemblies can be found.
&lt;/p&gt;
&lt;p&gt;
In this case, we'll be adding references to Enterprise Library assemblies, so we need
to know the directory where Enterprise Library was installed. By default, this is
in C:\Program Files\Microsoft Enterprise Library January 2006\bin, but it could have
been installed elsewhere so we better verify this with the user. The easiest way,
of course, is to make this a recipe argument, just like with the application block's
name and namespace:
&lt;/p&gt;
&lt;pre&gt;&amp;lt;Argument Name="EnterpriseLibraryPath" Required="true"&amp;gt;
  &amp;lt;ValueProvider Type="Evaluator" Expression="C:\Program Files\Microsoft Enterprise Library January 2006\bin" /&amp;gt;
&amp;lt;/Argument&amp;gt;&lt;/pre&gt;
&lt;p&gt;
Notice that we're using the &lt;code&gt;ExpressionEvaluatorValueProvider&lt;/code&gt; again, but
this time without a complex expression. Used in this way, it just sets a default value
for the argument that can be modified by the user later on. If we now want to add
a binary reference to the Microsoft.Practices.EnterpriseLibrary.Common.dll assembly,
for example, we can just add the following lines to the itemgroup containing the references:
&lt;/p&gt;
&lt;pre&gt;&amp;lt;Reference Include="Microsoft.Practices.EnterpriseLibrary.Common, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null, processorArchitecture=MSIL"&amp;gt;
  &amp;lt;SpecificVersion&amp;gt;False&amp;lt;/SpecificVersion&amp;gt;
  &amp;lt;HintPath&amp;gt;$EnterpriseLibraryPath$\Microsoft.Practices.EnterpriseLibrary.Common.dll&amp;lt;/HintPath&amp;gt;
&amp;lt;/Reference&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
Notice that the &lt;code&gt;$EnterpriseLibraryPath$&lt;/code&gt; parameter will automatically
be replaced by the directory that the user chose in the wizard. So finally, the wizard's
page definition will also need to include the new field:
&lt;/p&gt;
&lt;pre&gt;&amp;lt;Field ValueName="EnterpriseLibraryPath" Label="Enterprise Library Path"&amp;gt;
  &amp;lt;Editor Type="System.Windows.Forms.Design.FolderNameEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/&amp;gt;
&amp;lt;/Field&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
This time, we're also providing an editor that allows the user to select the directory
more easily using a folder browser window.
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;Adding A Post-Build Command&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;
Now that we've referenced Enterprise Library, it's also a good time to make sure that
the project output is always copied to the Enterprise Library directory. Otherwise,
the application block is quite unuseable. The easiest way to do this is to add a post-build
command to both projects that simply copy the project's assemblies to the EnterpriseLibraryPath
the user has already chosen. Just like with the binary references, this is just a
matter of modifying the MSBuild project file to include a new property group as such:
&lt;/p&gt;
&lt;pre&gt;&amp;lt;PropertyGroup&amp;gt;
  &amp;lt;PostBuildEvent&amp;gt;$PostBuildCommand$&amp;lt;/PostBuildEvent&amp;gt;
&amp;lt;/PropertyGroup&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
Creating the post-build command itself is a little trickier: it depends on the EnterpriseLibraryPath
argument, and needs to build a more complex string based on that argument's value.
The post-build command itself (as defined in Visual Studio) is simply:
&lt;/p&gt;
&lt;code&gt; 
&lt;p&gt;
COPY /Y $(TargetPath) "&amp;lt;Enterprise Library Path&amp;gt;"
&lt;/p&gt;
&lt;p&gt;
&lt;/code&gt;Where we need to replace &amp;lt;Enterprise Library Path&amp;gt; with the value of
the EnterpriseLibraryPath argument. So, how can we provide this value to the &lt;code&gt;$PostBuildCommand$&lt;/code&gt; argument?
By building our own Value Provider, of course.&gt;
&lt;p&gt;
We start off by defining the argument itself:
&lt;/p&gt;
&lt;pre&gt;&amp;lt;Argument Name="PostBuildCommand"&amp;gt;
  &amp;lt;ValueProvider Type="JelleDruyts.EnterpriseLibraryGuidance.ValueProviders.PostBuildCommandValueProvider, JelleDruyts.EnterpriseLibraryGuidance"
                 EnterpriseLibraryPathArgument="EnterpriseLibraryPath"&amp;gt;
    &amp;lt;MonitorArgument Name="EnterpriseLibraryPath" /&amp;gt;
  &amp;lt;/ValueProvider&amp;gt;
&amp;lt;/Argument&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
Notice that the value provider is now of our own type, namely the PostBuildCommandValueProvider
we'll be building, and that it monitors the EnterpriseLibraryPath argument (since
it depends on it). Furthermore, since the value provider needs access to the value
of the EnterpriseLibraryPath argument, we also need to specify that argument's name,
which is done through a custom EnterpriseLibraryPathArgument attribute on the &lt;code&gt;ValueProvider&lt;/code&gt; node.
&lt;/p&gt;
&lt;p&gt;
The code for the Value Provider isn't all that difficult, either. It's a class that
inherits from the &lt;code&gt;ValueProvider&lt;/code&gt; base class and that also implements the &lt;code&gt;IAttributesConfigurable&lt;/code&gt; interface
to support that custom EnterpriseLibraryPathArgument attribute. It can then override
the &lt;code&gt;OnArgumentChanged&lt;/code&gt; method that gets called when a monitored argument
has changed, and re-generate the post-build command as such:
&lt;/p&gt;
&lt;pre&gt;public override bool OnArgumentChanged(string changedArgumentName, object changedArgumentValue, object currentValue, out object newValue)
{
    // Get the name of the argument that defines the EnterpriseLibraryPath.
    string enterpriseLibraryPathArgumentName = m_Attributes[EnterpriseLibraryPathArgumentAttribute];
    
    // Get the dictionary that contains the recipe arguments.
    IDictionaryService dictionary = this.GetService&amp;lt;IDictionaryService&amp;gt;(true);

    // Get the current value of the EnterpriseLibraryPath argument.
    string enterpriseLibraryPath = (string)dictionary.GetValue(enterpriseLibraryPathArgumentName);

    // Create the post-build command.
    newValue = string.Format("COPY /Y \"$(TargetPath)\" \"{0}\"", enterpriseLibraryPath);
    return true;
}
&lt;/pre&gt;
&lt;p&gt;
Now we have an argument containing the post-build command that automatically contains
the proper directory based on the Enterprise Library directory the user has chosen
in the wizard dialog:
&lt;/p&gt;
&lt;p&gt;
&lt;img src="http://jelle.druyts.net/content/binary/GAS%20-%20ApplicationBlockWizardWithEntLib.png" border=0&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;Where Are We&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;
We've seen how to add binary references to the C# projects by modifying the MSBuild
files and gathering user input through the wizard. We've also built a custom Value
Provider that depends on the value of another argument, and used that as a post-build
command. Next time, we'll see how we can generate some classes so that the application
block can already be added to the Enterprise Library Configuration Tool.
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://jelle.druyts.net/content/binary/GAS%20-%2004%20-%20Add%20binary%20references%20and%20a%20post-build%20command.zip"&gt;Download
the source code for the current state of the Guidance Package.&lt;/a&gt;
&lt;/p&gt;
&lt;img width="0" height="0" src="http://jelle.druyts.net/aggbug.ashx?id=114ab5d7-e912-4074-b36c-a5eae77beb46" /&gt;</description>
      <comments>http://jelle.druyts.net/CommentView.aspx?guid=114ab5d7-e912-4074-b36c-a5eae77beb46</comments>
      <category>Blog</category>
      <category>Blog/Programming</category>
      <category>Blog/Programming/.NET</category>
      <category>Blog/Programming/.NET/GuidanceAutomation</category>
      <category>Blog/Programming/.NET/VS.NET</category>
    </item>
    <item>
      <trackback:ping>http://jelle.druyts.net/Trackback.aspx?guid=19c5d307-3a4a-4b02-95b7-c68ef0021006</trackback:ping>
      <pingback:server>http://jelle.druyts.net/pingback.aspx</pingback:server>
      <pingback:target>http://jelle.druyts.net/PermaLink.aspx?guid=19c5d307-3a4a-4b02-95b7-c68ef0021006</pingback:target>
      <dc:creator>Jelle Druyts</dc:creator>
      <wfw:comment>http://jelle.druyts.net/CommentView.aspx?guid=19c5d307-3a4a-4b02-95b7-c68ef0021006</wfw:comment>
      <wfw:commentRss>http://jelle.druyts.net/SyndicationService.asmx/GetEntryCommentsRss?guid=19c5d307-3a4a-4b02-95b7-c68ef0021006</wfw:commentRss>
      <slash:comments>3</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
[This is episode 4 of the <a href="http://jelle.druyts.net/CategoryView.aspx?category=Blog%7CProgramming%7C.NET%7CGuidanceAutomation">Guidance
Automation Series</a>]
</p>
        <p>
Last time, we did a whole lot of work just to generate one very empty C# project.
Just think of that as the healthy dish before the dessert: it's probably necessary
but not really what you've been waiting for. This time, we'll really start using the
power of Guidance Automation to generate a second project and add a project reference
to it.
</p>
        <p>
          <strong>Adding Another Project</strong>
        </p>
        <p>
Now that we have seen how to generate the runtime project for the Enterprise Library
Application Block, we'll just do the same thing to generate the design-time project.
By convention, this has the same name as the runtime project, with ".Configuration.Design"
added at the end. This is easily done in the solution's vstemplate file by adding
a second project to the <code>ProjectCollection</code> node as such:
</p>
        <pre>&lt;ProjectTemplateLink ProjectName="$ApplicationBlockNamespace$.$ApplicationBlockName$.Configuration.Design"&gt;Projects\DesignTime\DesignTime.vstemplate&lt;/ProjectTemplateLink&gt;</pre>
        <p>
For this to work, we also need the DesignTime.vstemplate file in the proper location,
but that's pretty easy to do: we can just copy the Runtime.vstemplate file and associated
project files and make a few basic modifications to change the name. So with very
little additional work, we have now generated two empty projects already.
</p>
        <p>
Now of course, this design-time project will need information from the runtime project
(e.g. the configuration types of the application block that are used by the runtime
but need to be configured in the design-time project), so we need to add a project
reference to it. Project references are actually stored in the referencing project's
project file, but this isn't something we can generate out-of-the-box like before
because it uses the GUID from the referenced project. This GUID is generated by Visual
Studio at the time the solution is unfolded (remember the <code>$guid1$</code> parameter)
and we don't have access to that GUID outside the scope of that file. But Visual Studio
knows how to add Project References, so we actually want to have the environment perform
some action for us.
</p>
        <p>
          <strong>Actions</strong>
        </p>
        <p>
Actions are actually a big part of Guidance Automation, and they represent pieces
of code that can run at the end of a recipe (and in other places too, but let's stick
to the current problem). There are a lot of predefined actions in the Guidance Automation
framework, but you can also define your own. To use an action, it can be registered
in the <code>Actions</code> section in the recipe xml file, so that each one will
be run in sequence at the end of the recipe. Actions have input and output arguments
that can be configured, and it is important to note that these are strongly typed,
which means that you're really passing objects from and actions and not just strings.
The input arguments can either come from the recipe (e.g. a value the user has entered
in the wizard), or they can be the output of another action.
</p>
        <p>
          <strong>Adding The Project Reference</strong>
        </p>
        <p>
To add a Project Reference, the predefined AddProjectReferenceAction seems to be just
what we need here. It takes two input arguments: the referring project and the referenced
project. Both are of the type EnvDTE.Project, meaning that they represent the actual
project automation objects in Visual Studio. So we need a way to retrieve these projects,
and that's where another action, aptly named GetProjectAction comes into play. This
searches for a project based on its name and puts the project in an output argument
so we can chain the two together as follows:
</p>
        <pre>&lt;Actions&gt;
  &lt;Action Name="GetRuntimeProject" Type="Microsoft.Practices.RecipeFramework.Library.Actions.GetProjectAction, Microsoft.Practices.RecipeFramework.Library"&gt;
    &lt;Input Name="ProjectName" RecipeArgument="RuntimeProjectName" /&gt;
    &lt;Output Name="Project" /&gt;
  &lt;/Action&gt;
  &lt;Action Name="GetDesignTimeProject" Type="Microsoft.Practices.RecipeFramework.Library.Actions.GetProjectAction, Microsoft.Practices.RecipeFramework.Library"&gt;
    &lt;Input Name="ProjectName" RecipeArgument="DesignTimeProjectName" /&gt;
    &lt;Output Name="Project" /&gt;
  &lt;/Action&gt;
  &lt;Action Name="AddProjectReference" Type="Microsoft.Practices.RecipeFramework.Library.Solution.Actions.AddProjectReferenceAction, Microsoft.Practices.RecipeFramework.Library"&gt;
    &lt;Input Name="ReferringProject" ActionOutput="GetDesignTimeProject.Project" /&gt;
    &lt;Input Name="ReferencedProject" ActionOutput="GetRuntimeProject.Project" /&gt;
  &lt;/Action&gt;
&lt;/Actions&gt;</pre>
        <p>
Notice that the <code>AddProjectReference</code> action has inputs that come from
the other two actions, and these in turn have inputs that come from the recipe. Now
if you remember, the user didn't specifically provide the name of the projects to
be generated, only the namespace and the name of the application block. We can use
these arguments to derive the actual project names using the built-in <code>ExpressionEvaluatorValueProvider</code>.
</p>
        <p>
          <strong>Value Providers</strong>
        </p>
        <p>
A value provider is a class that provides values to arguments. Just like a <code>Converter</code> can
convert and validate arguments, value providers can put values into arguments. Some
of the built-in value providers can return the currently selected class in Visual
Studio or a certain project by its name, but there's also a pretty powerful <code>ExpressionEvaluatorValueProvider</code> that
can return values based on other arguments through expressions.
</p>
        <p>
In this case, we simply want to build up the name of the runtime project by appending
the two fields the user has entered:
</p>
        <pre>&lt;Argument Name="RuntimeProjectName"&gt;
  &lt;ValueProvider Type="Evaluator" Expression="$(ApplicationBlockNamespace).$(ApplicationBlockName)"&gt;
    &lt;MonitorArgument Name="ApplicationBlockNamespace" /&gt;
    &lt;MonitorArgument Name="ApplicationBlockName" /&gt;
  &lt;/ValueProvider&gt;
&lt;/Argument&gt;
</pre>
        <p>
By also supplying the <code>MonitorArgument</code> values, the expression will automatically
be re-evaluated if one of the monitored arguments has changed. We can now base the
design-time project's name on this argument by appending a suffix to the project name
as follows:
</p>
        <pre>&lt;Argument Name="DesignTimeProjectSuffix"&gt;
  &lt;ValueProvider Type="Evaluator" Expression="Configuration.Design" /&gt;
&lt;/Argument&gt;
&lt;Argument Name="DesignTimeProjectName"&gt;
  &lt;ValueProvider Type="Evaluator" Expression="$(RuntimeProjectName).$(DesignTimeProjectSuffix)"&gt;
    &lt;MonitorArgument Name="RuntimeProjectName" /&gt;
    &lt;MonitorArgument Name="DesignTimeProjectSuffix" /&gt;
  &lt;/ValueProvider&gt;
&lt;/Argument&gt;
</pre>
        <p>
          <em>Side note: it would also have been possible to use the expression "$(RuntimeProjectName).$(DesignTimeProjectSuffix).Configuration.Design"
directly (without creating the DesignTimeProjectSuffix argument) but unfortunately
there's <a href="http://weblogs.asp.net/cazzu/archive/2006/06/09/RegexBug.aspx">a
problem with the expression parser that makes the wizard very slow</a> in this case.</em>
        </p>
        <p>
Now that we have the names for both projects stored in arguments, we can also replace
the project names in the vstemplate files that were still made up of the application
block's name and namespace individually. E.g., we can replace <code>ProjectName="$ApplicationBlockNamespace$.$ApplicationBlockName$"</code> by <code>ProjectName="$RuntimeProjectName$"</code>.
</p>
        <p>
          <strong>Where Are We</strong>
        </p>
        <p>
At this point, we've seen how to generate a second project with a project reference
by using actions that are chained together. We've also seen how to use value providers
that put values into arguments and how to use these values in actions. Next time,
we'll see how to build our own value providers and have them interact with other arguments.
Furthermore, we'll add binary references to some of the Enterprise Library assemblies
and add post-build events to the projects. Stay tuned!
</p>
        <p>
        </p>
        <p>
          <a href="http://jelle.druyts.net/content/binary/GAS%20-%2003%20-%20Add%20a%20project%20with%20a%20project%20reference.zip">Download
the source code for the current state of the Guidance Package.</a>
        </p>
        <img width="0" height="0" src="http://jelle.druyts.net/aggbug.ashx?id=19c5d307-3a4a-4b02-95b7-c68ef0021006" />
      </body>
      <title>GAS04: Adding Project References</title>
      <guid isPermaLink="false">http://jelle.druyts.net/PermaLink.aspx?guid=19c5d307-3a4a-4b02-95b7-c68ef0021006</guid>
      <link>http://jelle.druyts.net/2006/06/28/GAS04AddingProjectReferences.aspx</link>
      <pubDate>Wed, 28 Jun 2006 19:06:58 GMT</pubDate>
      <description>&lt;p&gt;
[This is episode 4 of the &lt;a href="http://jelle.druyts.net/CategoryView.aspx?category=Blog%7CProgramming%7C.NET%7CGuidanceAutomation"&gt;Guidance
Automation Series&lt;/a&gt;]
&lt;/p&gt;
&lt;p&gt;
Last time, we did a whole lot of work just to generate one very empty C# project.
Just think of that as the healthy dish before the dessert: it's probably necessary
but not really what you've been waiting for. This time, we'll really start using the
power of Guidance Automation to generate a second project and add a project reference
to it.
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;Adding Another Project&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;
Now that we have seen how to generate the runtime project for the Enterprise Library
Application Block, we'll just do the same thing to generate the design-time project.
By convention, this has the same name as the runtime project, with ".Configuration.Design"
added at the end. This is easily done in the solution's vstemplate file by adding
a second project to the &lt;code&gt;ProjectCollection&lt;/code&gt; node as such:
&lt;/p&gt;
&lt;pre&gt;&amp;lt;ProjectTemplateLink ProjectName="$ApplicationBlockNamespace$.$ApplicationBlockName$.Configuration.Design"&amp;gt;Projects\DesignTime\DesignTime.vstemplate&amp;lt;/ProjectTemplateLink&amp;gt;&lt;/pre&gt;
&lt;p&gt;
For this to work, we also need the DesignTime.vstemplate file in the proper location,
but that's pretty easy to do: we can just copy the Runtime.vstemplate file and associated
project files and make a few basic modifications to change the name. So with very
little additional work, we have now generated two empty projects already.
&lt;/p&gt;
&lt;p&gt;
Now of course, this design-time project will need information from the runtime project
(e.g. the configuration types of the application block that are used by the runtime
but need to be configured in the design-time project), so we need to add a project
reference to it. Project references are actually stored in the referencing project's
project file, but this isn't something we can generate out-of-the-box like before
because it uses the GUID from the referenced project. This GUID is generated by Visual
Studio at the time the solution is unfolded (remember the &lt;code&gt;$guid1$&lt;/code&gt; parameter)
and we don't have access to that GUID outside the scope of that file. But Visual Studio
knows how to add Project References, so we actually want to have the environment perform
some action for us.
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;Actions&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;
Actions are actually a big part of Guidance Automation, and they represent pieces
of code that can run at the end of a recipe (and in other places too, but let's stick
to the current problem). There are a lot of predefined actions in the Guidance Automation
framework, but you can also define your own. To use an action, it can be registered
in the &lt;code&gt;Actions&lt;/code&gt; section in the recipe xml file, so that each one will
be run in sequence at the end of the recipe. Actions have input and output arguments
that can be configured, and it is important to note that these are strongly typed,
which means that you're really passing objects from and actions and not just strings.
The input arguments can either come from the recipe (e.g. a value the user has entered
in the wizard), or they can be the output of another action.
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;Adding The Project Reference&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;
To add a Project Reference, the predefined AddProjectReferenceAction seems to be just
what we need here. It takes two input arguments: the referring project and the referenced
project. Both are of the type EnvDTE.Project, meaning that they represent the actual
project automation objects in Visual Studio. So we need a way to retrieve these projects,
and that's where another action, aptly named GetProjectAction comes into play. This
searches for a project based on its name and puts the project in an output argument
so we can chain the two together as follows:
&lt;/p&gt;
&lt;pre&gt;&amp;lt;Actions&amp;gt;
  &amp;lt;Action Name="GetRuntimeProject" Type="Microsoft.Practices.RecipeFramework.Library.Actions.GetProjectAction, Microsoft.Practices.RecipeFramework.Library"&amp;gt;
    &amp;lt;Input Name="ProjectName" RecipeArgument="RuntimeProjectName" /&amp;gt;
    &amp;lt;Output Name="Project" /&amp;gt;
  &amp;lt;/Action&amp;gt;
  &amp;lt;Action Name="GetDesignTimeProject" Type="Microsoft.Practices.RecipeFramework.Library.Actions.GetProjectAction, Microsoft.Practices.RecipeFramework.Library"&amp;gt;
    &amp;lt;Input Name="ProjectName" RecipeArgument="DesignTimeProjectName" /&amp;gt;
    &amp;lt;Output Name="Project" /&amp;gt;
  &amp;lt;/Action&amp;gt;
  &amp;lt;Action Name="AddProjectReference" Type="Microsoft.Practices.RecipeFramework.Library.Solution.Actions.AddProjectReferenceAction, Microsoft.Practices.RecipeFramework.Library"&amp;gt;
    &amp;lt;Input Name="ReferringProject" ActionOutput="GetDesignTimeProject.Project" /&amp;gt;
    &amp;lt;Input Name="ReferencedProject" ActionOutput="GetRuntimeProject.Project" /&amp;gt;
  &amp;lt;/Action&amp;gt;
&amp;lt;/Actions&amp;gt;&lt;/pre&gt;
&lt;p&gt;
Notice that the &lt;code&gt;AddProjectReference&lt;/code&gt; action has inputs that come from
the other two actions, and these in turn have inputs that come from the recipe. Now
if you remember, the user didn't specifically provide the name of the projects to
be generated, only the namespace and the name of the application block. We can use
these arguments to derive the actual project names using the built-in &lt;code&gt;ExpressionEvaluatorValueProvider&lt;/code&gt;.
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;Value Providers&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;
A value provider is a class that provides values to arguments. Just like a &lt;code&gt;Converter&lt;/code&gt; can
convert and validate arguments, value providers can put values into arguments. Some
of the built-in value providers can return the currently selected class in Visual
Studio or a certain project by its name, but there's also a pretty powerful &lt;code&gt;ExpressionEvaluatorValueProvider&lt;/code&gt; that
can return values based on other arguments through expressions.
&lt;/p&gt;
&lt;p&gt;
In this case, we simply want to build up the name of the runtime project by appending
the two fields the user has entered:
&lt;/p&gt;
&lt;pre&gt;&amp;lt;Argument Name="RuntimeProjectName"&amp;gt;
  &amp;lt;ValueProvider Type="Evaluator" Expression="$(ApplicationBlockNamespace).$(ApplicationBlockName)"&amp;gt;
    &amp;lt;MonitorArgument Name="ApplicationBlockNamespace" /&amp;gt;
    &amp;lt;MonitorArgument Name="ApplicationBlockName" /&amp;gt;
  &amp;lt;/ValueProvider&amp;gt;
&amp;lt;/Argument&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
By also supplying the &lt;code&gt;MonitorArgument&lt;/code&gt; values, the expression will automatically
be re-evaluated if one of the monitored arguments has changed. We can now base the
design-time project's name on this argument by appending a suffix to the project name
as follows:
&lt;/p&gt;
&lt;pre&gt;&amp;lt;Argument Name="DesignTimeProjectSuffix"&amp;gt;
  &amp;lt;ValueProvider Type="Evaluator" Expression="Configuration.Design" /&amp;gt;
&amp;lt;/Argument&amp;gt;
&amp;lt;Argument Name="DesignTimeProjectName"&amp;gt;
  &amp;lt;ValueProvider Type="Evaluator" Expression="$(RuntimeProjectName).$(DesignTimeProjectSuffix)"&amp;gt;
    &amp;lt;MonitorArgument Name="RuntimeProjectName" /&amp;gt;
    &amp;lt;MonitorArgument Name="DesignTimeProjectSuffix" /&amp;gt;
  &amp;lt;/ValueProvider&amp;gt;
&amp;lt;/Argument&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
&lt;em&gt;Side note: it would also have been possible to use the expression "$(RuntimeProjectName).$(DesignTimeProjectSuffix).Configuration.Design"
directly (without creating the DesignTimeProjectSuffix argument) but unfortunately
there's &lt;a href="http://weblogs.asp.net/cazzu/archive/2006/06/09/RegexBug.aspx"&gt;a
problem with the expression parser that makes the wizard very slow&lt;/a&gt; in this case.&lt;/em&gt;
&lt;/p&gt;
&lt;p&gt;
Now that we have the names for both projects stored in arguments, we can also replace
the project names in the vstemplate files that were still made up of the application
block's name and namespace individually. E.g., we can replace &lt;code&gt;ProjectName="$ApplicationBlockNamespace$.$ApplicationBlockName$"&lt;/code&gt; by &lt;code&gt;ProjectName="$RuntimeProjectName$"&lt;/code&gt;.
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;Where Are We&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;
At this point, we've seen how to generate a second project with a project reference
by using actions that are chained together. We've also seen how to use value providers
that put values into arguments and how to use these values in actions. Next time,
we'll see how to build our own value providers and have them interact with other arguments.
Furthermore, we'll add binary references to some of the Enterprise Library assemblies
and add post-build events to the projects. Stay tuned!
&lt;/p&gt;
&lt;p&gt;
&lt;p&gt;
&lt;a href="http://jelle.druyts.net/content/binary/GAS%20-%2003%20-%20Add%20a%20project%20with%20a%20project%20reference.zip"&gt;Download
the source code for the current state of the Guidance Package.&lt;/a&gt;
&lt;/p&gt;
&lt;img width="0" height="0" src="http://jelle.druyts.net/aggbug.ashx?id=19c5d307-3a4a-4b02-95b7-c68ef0021006" /&gt;</description>
      <comments>http://jelle.druyts.net/CommentView.aspx?guid=19c5d307-3a4a-4b02-95b7-c68ef0021006</comments>
      <category>Blog</category>
      <category>Blog/Programming</category>
      <category>Blog/Programming/.NET</category>
      <category>Blog/Programming/.NET/GuidanceAutomation</category>
      <category>Blog/Programming/.NET/VS.NET</category>
    </item>
    <item>
      <trackback:ping>http://jelle.druyts.net/Trackback.aspx?guid=10f2c5b6-717e-4504-816f-3ea8a630be94</trackback:ping>
      <pingback:server>http://jelle.druyts.net/pingback.aspx</pingback:server>
      <pingback:target>http://jelle.druyts.net/PermaLink.aspx?guid=10f2c5b6-717e-4504-816f-3ea8a630be94</pingback:target>
      <dc:creator>Jelle Druyts</dc:creator>
      <wfw:comment>http://jelle.druyts.net/CommentView.aspx?guid=10f2c5b6-717e-4504-816f-3ea8a630be94</wfw:comment>
      <wfw:commentRss>http://jelle.druyts.net/SyndicationService.asmx/GetEntryCommentsRss?guid=10f2c5b6-717e-4504-816f-3ea8a630be94</wfw:commentRss>
      <slash:comments>8</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
[This is episode 3 of the <a href="http://jelle.druyts.net/CategoryView.aspx?category=Blog%7CProgramming%7C.NET%7CGuidanceAutomation">Guidance
Automation Series</a>]
</p>
        <p>
Last time, we left off with an empty guidance package that we will now be using to
build the Enterprise Library guidance package. To scope it down a bit, we'll only
be supporting C# in this example - but of course the same principles apply to other
languages. As a first step, we'll set up the package so that it will generate two
projects: the runtime project that contains the logic for the application block, and
the design-time project that contains the user interface logic for the configuration
tool.
</p>
        <p>
          <strong>Templates And Recipes</strong>
        </p>
        <p>
Before diving in, you need to know that a guidance package is basically a combination
of Visual Studio Templates and Guidance Automation Recipes.
</p>
        <ul>
          <li>
Visual Studio Templates are the standard way to define types of solutions, projects
and classes that the developer can create in Visual Studio (e.g. they show up in the
Create New Project/Item dialog boxes). Visual Studio Templates are defined as .vstemplate
files in the Templates directory of the project, in a subdirectory that defines the
type of template (e.g. Solution or Projects). 
</li>
          <li>
Recipes are automated activities that follow a series of instructions to execute some
action that a developer would otherwise need to do manually (e.g. creating a number
of projects in Visual Studio and adding references to them). Recipes are defined in
the main xml file (i.e. not the TypeAlias.xml file but the xml file named after your
guidance package), which means configuring this part will require some xml tinkering
- although fortunately there is pretty good IntelliSense support in Visual Studio.</li>
        </ul>
        <p>
The two are linked by adding a recipe reference to the vstemplate file that will make
sure the recipe is executed at the time the solution is "unfolded" (which is just
a fancy term to say "created" in Visual Studio).
</p>
        <p>
          <strong>The Solution Template</strong>
        </p>
        <p>
Armed with this knowledge, we can now create a basic vstemplate file in the Templates\Solutions
directory that contains the template for the Application Block solution:
</p>
        <pre>&lt;VSTemplate Version="2.0" Type="ProjectGroup" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005"&gt;
  &lt;TemplateData&gt;
    &lt;Name&gt;Application Block&lt;/Name&gt;
    &lt;Description&gt;Guidance Package that creates a new Enterprise Library Application Block.&lt;/Description&gt;
    &lt;ProjectType&gt;CSharp&lt;/ProjectType&gt;
    &lt;SortOrder&gt;91&lt;/SortOrder&gt;
    &lt;Icon&gt;ApplicationBlock.ico&lt;/Icon&gt;
    &lt;CreateNewFolder&gt;false&lt;/CreateNewFolder&gt;
    &lt;DefaultName&gt;ApplicationBlock&lt;/DefaultName&gt;
    &lt;ProvideDefaultName&gt;true&lt;/ProvideDefaultName&gt;
  &lt;/TemplateData&gt;
  &lt;TemplateContent&gt;
    &lt;ProjectCollection&gt;
      &lt;ProjectTemplateLink ProjectName="$ApplicationBlockNamespace$.$ApplicationBlockName$"&gt;Projects\Runtime\Runtime.vstemplate&lt;/ProjectTemplateLink&gt;
    &lt;/ProjectCollection&gt;
  &lt;/TemplateContent&gt;
  &lt;WizardExtension&gt;
    &lt;Assembly&gt;Microsoft.Practices.RecipeFramework.VisualStudio, Version=1.0.51206.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a&lt;/Assembly&gt;
    &lt;FullClassName&gt;Microsoft.Practices.RecipeFramework.VisualStudio.Templates.UnfoldTemplate&lt;/FullClassName&gt;
  &lt;/WizardExtension&gt;
  &lt;WizardData&gt;
    &lt;Template xmlns="http://schemas.microsoft.com/pag/gax-template" SchemaVersion="1.0" Recipe="CreateApplicationBlock"&gt;
    &lt;/Template&gt;
  &lt;/WizardData&gt;
&lt;/VSTemplate&gt;</pre>
        <p>
There are a few interesting things to note here:
</p>
        <ul>
          <li>
The <code>TemplateData</code> section covers the metadata for the solution: its icon,
name and description as they appear in the New Project dialog box, the project type,
and some other properties. 
</li>
          <li>
The <code>TemplateContent</code> section defines the structure of the solution; in
this case we define a project collection (we'll be adding another project later on)
that contains one project template for the runtime part of the application block.
When the solution is created, the template defined in the Runtime.vstemplate file
will also be "unfolded". 
</li>
          <li>
The project's name is defined as <code>"$ApplicationBlockNamespace$.$ApplicationBlockName$"</code>;
this is the first appearance of parameters (enclosed in $-signs). These values will
be replaced at runtime by arguments from the Guidance Recipe, which we will define
later on, to form the project name in Visual Studio. 
</li>
          <li>
The <code>Template</code> node in the <code>WizardData</code> section establishes
the link between the vstemplate and the corresponding "CreateApplicationBlock" recipe
that needs to be defined in the main xml file.</li>
        </ul>
        <p>
          <em>
            <strong>Guidance Automation Tip #2:</strong> All Visual Studio template files
(vstemplates and their contents such as project files, code files and images) must
have their "Build Action" set to "Content" and "Copy to Output Directory" to "Copy
Always". The Guidance Automation Toolkit adds an item in the Templates directory's
context menu that will do this for you.</em>
        </p>
        <p>
          <strong>The Project Template</strong>
        </p>
        <p>
At this point, we have a template for the solution that contains a link to a project
template and to a recipe. The Runtime.vstemplate project template looks like this:
</p>
        <pre>&lt;VSTemplate Version="2.0.0" Type="Project" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005"&gt;
  &lt;TemplateData&gt;
    &lt;Name&gt;Application Block Runtime Project&lt;/Name&gt;
    &lt;Description&gt;A project template for the runtime part of an Application Block.&lt;/Description&gt;
    &lt;Icon Package="{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}" ID="4547" /&gt;
    &lt;ProjectType&gt;CSharp&lt;/ProjectType&gt;
    &lt;SortOrder&gt;20&lt;/SortOrder&gt;
    &lt;CreateNewFolder&gt;true&lt;/CreateNewFolder&gt;
    &lt;DefaultName&gt;Runtime&lt;/DefaultName&gt;
    &lt;ProvideDefaultName&gt;true&lt;/ProvideDefaultName&gt;
  &lt;/TemplateData&gt;
  &lt;TemplateContent&gt;
    &lt;Project File="Runtime.csproj" TargetFileName="$ApplicationBlockNamespace$.$ApplicationBlockName$.csproj" ReplaceParameters="true"&gt;
      &lt;ProjectItem ReplaceParameters="true"&gt;Properties\AssemblyInfo.cs&lt;/ProjectItem&gt;
    &lt;/Project&gt;
  &lt;/TemplateContent&gt;
  &lt;WizardExtension&gt;
    &lt;Assembly&gt;Microsoft.Practices.RecipeFramework.VisualStudio, Version=1.0.51206.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a&lt;/Assembly&gt;
    &lt;FullClassName&gt;Microsoft.Practices.RecipeFramework.VisualStudio.Templates.UnfoldTemplate&lt;/FullClassName&gt;
  &lt;/WizardExtension&gt;
  &lt;WizardData&gt;
    &lt;Template xmlns="http://schemas.microsoft.com/pag/gax-template" SchemaVersion="1.0"&gt;
    &lt;/Template&gt;
  &lt;/WizardData&gt;
&lt;/VSTemplate&gt;</pre>
        <p>
Some highlights from this file:
</p>
        <ul>
          <li>
The icon from the package {FAE04EC1-301F-11d3-BF4B-00C04F79EFBC} is the ID of an icon
in the C# Project dll, typically located at C:\Program Files\Microsoft Visual Studio
8\VC#\VCSPackages\csproj.dll. This dll can be opened in Visual Studio so you can pick
another icon from this dll if you want, but you can also use a custom icon like we
did in the solution's template above. 
</li>
          <li>
The <code>TemplateContent</code> section now defines a project that is created from
the Runtime.csproj file. This is a regular C# project file that can again contain
some parameters that will be replaced at runtime (if the <code>ReplaceParameters</code> attribute
is set to <code>true</code>, anyway). 
</li>
          <li>
Within the project, we also include a Project Item (typically a file within the project),
which will in this case be a boilerplate AssemblyInfo.cs file that the project will
already contain. 
</li>
          <li>
There is no link to a recipe in the <code>Template</code> node here, since this project
will only be unfolded by the solution template defined above - which already contains
a reference to a recipe.</li>
        </ul>
        <p>
          <strong>The Project File</strong>
        </p>
        <p>
This is the content of the Runtime.csproj file:
</p>
        <pre>&lt;Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"&gt;
  &lt;PropertyGroup&gt;
    &lt;Configuration Condition=" '$(Configuration)' == '' "&gt;Debug&lt;/Configuration&gt;
    &lt;Platform Condition=" '$(Platform)' == '' "&gt;AnyCPU&lt;/Platform&gt;
    &lt;ProductVersion&gt;8.0.50727&lt;/ProductVersion&gt;
    &lt;SchemaVersion&gt;2.0&lt;/SchemaVersion&gt;
    &lt;ProjectGuid&gt;$guid1$&lt;/ProjectGuid&gt;
    &lt;OutputType&gt;Library&lt;/OutputType&gt;
    &lt;AppDesignerFolder&gt;Properties&lt;/AppDesignerFolder&gt;
    &lt;RootNamespace&gt;$safeprojectname$&lt;/RootNamespace&gt;
    &lt;AssemblyName&gt;$safeprojectname$&lt;/AssemblyName&gt;
  &lt;/PropertyGroup&gt;
  &lt;PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "&gt;
    &lt;DebugSymbols&gt;true&lt;/DebugSymbols&gt;
    &lt;DebugType&gt;full&lt;/DebugType&gt;
    &lt;Optimize&gt;false&lt;/Optimize&gt;
    &lt;OutputPath&gt;bin\Debug\&lt;/OutputPath&gt;
    &lt;DefineConstants&gt;DEBUG;TRACE&lt;/DefineConstants&gt;
    &lt;ErrorReport&gt;prompt&lt;/ErrorReport&gt;
    &lt;WarningLevel&gt;4&lt;/WarningLevel&gt;
    &lt;TreatWarningsAsErrors&gt;true&lt;/TreatWarningsAsErrors&gt;
    &lt;DocumentationFile&gt;bin\Debug\$safeprojectname$.xml&lt;/DocumentationFile&gt;
  &lt;/PropertyGroup&gt;
  &lt;PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "&gt;
    &lt;DebugType&gt;pdbonly&lt;/DebugType&gt;
    &lt;Optimize&gt;true&lt;/Optimize&gt;
    &lt;OutputPath&gt;bin\Release\&lt;/OutputPath&gt;
    &lt;DefineConstants&gt;TRACE&lt;/DefineConstants&gt;
    &lt;ErrorReport&gt;prompt&lt;/ErrorReport&gt;
    &lt;WarningLevel&gt;4&lt;/WarningLevel&gt;
    &lt;TreatWarningsAsErrors&gt;true&lt;/TreatWarningsAsErrors&gt;
    &lt;DocumentationFile&gt;bin\Release\$safeprojectname$.xml&lt;/DocumentationFile&gt;
  &lt;/PropertyGroup&gt;
  &lt;ItemGroup&gt;
    &lt;Reference Include="System" /&gt;
    &lt;Reference Include="System.Data" /&gt;
    &lt;Reference Include="System.Xml" /&gt;
  &lt;/ItemGroup&gt;
  &lt;ItemGroup&gt;
    &lt;Compile Include="Properties\AssemblyInfo.cs" /&gt;
  &lt;/ItemGroup&gt;
  &lt;Import Project="$(MSBuildBinPath)\Microsoft.CSHARP.Targets" /&gt;
&lt;/Project&gt;</pre>
        <p>
Again, some interesting points to look at here:
</p>
        <ul>
          <li>
There is no magic here, this is just an MSBuild project file for C# that you can tweak
as much as you want. 
</li>
          <li>
Not only can we use the <code>$ApplicationBlockName$</code> and other parameters we
defined in the recipe, but we also have some standard parameters that can be used
(see <a href="http://msdn2.microsoft.com/en-us/library/eehb4faa.aspx">Template Parameters
on MSDN</a> for more information). The <code>$guid1$</code> parameter will be replaced
by a unique GUID (you can define up to 10 different GUIDs like this), the <code>$safeprojectname$</code> will
be replaced by the name provided by the user in the New Project dialog box, with all
unsafe characters and spaces removed. 
</li>
          <li>
Both for the Debug and Release configurations, I've set <code>TreatWarningsAsErrors</code> to <code>true</code> (to
enforce the best practice that warnings should be taken seriously) and I've enabled
the output of an XML documentation file. The combination of these two settings also
enforces that all public members must have XML comments, another best practice I always
try to enforce. 
</li>
          <li>
The <code>ItemGroup</code> section defines that the AssemblyInfo.cs file we included
in the project template is part of the project and needs to be compiled.</li>
        </ul>
        <p>
          <strong>The Recipe</strong>
        </p>
        <p>
Now that we have all the templates in place, we still need to define the recipe that
will enable the user to automate the creation of the solution and its projects. In
its simplest form, you can think of a recipe as a wizard that collects user data -
but as we will see later on in this series, it can do a lot more. We've currently
defined two custom parameters in the template files: <code>$ApplicationBlockName$</code> and <code>$ApplicationBlockNamespace$</code>.
This means we need to collect this information from the user when the solution is
being created. The following recipe definition does just that:
</p>
        <pre>&lt;?xml version="1.0" encoding="utf-8" ?&gt;
&lt;GuidancePackage xmlns="http://schemas.microsoft.com/pag/gax-core"
    Name="JelleDruyts.EnterpriseLibraryGuidance" 
    Caption="Enterprise Library Guidance"
    Description="Provides guidance around the creation of Enterprise Library Application Blocks"
    Guid="2cac5b9c-a04f-4a49-8a56-3ee5d63bd83f" 
    SchemaVersion="1.0"&gt;
  &lt;Recipes&gt;
    &lt;Recipe Name="CreateApplicationBlock"&gt;
      &lt;Caption&gt;Create a new Enterprise Library Application Block&lt;/Caption&gt;
      &lt;Arguments&gt;
        &lt;Argument Name="ApplicationBlockName" Required="true"&gt;
          &lt;Converter Type="Microsoft.Practices.RecipeFramework.Library.Converters.CodeIdentifierStringConverter, Microsoft.Practices.RecipeFramework.Library" /&gt;
        &lt;/Argument&gt;
        &lt;Argument Name="ApplicationBlockNamespace" Required="true"&gt;
          &lt;Converter Type="Microsoft.Practices.RecipeFramework.Library.Converters.NamespaceStringConverter, Microsoft.Practices.RecipeFramework.Library" /&gt;
        &lt;/Argument&gt;
      &lt;/Arguments&gt;
      &lt;GatheringServiceData&gt;
        &lt;Wizard xmlns="http://schemas.microsoft.com/pag/gax-wizards" SchemaVersion="1.0"&gt;
          &lt;Pages&gt;
            &lt;Page&gt;
              &lt;Title&gt;Application Block Information&lt;/Title&gt;
              &lt;LinkTitle&gt;Application Block&lt;/LinkTitle&gt;
              &lt;Help&gt;
                Enter the Application Block name and namespace.
              &lt;/Help&gt;
              &lt;Fields&gt;
                &lt;Field ValueName="ApplicationBlockName" Label="Application Block Name" InvalidValueMessage="Must be a valid .NET identifier (e.g. it shouldn't contain spaces or special characters)." /&gt;
                &lt;Field ValueName="ApplicationBlockNamespace" Label="Application Block Namespace" InvalidValueMessage="Must be a valid .NET namespace identifier (e.g. it shouldn't contain spaces or special characters)." /&gt;
              &lt;/Fields&gt;
            &lt;/Page&gt;
          &lt;/Pages&gt;
        &lt;/Wizard&gt;
      &lt;/GatheringServiceData&gt;
    &lt;/Recipe&gt;
  &lt;/Recipes&gt;
&lt;/GuidancePackage&gt;</pre>
        <p>
Some remarks on this file:
</p>
        <ul>
          <li>
Recall that the "CreateApplicationBlock" recipe that is defined here is referenced
by the solution's template, so this is the recipe that will be used to gather the
data from the user. 
</li>
          <li>
The <code>Arguments</code> section defines all the recipe arguments that are defined,
and can be replaced in the template using parameters. In this case, we have two arguments, <code>ApplicationBlockName</code> and <code>ApplicationBlockNamespace</code>,
which are both required. They also have converters associated with them that can validate
the arguments (e.g. to make sure they are valid .NET identifiers or namespaces). If
the argument is invalid, it will immediately become visible to the user. Likewise,
a required argument will have a light yellow background in the wizard, so it's immediately
clear which arguments need to be filled in and which can be skipped. 
</li>
          <li>
The <code>GatheringServiceData</code> section contains a wizard with one page and
two fields, one for each argument we want to collect.</li>
        </ul>
        <p>
          <strong>Running The Guidance Package</strong>
        </p>
        <p>
Now that we have all the parts covered, we can finally register and test our package.
It will show up as the "Application Block" project type in Visual Studio (with the
proper icon and description):
</p>
        <p>
          <img src="http://jelle.druyts.net/content/binary/GAS%20-%20NewProjectApplicationBlock.png" border="0" />
        </p>
        <p>
When we create the solution, a wizard will pop up that allows us to enter the information
required to generate the projects. Notice that the fields have a light yellow background
and that we get an error icon and a tooltip if some argument is invalid:
</p>
        <p>
          <img src="http://jelle.druyts.net/content/binary/GAS%20-%20ApplicationBlockWizardBasic.png" border="0" />
        </p>
        <p>
When we correct the information and press Finish, our first skeleton project for the
Enterprise Library Application Block has been created:
</p>
        <p>
          <img src="http://jelle.druyts.net/content/binary/GAS%20-%20ApplicationBlockEmptyProject.png" border="0" />
        </p>
        <p>
Note that the project was correctly named using the combination of the namespace (JelleDruyts.EnterpriseLibrary)
and name (ServiceAgents) that was entered in the wizard.
</p>
        <p>
          <strong>Where Are We</strong>
        </p>
        <p>
At this point, we've seen how to define Visual Studio Templates that accept parameters
and create solution files, project files and project items. We can already impose
some guidance and best practices by tweaking the projects (e.g. to treat warnings
as errors). We've also seen how to define a recipe that gathers information from the
user to pass along to the templates.
</p>
        <p>
You might say, "well, that's an awful lot of work to create a blank project", and
you would be absolutely right. But of course, this is just the beginning. And now
that we have the basics covered, the fun can really start! Stay tuned for the next
episode, in which we'll take this guidance package a step further.
</p>
        <p>
          <a href="http://jelle.druyts.net/content/binary/GAS%20-%2002%20-%20Create%20a%20project.zip">Download
the source code for the current state of the Guidance Package.</a>
        </p>
        <img width="0" height="0" src="http://jelle.druyts.net/aggbug.ashx?id=10f2c5b6-717e-4504-816f-3ea8a630be94" />
      </body>
      <title>GAS03: Generating A C# Project</title>
      <guid isPermaLink="false">http://jelle.druyts.net/PermaLink.aspx?guid=10f2c5b6-717e-4504-816f-3ea8a630be94</guid>
      <link>http://jelle.druyts.net/2006/06/27/GAS03GeneratingACProject.aspx</link>
      <pubDate>Tue, 27 Jun 2006 20:35:19 GMT</pubDate>
      <description>&lt;p&gt;
[This is episode 3 of the &lt;a href="http://jelle.druyts.net/CategoryView.aspx?category=Blog%7CProgramming%7C.NET%7CGuidanceAutomation"&gt;Guidance
Automation Series&lt;/a&gt;]
&lt;/p&gt;
&lt;p&gt;
Last time, we left off with an empty guidance package that we will now be using to
build the Enterprise Library guidance package. To scope it down a bit, we'll only
be supporting C# in this example - but of course the same principles apply to other
languages. As a first step, we'll set up the package so that it will generate two
projects: the runtime project that contains the logic for the application block, and
the design-time project that contains the user interface logic for the configuration
tool.
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;Templates And Recipes&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;
Before diving in, you need to know that a guidance package is basically a combination
of Visual Studio Templates and Guidance Automation Recipes.
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
Visual Studio Templates are the standard way to define types of solutions, projects
and classes that the developer can create in Visual Studio (e.g. they show up in the
Create New Project/Item dialog boxes). Visual Studio Templates are defined as .vstemplate
files in the Templates directory of the project, in a subdirectory that defines the
type of template (e.g. Solution or Projects). 
&lt;li&gt;
Recipes are automated activities that follow a series of instructions to execute some
action that a developer would otherwise need to do manually (e.g. creating a number
of projects in Visual Studio and adding references to them). Recipes are defined in
the main xml file (i.e. not the TypeAlias.xml file but the xml file named after your
guidance package), which means configuring this part will require some xml tinkering
- although fortunately there is pretty good IntelliSense support in Visual Studio.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
The two are linked by adding a recipe reference to the vstemplate file that will make
sure the recipe is executed at the time the solution is "unfolded" (which is just
a fancy term to say "created" in Visual Studio).
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;The Solution Template&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;
Armed with this knowledge, we can now create a basic vstemplate file in the Templates\Solutions
directory that contains the template for the Application Block solution:
&lt;/p&gt;
&lt;pre&gt;&amp;lt;VSTemplate Version="2.0" Type="ProjectGroup" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005"&amp;gt;
  &amp;lt;TemplateData&amp;gt;
    &amp;lt;Name&amp;gt;Application Block&amp;lt;/Name&amp;gt;
    &amp;lt;Description&amp;gt;Guidance Package that creates a new Enterprise Library Application Block.&amp;lt;/Description&amp;gt;
    &amp;lt;ProjectType&amp;gt;CSharp&amp;lt;/ProjectType&amp;gt;
    &amp;lt;SortOrder&amp;gt;91&amp;lt;/SortOrder&amp;gt;
    &amp;lt;Icon&amp;gt;ApplicationBlock.ico&amp;lt;/Icon&amp;gt;
    &amp;lt;CreateNewFolder&amp;gt;false&amp;lt;/CreateNewFolder&amp;gt;
    &amp;lt;DefaultName&amp;gt;ApplicationBlock&amp;lt;/DefaultName&amp;gt;
    &amp;lt;ProvideDefaultName&amp;gt;true&amp;lt;/ProvideDefaultName&amp;gt;
  &amp;lt;/TemplateData&amp;gt;
  &amp;lt;TemplateContent&amp;gt;
    &amp;lt;ProjectCollection&amp;gt;
      &amp;lt;ProjectTemplateLink ProjectName="$ApplicationBlockNamespace$.$ApplicationBlockName$"&amp;gt;Projects\Runtime\Runtime.vstemplate&amp;lt;/ProjectTemplateLink&amp;gt;
    &amp;lt;/ProjectCollection&amp;gt;
  &amp;lt;/TemplateContent&amp;gt;
  &amp;lt;WizardExtension&amp;gt;
    &amp;lt;Assembly&amp;gt;Microsoft.Practices.RecipeFramework.VisualStudio, Version=1.0.51206.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a&amp;lt;/Assembly&amp;gt;
    &amp;lt;FullClassName&amp;gt;Microsoft.Practices.RecipeFramework.VisualStudio.Templates.UnfoldTemplate&amp;lt;/FullClassName&amp;gt;
  &amp;lt;/WizardExtension&amp;gt;
  &amp;lt;WizardData&amp;gt;
    &amp;lt;Template xmlns="http://schemas.microsoft.com/pag/gax-template" SchemaVersion="1.0" Recipe="CreateApplicationBlock"&amp;gt;
    &amp;lt;/Template&amp;gt;
  &amp;lt;/WizardData&amp;gt;
&amp;lt;/VSTemplate&amp;gt;&lt;/pre&gt;
&lt;p&gt;
There are a few interesting things to note here:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
The &lt;code&gt;TemplateData&lt;/code&gt; section covers the metadata for the solution: its icon,
name and description as they appear in the New Project dialog box, the project type,
and some other properties. 
&lt;li&gt;
The &lt;code&gt;TemplateContent&lt;/code&gt; section defines the structure of the solution; in
this case we define a project collection (we'll be adding another project later on)
that contains one project template for the runtime part of the application block.
When the solution is created, the template defined in the Runtime.vstemplate file
will also be "unfolded". 
&lt;li&gt;
The project's name is defined as &lt;code&gt;"$ApplicationBlockNamespace$.$ApplicationBlockName$"&lt;/code&gt;;
this is the first appearance of parameters (enclosed in $-signs). These values will
be replaced at runtime by arguments from the Guidance Recipe, which we will define
later on, to form the project name in Visual Studio. 
&lt;li&gt;
The &lt;code&gt;Template&lt;/code&gt; node in the &lt;code&gt;WizardData&lt;/code&gt; section establishes
the link between the vstemplate and the corresponding "CreateApplicationBlock" recipe
that needs to be defined in the main xml file.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
&lt;em&gt;&lt;strong&gt;Guidance Automation Tip #2:&lt;/strong&gt; All Visual Studio template files
(vstemplates and their contents such as project files, code files and images) must
have their "Build Action" set to "Content" and "Copy to Output Directory" to "Copy
Always". The Guidance Automation Toolkit adds an item in the Templates directory's
context menu that will do this for you.&lt;/em&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;The Project Template&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;
At this point, we have a template for the solution that contains a link to a project
template and to a recipe. The Runtime.vstemplate project template looks like this:
&lt;/p&gt;
&lt;pre&gt;&amp;lt;VSTemplate Version="2.0.0" Type="Project" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005"&amp;gt;
  &amp;lt;TemplateData&amp;gt;
    &amp;lt;Name&amp;gt;Application Block Runtime Project&amp;lt;/Name&amp;gt;
    &amp;lt;Description&amp;gt;A project template for the runtime part of an Application Block.&amp;lt;/Description&amp;gt;
    &amp;lt;Icon Package="{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}" ID="4547" /&amp;gt;
    &amp;lt;ProjectType&amp;gt;CSharp&amp;lt;/ProjectType&amp;gt;
    &amp;lt;SortOrder&amp;gt;20&amp;lt;/SortOrder&amp;gt;
    &amp;lt;CreateNewFolder&amp;gt;true&amp;lt;/CreateNewFolder&amp;gt;
    &amp;lt;DefaultName&amp;gt;Runtime&amp;lt;/DefaultName&amp;gt;
    &amp;lt;ProvideDefaultName&amp;gt;true&amp;lt;/ProvideDefaultName&amp;gt;
  &amp;lt;/TemplateData&amp;gt;
  &amp;lt;TemplateContent&amp;gt;
    &amp;lt;Project File="Runtime.csproj" TargetFileName="$ApplicationBlockNamespace$.$ApplicationBlockName$.csproj" ReplaceParameters="true"&amp;gt;
      &amp;lt;ProjectItem ReplaceParameters="true"&amp;gt;Properties\AssemblyInfo.cs&amp;lt;/ProjectItem&amp;gt;
    &amp;lt;/Project&amp;gt;
  &amp;lt;/TemplateContent&amp;gt;
  &amp;lt;WizardExtension&amp;gt;
    &amp;lt;Assembly&amp;gt;Microsoft.Practices.RecipeFramework.VisualStudio, Version=1.0.51206.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a&amp;lt;/Assembly&amp;gt;
    &amp;lt;FullClassName&amp;gt;Microsoft.Practices.RecipeFramework.VisualStudio.Templates.UnfoldTemplate&amp;lt;/FullClassName&amp;gt;
  &amp;lt;/WizardExtension&amp;gt;
  &amp;lt;WizardData&amp;gt;
    &amp;lt;Template xmlns="http://schemas.microsoft.com/pag/gax-template" SchemaVersion="1.0"&amp;gt;
    &amp;lt;/Template&amp;gt;
  &amp;lt;/WizardData&amp;gt;
&amp;lt;/VSTemplate&amp;gt;&lt;/pre&gt;
&lt;p&gt;
Some highlights from this file:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
The icon from the package {FAE04EC1-301F-11d3-BF4B-00C04F79EFBC} is the ID of an icon
in the C# Project dll, typically located at C:\Program Files\Microsoft Visual Studio
8\VC#\VCSPackages\csproj.dll. This dll can be opened in Visual Studio so you can pick
another icon from this dll if you want, but you can also use a custom icon like we
did in the solution's template above. 
&lt;li&gt;
The &lt;code&gt;TemplateContent&lt;/code&gt; section now defines a project that is created from
the Runtime.csproj file. This is a regular C# project file that can again contain
some parameters that will be replaced at runtime (if the &lt;code&gt;ReplaceParameters&lt;/code&gt; attribute
is set to &lt;code&gt;true&lt;/code&gt;, anyway). 
&lt;li&gt;
Within the project, we also include a Project Item (typically a file within the project),
which will in this case be a boilerplate AssemblyInfo.cs file that the project will
already contain. 
&lt;li&gt;
There is no link to a recipe in the &lt;code&gt;Template&lt;/code&gt; node here, since this project
will only be unfolded by the solution template defined above - which already contains
a reference to a recipe.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
&lt;strong&gt;The Project File&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;
This is the content of the Runtime.csproj file:
&lt;/p&gt;
&lt;pre&gt;&amp;lt;Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"&amp;gt;
  &amp;lt;PropertyGroup&amp;gt;
    &amp;lt;Configuration Condition=" '$(Configuration)' == '' "&amp;gt;Debug&amp;lt;/Configuration&amp;gt;
    &amp;lt;Platform Condition=" '$(Platform)' == '' "&amp;gt;AnyCPU&amp;lt;/Platform&amp;gt;
    &amp;lt;ProductVersion&amp;gt;8.0.50727&amp;lt;/ProductVersion&amp;gt;
    &amp;lt;SchemaVersion&amp;gt;2.0&amp;lt;/SchemaVersion&amp;gt;
    &amp;lt;ProjectGuid&amp;gt;$guid1$&amp;lt;/ProjectGuid&amp;gt;
    &amp;lt;OutputType&amp;gt;Library&amp;lt;/OutputType&amp;gt;
    &amp;lt;AppDesignerFolder&amp;gt;Properties&amp;lt;/AppDesignerFolder&amp;gt;
    &amp;lt;RootNamespace&amp;gt;$safeprojectname$&amp;lt;/RootNamespace&amp;gt;
    &amp;lt;AssemblyName&amp;gt;$safeprojectname$&amp;lt;/AssemblyName&amp;gt;
  &amp;lt;/PropertyGroup&amp;gt;
  &amp;lt;PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "&amp;gt;
    &amp;lt;DebugSymbols&amp;gt;true&amp;lt;/DebugSymbols&amp;gt;
    &amp;lt;DebugType&amp;gt;full&amp;lt;/DebugType&amp;gt;
    &amp;lt;Optimize&amp;gt;false&amp;lt;/Optimize&amp;gt;
    &amp;lt;OutputPath&amp;gt;bin\Debug\&amp;lt;/OutputPath&amp;gt;
    &amp;lt;DefineConstants&amp;gt;DEBUG;TRACE&amp;lt;/DefineConstants&amp;gt;
    &amp;lt;ErrorReport&amp;gt;prompt&amp;lt;/ErrorReport&amp;gt;
    &amp;lt;WarningLevel&amp;gt;4&amp;lt;/WarningLevel&amp;gt;
    &amp;lt;TreatWarningsAsErrors&amp;gt;true&amp;lt;/TreatWarningsAsErrors&amp;gt;
    &amp;lt;DocumentationFile&amp;gt;bin\Debug\$safeprojectname$.xml&amp;lt;/DocumentationFile&amp;gt;
  &amp;lt;/PropertyGroup&amp;gt;
  &amp;lt;PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "&amp;gt;
    &amp;lt;DebugType&amp;gt;pdbonly&amp;lt;/DebugType&amp;gt;
    &amp;lt;Optimize&amp;gt;true&amp;lt;/Optimize&amp;gt;
    &amp;lt;OutputPath&amp;gt;bin\Release\&amp;lt;/OutputPath&amp;gt;
    &amp;lt;DefineConstants&amp;gt;TRACE&amp;lt;/DefineConstants&amp;gt;
    &amp;lt;ErrorReport&amp;gt;prompt&amp;lt;/ErrorReport&amp;gt;
    &amp;lt;WarningLevel&amp;gt;4&amp;lt;/WarningLevel&amp;gt;
    &amp;lt;TreatWarningsAsErrors&amp;gt;true&amp;lt;/TreatWarningsAsErrors&amp;gt;
    &amp;lt;DocumentationFile&amp;gt;bin\Release\$safeprojectname$.xml&amp;lt;/DocumentationFile&amp;gt;
  &amp;lt;/PropertyGroup&amp;gt;
  &amp;lt;ItemGroup&amp;gt;
    &amp;lt;Reference Include="System" /&amp;gt;
    &amp;lt;Reference Include="System.Data" /&amp;gt;
    &amp;lt;Reference Include="System.Xml" /&amp;gt;
  &amp;lt;/ItemGroup&amp;gt;
  &amp;lt;ItemGroup&amp;gt;
    &amp;lt;Compile Include="Properties\AssemblyInfo.cs" /&amp;gt;
  &amp;lt;/ItemGroup&amp;gt;
  &amp;lt;Import Project="$(MSBuildBinPath)\Microsoft.CSHARP.Targets" /&amp;gt;
&amp;lt;/Project&amp;gt;&lt;/pre&gt;
&lt;p&gt;
Again, some interesting points to look at here:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
There is no magic here, this is just an MSBuild project file for C# that you can tweak
as much as you want. 
&lt;li&gt;
Not only can we use the &lt;code&gt;$ApplicationBlockName$&lt;/code&gt; and other parameters we
defined in the recipe, but we also have some standard parameters that can be used
(see &lt;a href="http://msdn2.microsoft.com/en-us/library/eehb4faa.aspx"&gt;Template Parameters
on MSDN&lt;/a&gt; for more information). The &lt;code&gt;$guid1$&lt;/code&gt; parameter will be replaced
by a unique GUID (you can define up to 10 different GUIDs like this), the &lt;code&gt;$safeprojectname$&lt;/code&gt; will
be replaced by the name provided by the user in the New Project dialog box, with all
unsafe characters and spaces removed. 
&lt;li&gt;
Both for the Debug and Release configurations, I've set &lt;code&gt;TreatWarningsAsErrors&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt; (to
enforce the best practice that warnings should be taken seriously) and I've enabled
the output of an XML documentation file. The combination of these two settings also
enforces that all public members must have XML comments, another best practice I always
try to enforce. 
&lt;li&gt;
The &lt;code&gt;ItemGroup&lt;/code&gt; section defines that the AssemblyInfo.cs file we included
in the project template is part of the project and needs to be compiled.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
&lt;strong&gt;The Recipe&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;
Now that we have all the templates in place, we still need to define the recipe that
will enable the user to automate the creation of the solution and its projects. In
its simplest form, you can think of a recipe as a wizard that collects user data -
but as we will see later on in this series, it can do a lot more. We've currently
defined two custom parameters in the template files: &lt;code&gt;$ApplicationBlockName$&lt;/code&gt; and &lt;code&gt;$ApplicationBlockNamespace$&lt;/code&gt;.
This means we need to collect this information from the user when the solution is
being created. The following recipe definition does just that:
&lt;/p&gt;
&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="utf-8" ?&amp;gt;
&amp;lt;GuidancePackage xmlns="http://schemas.microsoft.com/pag/gax-core"
    Name="JelleDruyts.EnterpriseLibraryGuidance" 
    Caption="Enterprise Library Guidance"
    Description="Provides guidance around the creation of Enterprise Library Application Blocks"
    Guid="2cac5b9c-a04f-4a49-8a56-3ee5d63bd83f" 
    SchemaVersion="1.0"&amp;gt;
  &amp;lt;Recipes&amp;gt;
    &amp;lt;Recipe Name="CreateApplicationBlock"&amp;gt;
      &amp;lt;Caption&amp;gt;Create a new Enterprise Library Application Block&amp;lt;/Caption&amp;gt;
      &amp;lt;Arguments&amp;gt;
        &amp;lt;Argument Name="ApplicationBlockName" Required="true"&amp;gt;
          &amp;lt;Converter Type="Microsoft.Practices.RecipeFramework.Library.Converters.CodeIdentifierStringConverter, Microsoft.Practices.RecipeFramework.Library" /&amp;gt;
        &amp;lt;/Argument&amp;gt;
        &amp;lt;Argument Name="ApplicationBlockNamespace" Required="true"&amp;gt;
          &amp;lt;Converter Type="Microsoft.Practices.RecipeFramework.Library.Converters.NamespaceStringConverter, Microsoft.Practices.RecipeFramework.Library" /&amp;gt;
        &amp;lt;/Argument&amp;gt;
      &amp;lt;/Arguments&amp;gt;
      &amp;lt;GatheringServiceData&amp;gt;
        &amp;lt;Wizard xmlns="http://schemas.microsoft.com/pag/gax-wizards" SchemaVersion="1.0"&amp;gt;
          &amp;lt;Pages&amp;gt;
            &amp;lt;Page&amp;gt;
              &amp;lt;Title&amp;gt;Application Block Information&amp;lt;/Title&amp;gt;
              &amp;lt;LinkTitle&amp;gt;Application Block&amp;lt;/LinkTitle&amp;gt;
              &amp;lt;Help&amp;gt;
                Enter the Application Block name and namespace.
              &amp;lt;/Help&amp;gt;
              &amp;lt;Fields&amp;gt;
                &amp;lt;Field ValueName="ApplicationBlockName" Label="Application Block Name" InvalidValueMessage="Must be a valid .NET identifier (e.g. it shouldn't contain spaces or special characters)." /&amp;gt;
                &amp;lt;Field ValueName="ApplicationBlockNamespace" Label="Application Block Namespace" InvalidValueMessage="Must be a valid .NET namespace identifier (e.g. it shouldn't contain spaces or special characters)." /&amp;gt;
              &amp;lt;/Fields&amp;gt;
            &amp;lt;/Page&amp;gt;
          &amp;lt;/Pages&amp;gt;
        &amp;lt;/Wizard&amp;gt;
      &amp;lt;/GatheringServiceData&amp;gt;
    &amp;lt;/Recipe&amp;gt;
  &amp;lt;/Recipes&amp;gt;
&amp;lt;/GuidancePackage&amp;gt;&lt;/pre&gt;
&lt;p&gt;
Some remarks on this file:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
Recall that the "CreateApplicationBlock" recipe that is defined here is referenced
by the solution's template, so this is the recipe that will be used to gather the
data from the user. 
&lt;li&gt;
The &lt;code&gt;Arguments&lt;/code&gt; section defines all the recipe arguments that are defined,
and can be replaced in the template using parameters. In this case, we have two arguments, &lt;code&gt;ApplicationBlockName&lt;/code&gt; and &lt;code&gt;ApplicationBlockNamespace&lt;/code&gt;,
which are both required. They also have converters associated with them that can validate
the arguments (e.g. to make sure they are valid .NET identifiers or namespaces). If
the argument is invalid, it will immediately become visible to the user. Likewise,
a required argument will have a light yellow background in the wizard, so it's immediately
clear which arguments need to be filled in and which can be skipped. 
&lt;li&gt;
The &lt;code&gt;GatheringServiceData&lt;/code&gt; section contains a wizard with one page and
two fields, one for each argument we want to collect.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
&lt;strong&gt;Running The Guidance Package&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;
Now that we have all the parts covered, we can finally register and test our package.
It will show up as the "Application Block" project type in Visual Studio (with the
proper icon and description):
&lt;/p&gt;
&lt;p&gt;
&lt;img src="http://jelle.druyts.net/content/binary/GAS%20-%20NewProjectApplicationBlock.png" border=0&gt;
&lt;/p&gt;
&lt;p&gt;
When we create the solution, a wizard will pop up that allows us to enter the information
required to generate the projects. Notice that the fields have a light yellow background
and that we get an error icon and a tooltip if some argument is invalid:
&lt;/p&gt;
&lt;p&gt;
&lt;img src="http://jelle.druyts.net/content/binary/GAS%20-%20ApplicationBlockWizardBasic.png" border=0&gt;
&lt;/p&gt;
&lt;p&gt;
When we correct the information and press Finish, our first skeleton project for the
Enterprise Library Application Block has been created:
&lt;/p&gt;
&lt;p&gt;
&lt;img src="http://jelle.druyts.net/content/binary/GAS%20-%20ApplicationBlockEmptyProject.png" border=0&gt;
&lt;/p&gt;
&lt;p&gt;
Note that the project was correctly named using the combination of the namespace (JelleDruyts.EnterpriseLibrary)
and name (ServiceAgents) that was entered in the wizard.
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;Where Are We&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;
At this point, we've seen how to define Visual Studio Templates that accept parameters
and create solution files, project files and project items. We can already impose
some guidance and best practices by tweaking the projects (e.g. to treat warnings
as errors). We've also seen how to define a recipe that gathers information from the
user to pass along to the templates.
&lt;/p&gt;
&lt;p&gt;
You might say, "well, that's an awful lot of work to create a blank project", and
you would be absolutely right. But of course, this is just the beginning. And now
that we have the basics covered, the fun can really start! Stay tuned for the next
episode, in which we'll take this guidance package a step further.
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://jelle.druyts.net/content/binary/GAS%20-%2002%20-%20Create%20a%20project.zip"&gt;Download
the source code for the current state of the Guidance Package.&lt;/a&gt;
&lt;/p&gt;
&lt;img width="0" height="0" src="http://jelle.druyts.net/aggbug.ashx?id=10f2c5b6-717e-4504-816f-3ea8a630be94" /&gt;</description>
      <comments>http://jelle.druyts.net/CommentView.aspx?guid=10f2c5b6-717e-4504-816f-3ea8a630be94</comments>
      <category>Blog</category>
      <category>Blog/Programming</category>
      <category>Blog/Programming/.NET</category>
      <category>Blog/Programming/.NET/GuidanceAutomation</category>
      <category>Blog/Programming/.NET/VS.NET</category>
    </item>
    <item>
      <trackback:ping>http://jelle.druyts.net/Trackback.aspx?guid=6031d391-3ece-417c-b29a-733db48d352e</trackback:ping>
      <pingback:server>http://jelle.druyts.net/pingback.aspx</pingback:server>
      <pingback:target>http://jelle.druyts.net/PermaLink.aspx?guid=6031d391-3ece-417c-b29a-733db48d352e</pingback:target>
      <dc:creator>Jelle Druyts</dc:creator>
      <wfw:comment>http://jelle.druyts.net/CommentView.aspx?guid=6031d391-3ece-417c-b29a-733db48d352e</wfw:comment>
      <wfw:commentRss>http://jelle.druyts.net/SyndicationService.asmx/GetEntryCommentsRss?guid=6031d391-3ece-417c-b29a-733db48d352e</wfw:commentRss>
      <slash:comments>2</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
[This is episode 2 of the <a href="http://jelle.druyts.net/CategoryView.aspx?category=Blog%7CProgramming%7C.NET%7CGuidanceAutomation">Guidance
Automation Series</a>]
</p>
        <p>
In this first real part of the Guidance Automation Series, we'll start off slowly
and easily... If you want to follow along, be sure to install both the Guidance Automation
Extensions (GAX) and the Guidance Automation Toolkit (GAT).
</p>
        <p>
Once you've installed GAT, you'll notice a new project type in Visual Studio called
"Guidance Packages". This will contain all the packages that were installed on this
machine. At this time, a first package is even already registered: the "Guidance Package"
package.
</p>
        <p>
          <img src="http://jelle.druyts.net/content/binary/GAS%20-%20NewProjectGuidancePackage.png" border="0" />
        </p>
        <p>
See, creating a guidance package from scratch yourself would be quite a hassle, so
this über-package will generate a sample package for you that you can use to immediately
start building your own. People that can handle more recursive thinking than me often
call this a Software Factory Factory, because it creates something that will in turn
create something else.
</p>
        <p>
Anyway, if you create a new project using this "Guidance Package" package, you'll
get quite a lot of code that shows off a big number of the available features, as
well as two additional projects that together form the setup project for your package.
You can now build this entire solution, right-click the guidance package project and
select "Register Guidance Package" from the context menu.
</p>
        <p>
          <img src="http://jelle.druyts.net/content/binary/GAS%20-%20GuidancePackageContextMenu.png" border="0" />
        </p>
        <p>
When the registration process has finished, you're ready: your first guidance package
has been built and deployed on your local machine. You can test it by opening another
instance of Visual Studio, where you should now notice your own package in the "Guidance
Packages" project type.
</p>
        <p>
          <img src="http://jelle.druyts.net/content/binary/GAS%20-%20NewProjectDefaultGuidancePackage.png" border="0" />
        </p>
        <p>
Using the MSI that was built by the setup project, you can now easily deploy this
Guidance Package to any developer pc that has the Guidance Automation Extensions installed.
</p>
        <p>
          <em>
            <strong>Guidance Automation Tip #1:</strong> Always keep your guidance package
open in one dedicated instance of Visual Studio and test the package in new instances
every time.</em>
        </p>
        <p>
Notice that registering the package took quite some time, and unfortunately this is
life in the Guidance Automation world: everytime you want to verify or test your package,
you'll have to register it and start another Visual Studio to test it in. So my development
cycle typically looks as follows: make modifications to the package, build the package,
register the package, open another Visual Studio, test the package, make modifications
to the package, ... In other words: think twice before you think your modification
is ok :-)
</p>
        <p>
The good news is that with this new release, a new "Quick Register" option has been
added to the context menu, as you can see in the screenshot above. You can use this
to drastically speed up the development process if you don't have any new recipes
or templates that need to be registered or if you didn't change any <code>HostData</code> sections
that define the integration with Visual Studio. If you did any of these things, you
will need to re-register the entire package, though.
</p>
        <p>
Now that we have the basics covered, let's remove all the pre-generated parts from
the package that aren't needed so that we're left with a clean and empty Guidance
Package. This basically means deleting all the files in the package except the two
xml files in the project root: these form the package definition and are used by GAX
to register it. This empty solution will be the basis on which we'll build the Enterprise
Library Guidance Package in the next installment of this series.
</p>
        <p>
          <a href="http://jelle.druyts.net/content/binary/GAS%20-%2001%20-%20Empty%20starter%20package.zip">Download
the source code for the current state of the Guidance Package.</a>
        </p>
        <img width="0" height="0" src="http://jelle.druyts.net/aggbug.ashx?id=6031d391-3ece-417c-b29a-733db48d352e" />
      </body>
      <title>GAS02: Creating A Guidance Package</title>
      <guid isPermaLink="false">http://jelle.druyts.net/PermaLink.aspx?guid=6031d391-3ece-417c-b29a-733db48d352e</guid>
      <link>http://jelle.druyts.net/2006/06/26/GAS02CreatingAGuidancePackage.aspx</link>
      <pubDate>Mon, 26 Jun 2006 20:09:55 GMT</pubDate>
      <description>&lt;p&gt;
[This is episode 2 of the &lt;a href="http://jelle.druyts.net/CategoryView.aspx?category=Blog%7CProgramming%7C.NET%7CGuidanceAutomation"&gt;Guidance
Automation Series&lt;/a&gt;]
&lt;/p&gt;
&lt;p&gt;
In this first real part of the Guidance Automation Series, we'll start off slowly
and easily... If you want to follow along, be sure to install both the Guidance Automation
Extensions (GAX) and the Guidance Automation Toolkit (GAT).
&lt;/p&gt;
&lt;p&gt;
Once you've installed GAT, you'll notice a new project type in Visual Studio called
"Guidance Packages". This will contain all the packages that were installed on this
machine. At this time, a first package is even already registered: the "Guidance Package"
package.
&lt;/p&gt;
&lt;p&gt;
&lt;img src="http://jelle.druyts.net/content/binary/GAS%20-%20NewProjectGuidancePackage.png" border=0&gt;
&lt;/p&gt;
&lt;p&gt;
See, creating a guidance package from scratch yourself would be quite a hassle, so
this über-package will generate a sample package for you that you can use to immediately
start building your own. People that can handle more recursive thinking than me often
call this a Software Factory Factory, because it creates something that will in turn
create something else.
&lt;/p&gt;
&lt;p&gt;
Anyway, if you create a new project using this "Guidance Package" package, you'll
get quite a lot of code that shows off a big number of the available features, as
well as two additional projects that together form the setup project for your package.
You can now build this entire solution, right-click the guidance package project and
select "Register Guidance Package" from the context menu.
&lt;/p&gt;
&lt;p&gt;
&lt;img src="http://jelle.druyts.net/content/binary/GAS%20-%20GuidancePackageContextMenu.png" border=0&gt;
&lt;/p&gt;
&lt;p&gt;
When the registration process has finished, you're ready: your first guidance package
has been built and deployed on your local machine. You can test it by opening another
instance of Visual Studio, where you should now notice your own package in the "Guidance
Packages" project type.
&lt;/p&gt;
&lt;p&gt;
&lt;img src="http://jelle.druyts.net/content/binary/GAS%20-%20NewProjectDefaultGuidancePackage.png" border=0&gt;
&lt;/p&gt;
&lt;p&gt;
Using the MSI that was built by the setup project, you can now easily deploy this
Guidance Package to any developer pc that has the Guidance Automation Extensions installed.
&lt;/p&gt;
&lt;p&gt;
&lt;em&gt;&lt;strong&gt;Guidance Automation Tip #1:&lt;/strong&gt; Always keep your guidance package
open in one dedicated instance of Visual Studio and test the package in new instances
every time.&lt;/em&gt;
&lt;/p&gt;
&lt;p&gt;
Notice that registering the package took quite some time, and unfortunately this is
life in the Guidance Automation world: everytime you want to verify or test your package,
you'll have to register it and start another Visual Studio to test it in. So my development
cycle typically looks as follows: make modifications to the package, build the package,
register the package, open another Visual Studio, test the package, make modifications
to the package, ... In other words: think twice before you think your modification
is ok :-)
&lt;/p&gt;
&lt;p&gt;
The good news is that with this new release, a new "Quick Register" option has been
added to the context menu, as you can see in the screenshot above. You can use this
to drastically speed up the development process if you don't have any new recipes
or templates that need to be registered or if you didn't change any &lt;code&gt;HostData&lt;/code&gt; sections
that define the integration with Visual Studio. If you did any of these things, you
will need to re-register the entire package, though.
&lt;/p&gt;
&lt;p&gt;
Now that we have the basics covered, let's remove all the pre-generated parts from
the package that aren't needed so that we're left with a clean and empty Guidance
Package. This basically means deleting all the files in the package except the two
xml files in the project root: these form the package definition and are used by GAX
to register it. This empty solution will be the basis on which we'll build the Enterprise
Library Guidance Package in the next installment of this series.
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://jelle.druyts.net/content/binary/GAS%20-%2001%20-%20Empty%20starter%20package.zip"&gt;Download
the source code for the current state of the Guidance Package.&lt;/a&gt;
&lt;/p&gt;
&lt;img width="0" height="0" src="http://jelle.druyts.net/aggbug.ashx?id=6031d391-3ece-417c-b29a-733db48d352e" /&gt;</description>
      <comments>http://jelle.druyts.net/CommentView.aspx?guid=6031d391-3ece-417c-b29a-733db48d352e</comments>
      <category>Blog</category>
      <category>Blog/Programming</category>
      <category>Blog/Programming/.NET</category>
      <category>Blog/Programming/.NET/GuidanceAutomation</category>
      <category>Blog/Programming/.NET/VS.NET</category>
    </item>
  </channel>
</rss>