[mapguide-commits] r6585 - in trunk/Installer: . Libraries/MapGuide Web Extensions Libraries/MapGuide Web Extensions/Lang Support Support/Paraffin Support/Paraffin/Properties

svn_mapguide at osgeo.org svn_mapguide at osgeo.org
Sun Apr 22 08:47:10 EDT 2012


Author: jng
Date: 2012-04-22 05:47:10 -0700 (Sun, 22 Apr 2012)
New Revision: 6585

Added:
   trunk/Installer/Features.xml
   trunk/Installer/Paraffin.exe
   trunk/Installer/Paraffin.pdb
   trunk/Installer/Support/Paraffin/
   trunk/Installer/Support/Paraffin/ArgParser.cs
   trunk/Installer/Support/Paraffin/Constants.Designer.cs
   trunk/Installer/Support/Paraffin/Constants.resx
   trunk/Installer/Support/Paraffin/Features.xml
   trunk/Installer/Support/Paraffin/GlobalSuppressions.cs
   trunk/Installer/Support/Paraffin/Main.cs
   trunk/Installer/Support/Paraffin/Paraffin.csproj
   trunk/Installer/Support/Paraffin/Paraffin.sln
   trunk/Installer/Support/Paraffin/ParaffinArgParser.cs
   trunk/Installer/Support/Paraffin/Properties/
   trunk/Installer/Support/Paraffin/Properties/AssemblyInfo.cs
   trunk/Installer/Support/Paraffin/Wintellect.SNK
   trunk/Installer/Support/Paraffin/WintellectDictionary.xml
Modified:
   trunk/Installer/Libraries/MapGuide Web Extensions/Lang/MapGuideWebExtensions_en-US.wxl
   trunk/Installer/Libraries/MapGuide Web Extensions/MapGuideWebExtensions.wxs
Log:
#1805: Make SVN metadata an optional install component. To do this we had to hack our own custom version of Paraffin to allow specifying a list of directories to look for. Wix components under these directories will be relocated to a ComponentGroup named <filename>_SVNMETADATA. A new top-level wix feature then includes all of these ComponentGroups. This submission includes the modified Paraffin source and executables

NOTE: Installing with the SVN metadata enabled will also include the .svn dirs for in-active components (because we're not tying svn metadata to their respective features, but to a single global feature). This is a small inconvenient price to pay.

Added: trunk/Installer/Features.xml
===================================================================
--- trunk/Installer/Features.xml	                        (rev 0)
+++ trunk/Installer/Features.xml	2012-04-22 12:47:10 UTC (rev 6585)
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<FeatureMap>
+  <DirectorySearch Name=".svn" Suffix="_SVNMETADATA" />
+</FeatureMap>
\ No newline at end of file

Modified: trunk/Installer/Libraries/MapGuide Web Extensions/Lang/MapGuideWebExtensions_en-US.wxl
===================================================================
--- trunk/Installer/Libraries/MapGuide Web Extensions/Lang/MapGuideWebExtensions_en-US.wxl	2012-04-22 06:20:09 UTC (rev 6584)
+++ trunk/Installer/Libraries/MapGuide Web Extensions/Lang/MapGuideWebExtensions_en-US.wxl	2012-04-22 12:47:10 UTC (rev 6585)
@@ -46,6 +46,8 @@
     <String Id="WebStdIconsFeature_Description">Icons for the AJAX viewer</String>
     <String Id="WebTempFeature">Temp Directory</String>
     <String Id="WebTempFeature_Description">You may choose a location where MapGuide will store temporary files.</String>
+    <String Id="SvnMetadataFeature">SVN Metadata</String>
+    <String Id="SvnMetadataFeature_Description">Includes SVN metadata for web application files allowing the user to pull down fixes and updates using a Subversion client</String>
 
     <String Id="Shortcut_DevGuide">MapGuide Developer's Guide</String>
     <String Id="Shortcut_WebAPI">Web API Documentation</String>

Modified: trunk/Installer/Libraries/MapGuide Web Extensions/MapGuideWebExtensions.wxs
===================================================================
--- trunk/Installer/Libraries/MapGuide Web Extensions/MapGuideWebExtensions.wxs	2012-04-22 06:20:09 UTC (rev 6584)
+++ trunk/Installer/Libraries/MapGuide Web Extensions/MapGuideWebExtensions.wxs	2012-04-22 12:47:10 UTC (rev 6585)
@@ -228,7 +228,25 @@
                 <ComponentGroupRef Id="group_MAPVIEWERSTDICONFILES" />
             </Feature>
             <FeatureRef Id="IISSetupFeature" />
+            <Feature Id="SvnMetadataFeature" Title="!(loc.SvnMetadataFeature)" Level="1" Description="!(loc.SvnMetadataFeature_Description)" AllowAdvertise="no" TypicalDefault="install" InstallDefault="local">
+                <ComponentGroupRef Id="incDotNetDevGuideFiles_SVNMETADATA" />
+                <ComponentGroupRef Id="incDotNetViewerSampleFiles_SVNMETADATA" />
+                <ComponentGroupRef Id="incFusionFiles_SVNMETADATA" />
+                <ComponentGroupRef Id="incHelpFiles_SVNMETADATA" />
+                <ComponentGroupRef Id="incJavaDevGuideFiles_SVNMETADATA" />
+                <ComponentGroupRef Id="incJavaViewerSampleFiles_SVNMETADATA" />
+                <ComponentGroupRef Id="incMapAgentFiles_SVNMETADATA" />
+                <ComponentGroupRef Id="incMapViewerAspxFiles_SVNMETADATA" />
+                <ComponentGroupRef Id="incMapViewerFiles_SVNMETADATA" />
+                <ComponentGroupRef Id="incMapViewerJspFiles_SVNMETADATA" />
+                <ComponentGroupRef Id="incMapViewerLocalizedFiles_SVNMETADATA" />
+                <ComponentGroupRef Id="incMapViewerMapAdminFiles_SVNMETADATA" />
+                <ComponentGroupRef Id="incMapViewerPhpFiles_SVNMETADATA" />
+                <ComponentGroupRef Id="incMapViewerSchemareportFiles_SVNMETADATA" />
+                <ComponentGroupRef Id="incMapViewerStdiconFiles_SVNMETADATA" />
+                <ComponentGroupRef Id="incPhpDevGuideFiles_SVNMETADATA" />
+                <ComponentGroupRef Id="incPhpViewerSampleFiles_SVNMETADATA" />
+            </Feature>
         </Feature>
-
     </Fragment>
 </Wix>

Added: trunk/Installer/Paraffin.exe
===================================================================
(Binary files differ)


Property changes on: trunk/Installer/Paraffin.exe
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/Installer/Paraffin.pdb
===================================================================
(Binary files differ)


Property changes on: trunk/Installer/Paraffin.pdb
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream


Property changes on: trunk/Installer/Support/Paraffin
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/Installer/Support/Paraffin/ArgParser.cs
===================================================================
--- trunk/Installer/Support/Paraffin/ArgParser.cs	                        (rev 0)
+++ trunk/Installer/Support/Paraffin/ArgParser.cs	2012-04-22 12:47:10 UTC (rev 6585)
@@ -0,0 +1,468 @@
+/*------------------------------------------------------------------------------
+ * Debugging Microsoft .NET 2.0 Applications
+ * Copyright © 1997-2006 John Robbins -- All rights reserved. 
+ -----------------------------------------------------------------------------*/
+using System;
+using System.Diagnostics;
+using System.Globalization;
+using System.Collections.Generic;
+using System.Text;
+using System.IO;
+using System.Diagnostics.CodeAnalysis;
+
+namespace Wintellect.Paraffin
+{
+    /// <summary>
+    /// A command line argument parsing class.
+    /// </summary>
+    /// <remarks>
+    /// This class is based on the WordCount version from the Framework SDK
+    /// samples.  Any errors are mine.
+    /// <para>
+    /// There are two arrays of flags you'll pass to the constructors.  The
+    /// flagSymbols are supposed to be standalone switches that toggle an option
+    /// on.  The dataSymbols are for switches that take data values.  For 
+    /// example, if your application needs a switch, -c, to set the count, 
+    /// you'd put "c" in the dataSymbols.  Note that you can pass null/Nothing 
+    /// for dataSymbols if you don't need them.
+    /// </para>
+    /// </remarks>
+    internal abstract class ArgParser
+    {
+        // For example: "/", "-"
+        private String [] switchChars;
+        // Switch character(s) that are simple flags
+        private String [] flagSymbols;
+        // Switch characters(s) that take parameters.  For example: -f <file>.
+        // This can be null if not needed.
+        private String [] dataSymbols;
+        // Are switches case-sensitive?
+        private Boolean caseSensitiveSwitches;
+
+
+        /// <summary>
+        /// The status values for various internal methods.
+        /// </summary>
+        protected enum SwitchStatus
+        {
+            /// <summary>
+            /// Success.
+            /// </summary>
+            NoError ,
+            /// <summary>
+            /// There was a problem.
+            /// </summary>
+            Error ,
+            /// <summary>
+            /// Show the usage help.
+            /// </summary>
+            ShowUsage
+        } ;
+
+        ///// <summary>
+        ///// Constructs the class with nothing but flag switches and defaults to 
+        ///// "/" and "-" as valid switch characters.
+        ///// </summary>
+        ///// <param name="flagSymbols">
+        ///// The array of simple flags to toggle options on or off. 
+        ///// </param>
+        //protected ArgParser ( String [] flagSymbols )
+        //    : this ( flagSymbols ,
+        //             null ,
+        //             false ,
+        //             new string [] { "/" , "-" } )
+        //{
+        //}
+
+        ///// <summary>
+        ///// Constructs the class with nothing but flag switches and defaults to 
+        ///// "/" and "-" as valid switch characters.
+        ///// </summary>
+        ///// <param name="flagSymbols">
+        ///// The array of simple flags to toggle options on or off. 
+        ///// </param>
+        ///// <param name="caseSensitiveSwitches">
+        ///// True if case sensitive switches are supposed to be used.
+        ///// </param>
+        //protected ArgParser ( String [] flagSymbols ,
+        //                      Boolean caseSensitiveSwitches )
+        //    : this ( flagSymbols ,
+        //             null ,
+        //             caseSensitiveSwitches ,
+        //             new string [] { "/" , "-" } )
+        //{
+        //}
+
+        ///// <summary>
+        ///// Constructs the class to use case-insensitive switches and 
+        ///// defaults to "/" and "-" as valid switch characters.
+        ///// </summary>
+        ///// <param name="flagSymbols">
+        ///// The array of simple flags to toggle options on or off. 
+        ///// </param>
+        ///// <param name="dataSymbols">
+        ///// The array of options that need data either in the next parameter or
+        ///// after the switch itself.  This value can be null/Nothing.
+        ///// </param>
+        //protected ArgParser ( String [] flagSymbols , String [] dataSymbols )
+        //    : this ( flagSymbols ,
+        //             dataSymbols ,
+        //             false ,
+        //             new string [] { "/" , "-" } )
+        //{
+        //}
+
+        ///// <summary>
+        ///// Constructs the class and defaults to "/" and "-" as the only 
+        ///// valid switch characters
+        ///// </summary>
+        ///// <param name="flagSymbols">
+        ///// The array of simple flags to toggle options on or off. 
+        ///// </param>
+        ///// <param name="dataSymbols">
+        ///// The array of options that need data either in the next parameter or
+        ///// after the switch itself.  This value can be null/Nothing.
+        ///// </param>
+        ///// <param name="caseSensitiveSwitches">
+        ///// True if case sensitive switches are supposed to be used.
+        ///// </param>
+        protected ArgParser ( String [] flagSymbols ,
+                              String [] dataSymbols ,
+                              Boolean caseSensitiveSwitches )
+            : this ( flagSymbols ,
+                     dataSymbols ,
+                     caseSensitiveSwitches ,
+                     new string [] { "/" , "-" } )
+        {
+        }
+
+
+        /// <summary>
+        /// Constructor where the caller sets all options to the class.
+        /// </summary>
+        /// <param name="flagSymbols">
+        /// The array of simple flags to toggle options on or off. 
+        /// </param>
+        /// <param name="dataSymbols">
+        /// The array of options that need data either in the next parameter or
+        /// after the switch itself.  This value can be null/Nothing.
+        /// </param>
+        /// <param name="caseSensitiveSwitches">
+        /// True if case sensitive switches are supposed to be used.
+        /// </param>
+        /// <param name="switchChars">
+        /// The array of switch characters to use.
+        /// </param>
+        /// <exception cref="ArgumentException">
+        /// Thrown if <paramref name="flagSymbols"/> or 
+        /// <paramref name="switchChars"/> are invalid.
+        /// </exception>
+        protected ArgParser ( String [] flagSymbols ,
+                              String [] dataSymbols ,
+                              Boolean caseSensitiveSwitches ,
+                              String [] switchChars )
+        {
+            Debug.Assert ( null != flagSymbols , "null != flagSymbols" );
+            // Avoid assertion side effects in debug builds.
+#if DEBUG
+            if ( null != flagSymbols )
+            {
+                Debug.Assert ( flagSymbols.Length > 0 ,
+                               "flagSymbols.Length > 0" );
+            }
+#endif
+            if ( ( null == flagSymbols ) || ( 0 == flagSymbols.Length ) )
+            {
+                throw new ArgumentException ( Constants.ArrayMustBeValid ,
+                                              "flagSymbols" );
+            }
+            Debug.Assert ( null != switchChars , "null != switchChars" );
+            // Avoid assertion side effects in debug builds.
+#if DEBUG
+            if ( null != switchChars )
+            {
+                Debug.Assert ( switchChars.Length > 0 ,
+                               "switchChars.Length > 0" );
+            }
+#endif
+            if ( ( null == switchChars ) || ( 0 == switchChars.Length ) )
+            {
+                throw new ArgumentException ( Constants.ArrayMustBeValid ,
+                                              "switchChars" );
+            }
+
+            this.flagSymbols = flagSymbols;
+            this.dataSymbols = dataSymbols;
+            this.caseSensitiveSwitches = caseSensitiveSwitches;
+            this.switchChars = switchChars;
+        }
+
+        /// <summary>
+        /// Reports correct command line usage.
+        /// </summary>
+        /// <param name="errorInfo">
+        /// The string with the invalid command line option.
+        /// </param>
+        public abstract void OnUsage ( String errorInfo );
+
+
+        // Every derived class must implement an OnSwitch method or a switch is 
+        // considered an error.
+        /// <summary>
+        /// Called when a switch is parsed out.
+        /// </summary>
+        /// <param name="switchSymbol">
+        /// The switch value parsed out.
+        /// </param>
+        /// <param name="switchValue">
+        /// The value of the switch.  For flag switches this is null/Nothing.
+        /// </param>
+        /// <returns>
+        /// One of the <see cref="SwitchStatus"/> values.
+        /// </returns>
+        protected virtual SwitchStatus OnSwitch ( String switchSymbol ,
+                                                  String switchValue )
+        {
+            return ( SwitchStatus.Error );
+        }
+
+        /// <summary>
+        /// Called when a non-switch value is parsed out.
+        /// </summary>
+        /// <param name="value">
+        /// The value parsed out.
+        /// </param>
+        /// <returns>
+        /// One of the <see cref="SwitchStatus"/> values.
+        /// </returns>
+        protected virtual SwitchStatus OnNonSwitch ( String value )
+        {
+            return ( SwitchStatus.Error );
+        }
+
+        /// <summary>
+        /// Called when parsing is finished so final sanity checking can be 
+        /// performed.
+        /// </summary>
+        /// <returns>
+        /// One of the <see cref="SwitchStatus"/> values.
+        /// </returns>
+        protected virtual SwitchStatus OnDoneParse ( )
+        {
+            // By default, we'll assume that all parsing was an error.
+            return ( SwitchStatus.Error );
+        }
+
+        ///// <summary>
+        ///// Parses the application's command-line arguments.
+        ///// </summary>
+        ///// <returns>
+        ///// True if the parsing succeeded.
+        ///// </returns>
+        //public Boolean Parse ( )
+        //{
+        //    // Visual Basic will use this method since its entry-point function 
+        //    // doesn't get the command-line arguments passed to it.
+        //    return ( Parse ( Environment.GetCommandLineArgs ( ) ) );
+        //}
+
+        // Looks to see if the switch is in the array.
+        private int IsSwitchInArray ( String [] switchArray ,
+                                      String value )
+        {
+            String valueCompare = value;
+            if ( true == caseSensitiveSwitches )
+            {
+                valueCompare = value.ToUpperInvariant ( );
+            }
+            int retValue = -1;
+            for ( int n = 0 ; n < switchArray.Length ; n++ )
+            {
+                String currSwitch = switchArray [ n ];
+                if ( true == caseSensitiveSwitches )
+                {
+                    currSwitch = currSwitch.ToUpperInvariant ( );
+                }
+                if ( 0 == String.CompareOrdinal ( valueCompare ,
+                                                  currSwitch ) )
+                {
+                    retValue = n;
+                    break;
+                }
+            }
+            return ( retValue );
+        }
+
+        /// <summary>
+        /// Looks to see if this string starts with a switch character.
+        /// </summary>
+        /// <param name="value">
+        /// The string to check.
+        /// </param>
+        /// <returns>
+        /// True if the string starts with a switch character.
+        /// </returns>
+        private Boolean StartsWithSwitchChar ( String value )
+        {
+            Boolean isSwitch = false;
+            for ( int n = 0 ; !isSwitch && ( n < switchChars.Length ) ; n++ )
+            {
+                if ( 0 == String.CompareOrdinal ( value ,
+                                                  0 ,
+                                                  switchChars [ n ] ,
+                                                  0 ,
+                                                  1 ) )
+                {
+                    isSwitch = true;
+                    break;
+                }
+            }
+            return ( isSwitch );
+        }
+
+        /// <summary>
+        /// Parses an arbitrary set of arguments.
+        /// </summary>
+        /// <param name="args">
+        /// The string array to parse through.
+        /// </param>
+        /// <returns>
+        /// True if parsing was correct.  
+        /// </returns>
+        /// <exception cref="ArgumentException">
+        /// Thrown if <paramref name="args"/> is null or empty.
+        /// </exception>
+        [SuppressMessage ( "Microsoft.Globalization" ,
+                           "CA1308:NormalizeStringsToUppercase" ,
+                           Justification = "I NEED TO FIX THIS!" )]
+        public Boolean Parse ( String [] args )
+        {
+            Debug.Assert ( null != args , "null != args" );
+            // Avoid side effects in debug builds.
+#if DEBUG
+            if ( null != args )
+            {
+                Debug.Assert ( args.Length > 0 , "args.Length > 0" );
+            }
+#endif
+            if ( ( null == args ) || ( args.Length == 0 ) )
+            {
+                throw new ArgumentException ( Constants.InvalidParameter );
+            }
+            // Assume parsing is successful.
+            SwitchStatus ss = SwitchStatus.NoError;
+
+            int errorArg = -1;
+            int currArg;
+            for ( currArg = 0 ;
+                 ( ss == SwitchStatus.NoError ) && ( currArg < args.Length ) ;
+                 currArg++ )
+            {
+                errorArg = currArg;
+                // Determine if this argument starts with a valid switch 
+                // character
+                Boolean isSwitch = StartsWithSwitchChar ( args [ currArg ] );
+                if ( true == isSwitch )
+                {
+                    // Indicates the symbol is a data symbol.
+                    Boolean useDataSymbols = false;
+                    // Get the argument itself.
+                    String processedArg = args [ currArg ].Substring ( 1 );
+                    // The index into the symbol array.
+                    int n;
+                    // First check the flags array.
+                    n = IsSwitchInArray ( flagSymbols , processedArg );
+                    // If it's not in the flags array, try the data array if that 
+                    // array is not null.
+                    if ( ( -1 == n ) && ( null != dataSymbols ) )
+                    {
+                        n = IsSwitchInArray ( dataSymbols , processedArg );
+                        useDataSymbols = true;
+                    }
+                    if ( -1 != n )
+                    {
+                        String theSwitch = null;
+                        String dataValue = null;
+                        // If it's a flag switch.
+                        if ( false == useDataSymbols )
+                        {
+                            // This is a legal switch, notified the derived 
+                            // class of this switch and its value.
+                            theSwitch = flagSymbols [ n ];
+                            if ( caseSensitiveSwitches )
+                            {
+                                theSwitch = flagSymbols [ n ].
+                                                           ToLowerInvariant ( );
+                            }
+                        }
+                        else
+                        {
+                            theSwitch = dataSymbols [ n ];
+                            // Look at the next parameter if it's there.
+                            if ( currArg + 1 < args.Length )
+                            {
+                                currArg++;
+                                dataValue = args [ currArg ];
+                                // Take a look at dataValue to see if it starts
+                                // with a switch character. If it does, that
+                                // means this data argument is empty.
+                                if ( true == StartsWithSwitchChar ( 
+                                                                   dataValue ) )
+                                {
+                                    ss = SwitchStatus.Error;
+                                    break;
+                                }
+                            }
+                            else
+                            {
+                                ss = SwitchStatus.Error;
+                                break;
+                            }
+                        }
+                        ss = OnSwitch ( theSwitch , dataValue );
+                    }
+                    else
+                    {
+                        ss = SwitchStatus.Error;
+                        break;
+                    }
+                }
+                else
+                {
+                    // This is not a switch, notified the derived class of this 
+                    // "non-switch value"
+                    ss = OnNonSwitch ( args [ currArg ] );
+                }
+            }
+
+            // Finished parsing arguments
+            if ( ss == SwitchStatus.NoError )
+            {
+                // No error occurred while parsing, let derived class perform a 
+                // sanity check and return an appropriate status
+                ss = OnDoneParse ( );
+            }
+
+            if ( ss == SwitchStatus.ShowUsage )
+            {
+                // Status indicates that usage should be shown, show it
+                OnUsage ( null );
+            }
+
+            if ( ss == SwitchStatus.Error )
+            {
+                String errorValue = null;
+                if ( ( errorArg != -1 ) && ( errorArg != args.Length ) )
+                {
+                    errorValue = args [ errorArg ];
+                }
+                // Status indicates that an error occurred, show it and the 
+                // proper usage
+                OnUsage ( errorValue );
+            }
+
+            // Return whether all parsing was successful.
+            return ( ss == SwitchStatus.NoError );
+        }
+    }
+}
\ No newline at end of file

Added: trunk/Installer/Support/Paraffin/Constants.Designer.cs
===================================================================
--- trunk/Installer/Support/Paraffin/Constants.Designer.cs	                        (rev 0)
+++ trunk/Installer/Support/Paraffin/Constants.Designer.cs	2012-04-22 12:47:10 UTC (rev 6585)
@@ -0,0 +1,309 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:2.0.50727.3053
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace Wintellect.Paraffin {
+    using System;
+    
+    
+    /// <summary>
+    ///   A strongly-typed resource class, for looking up localized strings, etc.
+    /// </summary>
+    // This class was auto-generated by the StronglyTypedResourceBuilder
+    // class via a tool like ResGen or Visual Studio.
+    // To add or remove a member, edit your .ResX file then rerun ResGen
+    // with the /str option, or rebuild your VS project.
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class Constants {
+        
+        private static global::System.Resources.ResourceManager resourceMan;
+        
+        private static global::System.Globalization.CultureInfo resourceCulture;
+        
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal Constants() {
+        }
+        
+        /// <summary>
+        ///   Returns the cached ResourceManager instance used by this class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager {
+            get {
+                if (object.ReferenceEquals(resourceMan, null)) {
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Wintellect.Paraffin.Constants", typeof(Constants).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+        
+        /// <summary>
+        ///   Overrides the current thread's CurrentUICulture property for all
+        ///   resource lookups using this strongly typed resource class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture {
+            get {
+                return resourceCulture;
+            }
+            set {
+                resourceCulture = value;
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Only one -alias switch at a time is supported..
+        /// </summary>
+        internal static string AliasMultipleSwitches {
+            get {
+                return ResourceManager.GetString("AliasMultipleSwitches", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to The array must not be null or of zero length..
+        /// </summary>
+        internal static string ArrayMustBeValid {
+            get {
+                return ResourceManager.GetString("ArrayMustBeValid", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Autogenerated by Paraffin - Wintellect - John Robbins - john at wintellect.com.
+        /// </summary>
+        internal static string CommentProducer {
+            get {
+                return ResourceManager.GetString("CommentProducer", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Manual changes to this file may cause incorrect behavior..
+        /// </summary>
+        internal static string CommentWarning {
+            get {
+                return ResourceManager.GetString("CommentWarning", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to The -directory switch must always be specified..
+        /// </summary>
+        internal static string DirectoryCannotBeEmpty {
+            get {
+                return ResourceManager.GetString("DirectoryCannotBeEmpty", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to The -directory specified path does not exist..
+        /// </summary>
+        internal static string DirectoryDoesNotExist {
+            get {
+                return ResourceManager.GetString("DirectoryDoesNotExist", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Only one -directory switch at a time is supported..
+        /// </summary>
+        internal static string DirectoryMultipleSwitches {
+            get {
+                return ResourceManager.GetString("DirectoryMultipleSwitches", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Only one -dirref switch at a time is supported..
+        /// </summary>
+        internal static string DirectoryRefMultipleSwitches {
+            get {
+                return ResourceManager.GetString("DirectoryRefMultipleSwitches", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Error on the &apos;{0}&apos; switch..
+        /// </summary>
+        internal static string ErrorSwitch {
+            get {
+                return ResourceManager.GetString("ErrorSwitch", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Extension values cannot be empty..
+        /// </summary>
+        internal static string ExtensionCannotBeEmpty {
+            get {
+                return ResourceManager.GetString("ExtensionCannotBeEmpty", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Only one increment switch at a time is supported..
+        /// </summary>
+        internal static string IncrementMultipleSwitches {
+            get {
+                return ResourceManager.GetString("IncrementMultipleSwitches", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to The increment value does not appear to be an integer..
+        /// </summary>
+        internal static string IncrementNoParse {
+            get {
+                return ResourceManager.GetString("IncrementNoParse", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to The increment value must be greater than zero..
+        /// </summary>
+        internal static string IncrementNotZero {
+            get {
+                return ResourceManager.GetString("IncrementNotZero", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to A call into Windows failed unexpectedly..
+        /// </summary>
+        internal static string InteropCallFailed {
+            get {
+                return ResourceManager.GetString("InteropCallFailed", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to There appears to be multiple nodes in input .WXS file with the same file name in the same directory: {0}..
+        /// </summary>
+        internal static string InvalidFileNameCountFmt {
+            get {
+                return ResourceManager.GetString("InvalidFileNameCountFmt", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to An invalid parameter was passed to the method..
+        /// </summary>
+        internal static string InvalidParameter {
+            get {
+                return ResourceManager.GetString("InvalidParameter", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Update and creation command line options are mutually excusive..
+        /// </summary>
+        internal static string MutuallyExclusiveOptions {
+            get {
+                return ResourceManager.GetString("MutuallyExclusiveOptions", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Only one output or processing file can be specified..
+        /// </summary>
+        internal static string OutputAlreadySpecified {
+            get {
+                return ResourceManager.GetString("OutputAlreadySpecified", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to You must specify the .WXS file to write to or process..
+        /// </summary>
+        internal static string OutputCannotBeEmpty {
+            get {
+                return ResourceManager.GetString("OutputCannotBeEmpty", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to The -unique value cannot be empty..
+        /// </summary>
+        internal static string UniqueCannotBeEmpty {
+            get {
+                return ResourceManager.GetString("UniqueCannotBeEmpty", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Only one -unique switch at a time is supported..
+        /// </summary>
+        internal static string UniqueMultipleSwitches {
+            get {
+                return ResourceManager.GetString("UniqueMultipleSwitches", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to The -unique string value cannot be longer than 65 characters..
+        /// </summary>
+        internal static string UniqueTooLong {
+            get {
+                return ResourceManager.GetString("UniqueTooLong", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Unknown command line option..
+        /// </summary>
+        internal static string UnknownCommandLineOption {
+            get {
+                return ResourceManager.GetString("UnknownCommandLineOption", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to The file to process does no appear to have been created with Paraffin. The comment node is missing..
+        /// </summary>
+        internal static string UnknownFileType {
+            get {
+                return ResourceManager.GetString("UnknownFileType", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to The file to update does not exist..
+        /// </summary>
+        internal static string UpdateFileMustExist {
+            get {
+                return ResourceManager.GetString("UpdateFileMustExist", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Paraffin {0}
+        ///(c) 2007-2008, John Robbins - john at wintellect.com
+        ///A better Tallow for generating Windows Installer XML (WiX) 3.0 fragments. 
+        ///
+        ///Usage:
+        /// Paraffin (-dir &lt;dir&gt; -custom &lt;value&gt; &lt;file&gt;
+        ///            [-alias &lt;alias&gt; -ext &lt;ext&gt;* | -dirref &lt;DirectoryRef&gt; |
+        ///             -direXclude &lt;exdir&gt;* | -inc # |  
+        ///             -guids |-multiple | -norecurse | -Win64] ) |
+        ///          (-update &lt;file&gt; [-ext &lt;ext&gt;*])
+        ///           
+        ///Required parameters to create a new fragment:
+        ///    -dir &lt;dir&gt;             - The direc [rest of string was truncated]&quot;;.
+        /// </summary>
+        internal static string UsageString {
+            get {
+                return ResourceManager.GetString("UsageString", resourceCulture);
+            }
+        }
+    }
+}

Added: trunk/Installer/Support/Paraffin/Constants.resx
===================================================================
--- trunk/Installer/Support/Paraffin/Constants.resx	                        (rev 0)
+++ trunk/Installer/Support/Paraffin/Constants.resx	2012-04-22 12:47:10 UTC (rev 6585)
@@ -0,0 +1,256 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="AliasMultipleSwitches" xml:space="preserve">
+    <value>Only one -alias switch at a time is supported.</value>
+  </data>
+  <data name="ArrayMustBeValid" xml:space="preserve">
+    <value>The array must not be null or of zero length.</value>
+  </data>
+  <data name="CommentProducer" xml:space="preserve">
+    <value>Autogenerated by Paraffin - Wintellect - John Robbins - john at wintellect.com</value>
+  </data>
+  <data name="CommentWarning" xml:space="preserve">
+    <value>Manual changes to this file may cause incorrect behavior.</value>
+  </data>
+  <data name="DirectoryCannotBeEmpty" xml:space="preserve">
+    <value>The -directory switch must always be specified.</value>
+  </data>
+  <data name="DirectoryDoesNotExist" xml:space="preserve">
+    <value>The -directory specified path does not exist.</value>
+  </data>
+  <data name="DirectoryMultipleSwitches" xml:space="preserve">
+    <value>Only one -directory switch at a time is supported.</value>
+  </data>
+  <data name="DirectoryRefMultipleSwitches" xml:space="preserve">
+    <value>Only one -dirref switch at a time is supported.</value>
+  </data>
+  <data name="ErrorSwitch" xml:space="preserve">
+    <value>Error on the '{0}' switch.</value>
+  </data>
+  <data name="ExtensionCannotBeEmpty" xml:space="preserve">
+    <value>Extension values cannot be empty.</value>
+  </data>
+  <data name="IncrementMultipleSwitches" xml:space="preserve">
+    <value>Only one increment switch at a time is supported.</value>
+  </data>
+  <data name="IncrementNoParse" xml:space="preserve">
+    <value>The increment value does not appear to be an integer.</value>
+  </data>
+  <data name="IncrementNotZero" xml:space="preserve">
+    <value>The increment value must be greater than zero.</value>
+  </data>
+  <data name="InteropCallFailed" xml:space="preserve">
+    <value>A call into Windows failed unexpectedly.</value>
+  </data>
+  <data name="InvalidFileNameCountFmt" xml:space="preserve">
+    <value>There appears to be multiple nodes in input .WXS file with the same file name in the same directory: {0}.</value>
+  </data>
+  <data name="InvalidParameter" xml:space="preserve">
+    <value>An invalid parameter was passed to the method.</value>
+  </data>
+  <data name="MutuallyExclusiveOptions" xml:space="preserve">
+    <value>Update and creation command line options are mutually excusive.</value>
+  </data>
+  <data name="OutputAlreadySpecified" xml:space="preserve">
+    <value>Only one output or processing file can be specified.</value>
+  </data>
+  <data name="OutputCannotBeEmpty" xml:space="preserve">
+    <value>You must specify the .WXS file to write to or process.</value>
+  </data>
+  <data name="UniqueCannotBeEmpty" xml:space="preserve">
+    <value>The -unique value cannot be empty.</value>
+  </data>
+  <data name="UniqueMultipleSwitches" xml:space="preserve">
+    <value>Only one -unique switch at a time is supported.</value>
+  </data>
+  <data name="UniqueTooLong" xml:space="preserve">
+    <value>The -unique string value cannot be longer than 65 characters.</value>
+  </data>
+  <data name="UnknownCommandLineOption" xml:space="preserve">
+    <value>Unknown command line option.</value>
+  </data>
+  <data name="UnknownFileType" xml:space="preserve">
+    <value>The file to process does no appear to have been created with Paraffin. The comment node is missing.</value>
+  </data>
+  <data name="UpdateFileMustExist" xml:space="preserve">
+    <value>The file to update does not exist.</value>
+  </data>
+  <data name="UsageString" xml:space="preserve">
+    <value>Paraffin {0}
+(c) 2007-2008, John Robbins - john at wintellect.com
+A better Tallow for generating Windows Installer XML (WiX) 3.0 fragments. 
+
+Usage:
+ Paraffin (-dir &lt;dir&gt; -custom &lt;value&gt; &lt;file&gt;
+            [-alias &lt;alias&gt; -ext &lt;ext&gt;* | -dirref &lt;DirectoryRef&gt; |
+             -direXclude &lt;exdir&gt;* | -inc # |  
+             -guids |-multiple | -norecurse | -Win64] ) |
+          (-update &lt;file&gt; [-ext &lt;ext&gt;*])
+           
+Required parameters to create a new fragment:
+    -dir &lt;dir&gt;             - The directory to recurse and build the fragment 
+                             from. (short: -d)
+    -custom &lt;value&gt;        - The unique value to apply to all generated 
+                             components, and files. Note all invalid id
+                             characters converted to underscores. (short: -c)
+    &lt;file&gt;                 - The .WXS output file.
+
+Optional parameters when creating a new fragment:
+    -alias &lt;alias&gt;         - The alias to replace the base directory in the 
+                             File element's src attribute. The default is to 
+                             put the full hard coded path. (short: -a)
+    -ext &lt;ext&gt;             - File extensions to exclude. Specify as many -ext
+                             options as necessary. (short: -e)
+    -direXclude &lt;exdir&gt;    - Directories to exclude. There is no wildcard 
+                             matching, only string contains matching. Specify 
+                             as many -direXclude options as necessary.
+                             (short: -x)
+    -dirref &lt;DirectoryRef&gt; - Override the default &lt;DirectoryRef&gt; Id 
+                             attribute.  Default: INSTALLDIR (short: -dr)
+    -inc #                 - The number to add to each directory and component 
+                             elements to allow space for additional items 
+                             later. (short: -i)
+    -guids                 - Generate GUID values for all components. 
+                             (short: -g)
+    -multiple              - Multiple files per component. Defaults to one 
+                             file per component. (short: -m)
+    -norecurse             - Do not recurse directories. Defaults to recursing 
+                             all directories under &lt;dir&gt;. (short: -nr)
+    -Win64                 - If specified, adds Win64="yes" to all components.
+
+Required parameters to update a previously created file:
+    -update                - Indicates you want to update an existing .WXS file
+                             that was created by Paraffin.
+    &lt;file&gt;                 - The .WXS file to process. The updated output is 
+                             written to &lt;file&gt;.PARAFFIN.
+
+Optional parameters when updating a previously created file:
+   -ext &lt;ext &gt;             - File extensions to exclude. Specify as many -ext
+                             options as necessary. Additional ignore extensions
+                             are added to those already specified when the file
+                             was initially created. (short: -e)
+   -direXclude &lt;exdir&gt;     - Directories to exclude. There is no wildcard 
+                             matching, only string contains matching. Specify 
+                             as many -direXclude options as necessary. 
+                             Additional exclude directories are added to those 
+                             already specified when the file was initially 
+                             created. (short: -x)</value>
+  </data>
+</root>
\ No newline at end of file

Added: trunk/Installer/Support/Paraffin/Features.xml
===================================================================
--- trunk/Installer/Support/Paraffin/Features.xml	                        (rev 0)
+++ trunk/Installer/Support/Paraffin/Features.xml	2012-04-22 12:47:10 UTC (rev 6585)
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<FeatureMap>
+  <DirectorySearch Name=".svn" Suffix="_SVNMETADATA" />
+</FeatureMap>
\ No newline at end of file

Added: trunk/Installer/Support/Paraffin/GlobalSuppressions.cs
===================================================================
--- trunk/Installer/Support/Paraffin/GlobalSuppressions.cs	                        (rev 0)
+++ trunk/Installer/Support/Paraffin/GlobalSuppressions.cs	2012-04-22 12:47:10 UTC (rev 6585)
@@ -0,0 +1,9 @@
+// This file is used by Code Analysis to maintain SuppressMessage
+// attributes that are applied to this project. Project-level
+// suppressions either have no target or are given a specific target
+// and scoped to a namespace, type, member, etc.
+//
+// To add a suppression to this file, right-click the message in the
+// Error List, point to "Suppress Message(s)", and click "In Project
+// Suppression File". You do not need to add suppressions to this
+// file manually.

Added: trunk/Installer/Support/Paraffin/Main.cs
===================================================================
--- trunk/Installer/Support/Paraffin/Main.cs	                        (rev 0)
+++ trunk/Installer/Support/Paraffin/Main.cs	2012-04-22 12:47:10 UTC (rev 6585)
@@ -0,0 +1,1386 @@
+//------------------------------------------------------------------------------
+// <copyright company="Wintellect">
+//    Copyright (c) John Robbins/Wintellect -- All rights reserved.
+// </copyright>
+// <Project>
+//    Wintellect Debugging .NET Code
+// </Project>
+//------------------------------------------------------------------------------
+/*------------------------------------------------------------------------------
+ * See the following blog entries for more information about PARAFFIN:
+ * 
+ * http://www.wintellect.com/cs/blogs/jrobbins/archive/2007/10/18/wix-hints-for-new-users-part-1-of-3.aspx
+ * http://www.wintellect.com/cs/blogs/jrobbins/archive/2007/10/19/wix-the-pain-of-wix-part-2-of-3.aspx
+ * http://www.wintellect.com/cs/blogs/jrobbins/archive/2007/10/21/wix-a-better-tallow-paraffin.aspx
+ * 
+ * 1.00 - Initial release
+ * 1.01 - Fixed a bug where directory and component names could have a dash in 
+ *        them, which is not supported by WiX.
+ * 1.02 - Special thanks to Darren Stone for all his input about PARAFFIN.
+ *      - Added -Win64 switch, which adds Win64="yes" to all components.
+ *      - Updated the Id naming to keep all values in the range [0-9a-zA-Z_] to
+ *        avoid any naming problems. WiX is not consistent on exactly what can
+ *        characters can be in the Id attribute.
+ *      - When updating, I was previously only relying on the Directory and 
+ *        File elements Name attribute to find those elements. I mistakenly 
+ *        thought the short file/directory name was guaranteed to be unique.
+ *        I fixed this bug by updating the Directory element searching to look 
+ *        for either the matching Name attribute or LongName attribute depending
+ *        if the long name is different than the short name. For File elements, 
+ *        I look at both the Name and the Source attributes for the exact match.
+ *      - Fixed a bug where I wasn't properly matching directory names when
+ *        generating the Id attribute.
+ *      - Fixed the innocuous bug where I was appending a double slash on an
+ *        alias if the input directory did not end in a trailing slash.
+ * 1.03 - Fixed a bug where I was assuming that the short name for a file was
+ *        constant. It's really a random value. Now I only look at the Source
+ *        attribute when updating a File node as there's no other way to ensure
+ *        that a file is the same. This means I might have a rare conflict with 
+ *        the short name for a file. The big reason for upgrading to WiX 3.0 is
+ *        that you no longer need to mess with these darn short names!
+ * 1.04 - Thanks to Matthew Goos, added the -dirref option to allow a custom 
+ *        name for the DirectoryRef node when creating a file.
+ *      - Now the -ext and direXclude command line options can also be specified 
+ *        for updates in order to add additional extensions or directories to 
+ *        ignore when updating a file.
+ * 3.00 - Sorry for the big version jump but Paraffin now targets WiX 3.0 so I
+ *        thought I'd make them the same. Now that WiX 3.0 has hit beta, it's 
+ *        time to support it. Note that this version no longer will create files
+ *        for use with WiX 2.0. However, it will import and convert previously
+ *        created Paraffin files to WiX 3.0.
+ *        Yay! No more short filenames!
+ *      - When adding files, I now check if they are .DLL, .EXE, or .OCX and if
+ *        so, add CheckSum='yes' attribute to the File element.
+ *      - All command line switches are now case insensitive. I'd forgotten to
+ *        set that in a prior version.
+ -----------------------------------------------------------------------------*/
+namespace Wintellect.Paraffin
+{
+    using System;
+    using System.Collections.Generic;
+    using System.Linq;
+    using System.Text;
+    using System.IO;
+    using System.Xml.Linq;
+    using System.Runtime.InteropServices;
+    using System.Globalization;
+    using System.Diagnostics;
+    using System.Collections;
+    using System.Text.RegularExpressions;
+
+    /// <summary>
+    /// The main program.
+    /// </summary>
+    internal class Program
+    {
+        #region Comment Options Elements
+        // All the elements for the data stored in the comment.
+        private const String CMDLINEOPTIONSELEM = "CommandLineOptions";
+        private const String PRODUCEDBYELEM = "Producer";
+        private const String WARNINGELEM = "WARNING";
+        private const String DATECREATE = "CreatedOn";
+        private const String DIRECTORYELEM = "Directory";
+        private const String CUSTOMELEM = "Custom";
+        private const String ALIASELEM = "DirAlias";
+        private const String INCREMENTELEM = "Increment";
+        private const String GUIDSELEM = "Guids";
+        private const String MULTIPLEELEM = "Multiple";
+        private const String NORECURSELEM = "Norecurse";
+        private const String WIN64ELEM = "Win64";
+        private const String EXTEXCLUDEELEM = "ExtensionExcludes";
+        private const String EXTELEM = "Ext";
+        private const String DIREEXCLUDEELEM = "DirExcludes";
+        private const String DIREXT = "Dir";
+        private const String NEXTDIRECTORYNUMELEM = "NextDirectoryNumber";
+        private const String NEXTCOMPONENTNUMBER = "NextComponentNumber";
+        #endregion
+
+        // The WiX 3.0 namespace.
+        private static XNamespace nsWiX3 =
+                                     "http://schemas.microsoft.com/wix/2006/wi";
+        private static XNamespace nsWiX2 =
+                                  "http://schemas.microsoft.com/wix/2003/01/wi";
+
+        // The argument values used across all the methods.
+        private static ParaffinArgParser argValues;
+
+        // The current directory number.
+        private static Int32 directoryNumber;
+
+        // The starting directory name. I use this to build up unique 
+        // Directory Id values.
+        private static String baseDirectoryName;
+
+        // The full starting directory. If the user wants aliases, I'll replace
+        // this with the alias.
+        private static String fullStartDirectory;
+
+        // The current component number
+        private static Int32 componentNumber;
+
+        // The error message.
+        private static String errorMessage;
+
+        // The input file namespace.
+        private static XNamespace inputNameSpace = nsWiX3;
+
+        // The PE file extensions.
+        private static String [] peFileExtension = { ".DLL" , ".EXE" , ".OCX" };
+
+        internal static Int32 Main ( string [] args )
+        {
+            LoadProcessFeatureMap();
+
+            directoryNumber = 0;
+            componentNumber = 0;
+            errorMessage = String.Empty;
+
+            // Think positive that everything will run completely.
+            Int32 returnValue = 0;
+            argValues = new ParaffinArgParser ( );
+            if ( args.Length > 0 )
+            {
+                Boolean parsed = argValues.Parse ( args );
+                if ( true == parsed )
+                {
+                    if ( true == argValues.Update )
+                    {
+                        returnValue = UpdateExistingFile ( );
+                    }
+                    else
+                    {
+                        returnValue = CreateNewFile ( );
+                    }
+                }
+            }
+            else
+            {
+                argValues.OnUsage ( String.Empty );
+                returnValue = 1;
+            }
+
+            if ( false == String.IsNullOrEmpty ( errorMessage ) )
+            {
+                Console.WriteLine ( errorMessage );
+            }
+
+            return ( returnValue );
+        }
+
+        /// <summary>
+        /// Creates a brand new .WXS file for the specified directory and 
+        /// options. Any previous file of this name is overwritten.
+        /// </summary>
+        /// <returns>
+        /// Zero if the file was all properly written.
+        /// </returns>
+        private static int CreateNewFile ( )
+        {
+            // Create the XML document.
+            XDocument doc = new XDocument ( );
+
+            // Add the WiX and Fragment nodes.
+            XElement root = new XElement ( nsWiX3 + "Wix" );
+            doc.Add ( root );
+            XElement fragment = new XElement ( nsWiX3 + "Fragment" );
+            root.Add ( fragment );
+
+            // Add the DirectoryRef node.
+            XElement directoryRef;
+            if ( true == String.IsNullOrEmpty ( argValues.DirectoryRef ) )
+            {
+                directoryRef = new XElement ( nsWiX3 + "DirectoryRef" ,
+                                          new XAttribute ( "Id" ,
+                                                           "INSTALLDIR" ) );
+            }
+            else
+            {
+                directoryRef = new XElement ( nsWiX3 + "DirectoryRef" ,
+                                           new XAttribute ( "Id" ,
+                                                     argValues.DirectoryRef ) );
+            }
+
+            fragment.Add ( directoryRef );
+
+            // Get the starting directories initialized.
+            InitializeDirectoryValues ( );
+
+            // Now start the grind.
+            RecurseDirectoriesForNewFile ( directoryRef , fullStartDirectory );
+
+            // Add the Component group node.
+            AddComponentGroup ( fragment );
+
+            // Add the comment with all the command line options.
+            AddCommandLineOptionsComment ( root );
+
+            ProcessFeatureMap(doc, Path.GetFileNameWithoutExtension(argValues.FileName) );
+
+            // We're done, save it!
+            doc.Save ( argValues.FileName );
+            return ( 0 );
+        }
+
+        private static Dictionary<string, string> _featureMap = new Dictionary<string, string>();
+
+        private static void LoadProcessFeatureMap()
+        {
+            if (File.Exists("Features.xml"))
+            {
+                var doc = XDocument.Load("Features.xml");
+                var mappings = from item in doc.Descendants("DirectorySearch")
+                               select new
+                               {
+                                   Name = item.Attribute("Name").Value,
+                                   Suffix = item.Attribute("Suffix").Value
+                               };
+                foreach (var m in mappings)
+                    _featureMap[m.Name] = m.Suffix;
+            }
+        }
+
+        /// <summary>
+        /// Performs a search through the given XDocument for Directory fragments
+        /// whose name matches the specified items in the FeatureMap, for each matching
+        /// directory, all of its child elements will be assigned to the specified feature
+        /// </summary>
+        /// <param name="doc"></param>
+        private static void ProcessFeatureMap(XDocument doc, string name)
+        {
+            Dictionary<string, HashSet<string>> componentMaps = new Dictionary<string, HashSet<string>>();
+
+            foreach (var dirName in _featureMap.Keys)
+            {
+                var suffix = _featureMap[dirName];
+                if (!componentMaps.ContainsKey(suffix))
+                    componentMaps[suffix] = new HashSet<string>();
+                Console.WriteLine("Checking if any Directory elements match: " + dirName);
+                var matches = doc.Descendants("{" + nsWiX3 + "}Directory").Where(x => x.Attribute("Name").Value == dirName);
+                foreach (var dir in matches)
+                {
+                    Console.WriteLine("\tProcessing Directory (Id=" + dir.Attribute("Id") + ")");
+                    foreach (var comp in dir.Descendants("{" + nsWiX3 + "}Component"))
+                    {
+                        //Console.WriteLine("\t\tSet Component Feature (Id=" + comp.Attribute("Id") + ") to use feature (" + suffix + ")");
+                        componentMaps[suffix].Add(comp.Attribute("Id").Value);
+                    }
+                }
+            }
+
+            //Remove these collected ids from the existing component group
+            var currentGroup = doc.Descendants("{" + nsWiX3 + "}ComponentGroup").First();
+            var remove = new List<XElement>();
+
+            int moved = 0;
+            //Now write out the component group
+            foreach(var suffix in _featureMap.Values)
+            {
+                if (componentMaps[suffix].Count > 0)
+                {
+                    var groupId = name + suffix;
+
+                    var grp = new XElement("{" + nsWiX3 + "}ComponentGroup");
+                    grp.SetAttributeValue("Id", groupId);
+
+                    foreach (var cid in componentMaps[suffix])
+                    {
+                        var matches = currentGroup.Descendants("{" + nsWiX3 + "}ComponentRef").Where(x => x.Attribute("Id").Value == cid);
+                        remove.AddRange(matches);
+
+                        var el = new XElement("{" + nsWiX3 + "}ComponentRef");
+                        el.SetAttributeValue("Id", cid);
+
+                        grp.Add(el);
+                        moved++;
+                    }
+
+                    //Prepend component group to the fragment
+                    doc.Descendants("{" + nsWiX3 + "}Fragment").First().AddFirst(grp);
+                }
+            }
+
+            //Remove moved componentrefs from original group
+            foreach (var el in remove)
+            {
+                el.Remove();
+            }
+            Console.WriteLine("Moved {0} elements to new ComponentGroup. Removed {1} elements from original ComponentGroup", moved, remove.Count);
+        }
+
+        /// <summary>
+        /// Takes an existing .WXS file and generates an updated version, which
+        /// is saved to a .PARAFFIN extension.
+        /// </summary>
+        /// <returns>
+        /// 0 - The .PARAFFIN file was created.
+        /// 2 - The input file does not have the special comment in the 
+        /// appropriate location.
+        /// </returns>
+        private static int UpdateExistingFile ( )
+        {
+            int returnValue = 0;
+
+            // Load the XML document. Any loading problems go right
+            // to an exception for the user.
+            XDocument inputDoc = XDocument.Load ( argValues.FileName );
+
+            XAttribute ns = inputDoc.Root.Attribute ( "xmlns" );
+            if ( 0 == String.Compare ( nsWiX2.ToString ( ) ,
+                                       ns.Value ,
+                                       StringComparison.OrdinalIgnoreCase ) )
+            {
+                inputNameSpace = nsWiX2;
+            }
+
+            // The output filename.
+            String outputFile = Path.ChangeExtension ( argValues.FileName ,
+                                                       ".PARAFFIN" );
+
+            // The first node has to be comment I put there when 
+            // the file was created.
+            XComment options = inputDoc.Root.FirstNode as XComment;
+            if ( null != options )
+            {
+                // It's a comment node, so set all the arguments from that 
+                // section.
+                InitializeArgumentsFromFile ( options.Value );
+
+                // Create the new output file.
+                XDocument outputDoc = new XDocument ( );
+
+                // Add the WiX and Fragment nodes.
+                XElement outputRoot = new XElement ( nsWiX3 + "Wix" );
+                outputDoc.Add ( outputRoot );
+                XElement outputFragment = new XElement ( nsWiX3 + "Fragment" );
+                outputRoot.Add ( outputFragment );
+
+                // Find the directory ref of the input file.
+                XElement inputDirRef = inputDoc.Descendants (
+                                            inputNameSpace +
+                                                     "DirectoryRef" ).First ( );
+                String idValue =
+                                inputDirRef.Attributes ( "Id" ).First ( ).Value;
+
+                // Build a DirectoryRef for the output file.
+                XElement outputDirRef = new XElement ( nsWiX3 + "DirectoryRef" ,
+                                            new XAttribute ( "Id" , idValue ) );
+
+                // Add the directory ref to the output file.
+                outputFragment.Add ( outputDirRef );
+
+                // Get the starting directory values ready to go.
+                InitializeDirectoryValues ( );
+
+                // Recurse through the input file and the directories 
+                // themselves.
+                RecurseDirectoriesForExistingFile ( inputDirRef ,
+                                                    outputDirRef ,
+                                                    fullStartDirectory );
+
+                // Add the Component group node.
+                AddComponentGroup ( outputFragment );
+
+                // Add the comment with all the command line options.
+                AddCommandLineOptionsComment ( outputRoot );
+
+                ProcessFeatureMap(outputDoc, Path.GetFileNameWithoutExtension(outputFile));
+
+                // All OK, Jumpmaster!
+                outputDoc.Save ( outputFile );
+            }
+            else
+            {
+                // This does not look like a file this tool previously 
+                // generated.
+                errorMessage = Constants.UnknownFileType;
+                returnValue = 2;
+            }
+
+            return ( returnValue );
+        }
+
+        /// <summary>
+        /// Does the work of recursing both the file system directories and the 
+        /// original .WXS file to produce an updated XML document.
+        /// </summary>
+        /// <param name="currInputElement">
+        /// The current element in the input .WXS file.
+        /// </param>
+        /// <param name="currOutputElement">
+        /// The current element in the output .PARAFFIN file.
+        /// </param>
+        /// <param name="directory">
+        /// The directory to process. This has to be the full directory value.
+        /// </param>
+        /// <remarks>
+        /// As you can guess, this is called recursively.
+        /// </remarks>
+        private static void
+                RecurseDirectoriesForExistingFile ( XElement currInputElement ,
+                                                    XElement currOutputElement ,
+                                                    String directory )
+        {
+            // If the currInputElement is null, I'm processing a brand new 
+            // directory that isn't in the original file. Thus, I can treat 
+            // adding this directory just like it's a new file and add this 
+            // directory, plus all under it.
+            if ( null == currInputElement )
+            {
+                RecurseDirectoriesForNewFile ( currOutputElement , directory );
+            }
+            else
+            {
+                // The directory element I'm going to be building up.
+                XElement outputDirElement;
+
+                // Get the directory info in order to get just the name.
+                DirectoryInfo info = new DirectoryInfo ( directory );
+                String name = info.Name;
+
+                String matchAttrib = "Name";
+                // If this is a WiX 2.0 input file, I need to match on LongName
+                // instead if the name is more than 8 characters.
+                if ( ( inputNameSpace == nsWiX2 ) && ( name.Length > 8 ) ) 
+                {
+                    matchAttrib = "LongName";
+                }
+
+                // Does this directory already exist in the input file? 
+                var qFindDirectory = from elem in currInputElement.Elements ( )
+                                     where
+                           ( (string)elem.Attribute ( matchAttrib ) == name )
+                                     select elem;
+
+                XElement inputDirElement = null;
+                int fileCount = qFindDirectory.Count ( );
+                Debug.Assert ( fileCount <= 1 , "fileCount <= 1" );
+                if ( fileCount > 1 )
+                {
+                    // We've got a serious problem. :( You can't have multiple
+                    // directories with the same name.
+                    String err = String.Format ( CultureInfo.CurrentCulture ,
+                                        Constants.InvalidFileNameCountFmt ,
+                                                 name );
+                    throw new InvalidOperationException ( err );
+                }
+                else if ( 0 == fileCount )
+                {
+                    // This is a new directory.
+                    outputDirElement = CreateDirectoryElement ( directory );
+                }
+                else
+                {
+                    // We've got one element so grab it.
+                    inputDirElement = qFindDirectory.First ( );
+
+                    if ( inputNameSpace == nsWiX2 )
+                    {
+                        // In case the input file was created with the 
+                        // version of Paraffin that supported WiX 2.0, I 
+                        // want to strip off the LongName attribute 
+                        // as it is no longer needed.
+                        inputDirElement.SetAttributeValue ( "LongName" , null );
+                        // Since WiX3 uses the Name attribute, I willl change
+                        // the existing Name attribute to the long name so
+                        // it gets copied below. This is the long name now.
+                        inputDirElement.SetAttributeValue ( "Name" , name );
+                    }
+
+                    // This directory was in the previous file so copy it's 
+                    // attributes over to the new file.
+                    outputDirElement = new XElement ( nsWiX3 + "Directory" );
+                    foreach ( var attrib in inputDirElement.Attributes ( ) )
+                    {
+                        outputDirElement.SetAttributeValue ( attrib.Name ,
+                                                             attrib.Value );
+                    }
+
+                }
+
+                // Add this element to the output element.
+                currOutputElement.Add ( outputDirElement );
+
+                // Process all the files in this directory as compared to the 
+                // input file.
+                UpdateFilesInDirectoryNode ( directory ,
+                                             inputDirElement ,
+                                             outputDirElement );
+
+                // Recurse directories if the original file had that set.
+                if ( false == argValues.NoDirectoryRecursion )
+                {
+                    String [] dirs = Directory.GetDirectories ( directory );
+                    foreach ( var item in dirs )
+                    {
+                        // Is this a directory the user wanted to skip?
+                        Boolean skipDirectory = IsDirectoryExcluded ( item );
+                        if ( false == skipDirectory )
+                        {
+                            RecurseDirectoriesForExistingFile ( 
+                                                            inputDirElement ,
+                                                            outputDirElement ,
+                                                            item );
+                        }
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// Looks at the input .WXS for the files in this directory and compare
+        /// it to the files on disk. If the file is the same or is a new file,
+        /// add the files to the output. If it's no longer present on disk, but
+        /// is in the input .WXS, skip adding the file to the output.
+        /// </summary>
+        /// <param name="directory">The disk directory to scan.</param>
+        /// <param name="inputDir">The Directory element from the .WXS file that
+        /// maps to <see cref="directory"/>.</param>
+        /// <param name="outputDir">The Directory element for the output 
+        /// .PARAFFIN file.</param>
+        /// <exception cref="InvalidOperationException">
+        /// Thrown if there's multiple files with the same name in the <paramref
+        /// name="outputDir"/> child elements.
+        /// </exception>
+        private static void UpdateFilesInDirectoryNode ( String directory ,
+                                                         XElement inputDir ,
+                                                         XElement outputDir )
+        {
+            // If the inputDir element is null, just treat this as if I was 
+            // creating a new file.
+            if ( null == inputDir )
+            {
+                AddNewFilesToDirectoryNode ( directory , outputDir );
+            }
+            else
+            {
+                // The .WXS file had files for this directory so I need to 
+                // match them all up with the existing files.
+
+                // Start by getting the files in this directory.
+                String [] files = Directory.GetFiles ( directory );
+
+                // Skip all those that have extension the user does not want.
+                var filesQuery = from file in files
+                                 where false == argValues.ExtensionList.
+                                        ContainsKey ( Path.GetExtension ( file )
+                                                         .ToUpperInvariant ( ) )
+                                 select file;
+
+                // If there's no files in this directory, there's nothing else 
+                // to do.
+                if ( 0 != filesQuery.Count ( ) )
+                {
+                    // I'll add to the output .WXS file current Directory node 
+                    // by default.
+                    XElement addToElement = outputDir;
+
+                    // Are we doing multiple files per component?
+                    if ( true == argValues.MultipleFilesPerComponent )
+                    {
+                        // Copy over the one component node. If you just add 
+                        // the node directly, that copies over all the child 
+                        // nodes as well so I need duplicate just the component
+                        // node itself.
+                        XElement intputCompElem =
+                                      inputDir.Element (
+                                                 inputNameSpace + "Component" );
+                        XElement outputCompElem =
+                                          new XElement ( nsWiX3 + "Component" );
+                        foreach ( var attrib in intputCompElem.Attributes ( ) )
+                        {
+                            outputCompElem.SetAttributeValue ( attrib.Name ,
+                                                               attrib.Value );
+                        }
+
+                        // Add the Component node on. 
+                        outputDir.Add ( outputCompElem );
+
+                        // Point to the component where I'll be adding all the
+                        // file nodes.
+                        addToElement = outputCompElem;
+                    }
+
+                    // First get the child component(s) from this Directory.
+                    var comps = inputDir.Elements (
+                                                 inputNameSpace + "Component" );
+
+                    // Now get all the files from just these Component 
+                    // elements.
+                    var inputFiles = comps.Descendants (
+                                                      inputNameSpace + "File" );
+
+                    // Loop through all the files on disk.
+                    foreach ( var file in filesQuery )
+                    {
+                        // Holds the element I'm going to add to the current
+                        // output XML document.
+                        XElement newOutputElem;
+
+                        // Get the aliased value for this file.
+                        String aliasedName = BuildAliasedFilename ( file );
+
+                        // See if I can find that file in the input .WXS file 
+                        // by checking the aliased Source names.
+                        var inputFileQuery = from fileNode in inputFiles
+                                             where
+                          ( ( (string)fileNode.Attribute ( "Source" ) )
+                                                               == aliasedName )
+                                             select fileNode;
+                        int fileCount = inputFileQuery.Count ( );
+                        Debug.Assert ( fileCount <= 1 , "fileCount <= 1" );
+                        if ( 0 == fileCount )
+                        {
+                            XElement comp = null;
+                            if ( false == argValues.MultipleFilesPerComponent )
+                            {
+                                // Put this file element in it's own Component.
+                                // The component always has to be created first.
+                                // so that the component and file are using the
+                                // same unique number.
+                                comp = CreateComponentElement ( );
+                            }
+
+                            // This is a new file that wasn't in the input .WXS
+                            // file so just add it. First create a new File 
+                            // element.
+                            newOutputElem = CreateFileElement ( file );
+
+                            // Did I create a component for this file?
+                            if ( null != comp )
+                            {
+                                // Add the file to this component.
+                                comp.Add ( newOutputElem );
+
+                                // Point at the component to add.
+                                newOutputElem = comp;
+                            }
+                        }
+                        else if ( 1 == fileCount )
+                        {
+                            newOutputElem = inputFileQuery.First ( );
+
+                            // In case the input file was created with the 
+                            // version of Paraffin that supported WiX 2.0, I 
+                            // want to strip off the Name and LongName 
+                            // attributes as they are no longer needed.
+                            newOutputElem.SetAttributeValue ( "Name" , null );
+                            newOutputElem.SetAttributeValue ( "LongName" ,
+                                                              null );
+
+
+                            // Is it one file per component? 
+                            if ( false == argValues.MultipleFilesPerComponent )
+                            {
+                                // If this is a WiX 3.0 .WXS file I can simply
+                                // cheat and make the newOutputElem actually
+                                // point  to the Component element. That way I 
+                                // just add the Component and the child File
+                                // element in one swoop.
+                                if ( inputNameSpace == nsWiX3 )
+                                {
+                                    newOutputElem = newOutputElem.Parent;
+                                }
+                                else
+                                {
+                                    // This is a fragment from WiX 2.0 being 
+                                    // updated to 3.0. Because of the namespace
+                                    // being different, I need to copy the 
+                                    // attributes manually.
+                                    XElement comp = new XElement (
+                                                         nsWiX3 + "Component" );
+                                    foreach ( var attrib in
+                                           newOutputElem.Parent.Attributes ( ) )
+                                    {
+                                        comp.SetAttributeValue ( attrib.Name ,
+                                                                 attrib.Value );
+                                    }
+
+                                    // Create the WiX 3 version of this WiX 2 
+                                    // file node.
+                                    XElement newFile = 
+                                        CreateWiX3FileFromWix2File ( 
+                                                                newOutputElem );
+                                    // Add the file to the component.
+                                    comp.Add ( newFile );
+                                    // Set the output element to the component.
+                                    newOutputElem = comp;
+                                }
+                            }
+                            else if ( inputNameSpace == nsWiX2 )
+                            {
+                                // It's multiple files per component and this is
+                                // a WiX 2.0 input file. Copy the attributes
+                                // over manually to keep the namespaces
+                                // straight. Now newOutput points to the WiX 3.0
+                                // element.
+                                newOutputElem = CreateWiX3FileFromWix2File ( 
+                                                                newOutputElem );
+                            }
+                        }
+                        else
+                        {
+                            // There's multiple files with the same name in this
+                            // particular node. That's bad. :(
+                            String err = String.Format (
+                                        CultureInfo.CurrentCulture ,
+                                        Constants.InvalidFileNameCountFmt ,
+                                        file );
+                            throw new InvalidOperationException ( err );
+                        }
+
+                        // Add the file element to the parent node.
+                        addToElement.Add ( newOutputElem );
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// Called when processing a new file or a directory that's wasn't seen
+        /// in the existing .WXS when updating.
+        /// </summary>
+        /// <param name="currElement">
+        /// The current element in the output XML document.
+        /// </param>
+        /// <param name="directory">
+        /// The disk directory to recurse.
+        /// </param>
+        private static void RecurseDirectoriesForNewFile ( XElement currElement ,
+                                                          String directory )
+        {
+            // It's new so create a Directory element.
+            XElement directoryNode = CreateDirectoryElement ( directory );
+
+            // Add the current directory to the passed in element
+            currElement.Add ( directoryNode );
+
+            // Add the files to this directory node.
+            AddNewFilesToDirectoryNode ( directory , directoryNode );
+
+            // Recurse the directories if I'm supposed to do so.
+            if ( false == argValues.NoDirectoryRecursion )
+            {
+                String [] dirs = Directory.GetDirectories ( directory );
+                foreach ( var item in dirs )
+                {
+                    Boolean skipDirectory = IsDirectoryExcluded ( item );
+                    if ( false == skipDirectory )
+                    {
+                        RecurseDirectoriesForNewFile ( directoryNode , item );
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// For new directories when creating new files or when adding new
+        /// directories when processing an existing .WXS, adds the files
+        /// to the <see cref="directoryElem"/> element.
+        /// </summary>
+        /// <param name="directory">
+        /// The directory to get the files from.
+        /// </param>
+        /// <param name="directoryElem">
+        /// The Director element to add the new Component/File elements to.
+        /// </param>
+        private static void AddNewFilesToDirectoryNode ( String directory ,
+                                                        XElement directoryElem )
+        {
+            // Get the files in this directory.
+            String [] files = Directory.GetFiles ( directory );
+
+            // Only do the work if there are some files in the directory.
+            if ( files.Length > 0 )
+            {
+                // Skip all those that have extensions the user does not want.
+                var filesQuery = from file in files
+                                 where false == argValues.ExtensionList.
+                                        ContainsKey ( Path.GetExtension ( file )
+                                                         .ToUpperInvariant ( ) )
+                                 select file;
+
+                // Create the first Component element. Only add this node to the
+                // directory node if the user wants multiple files per component 
+                // node. 
+                XElement currentComponent = CreateComponentElement ( );
+                if ( true == argValues.MultipleFilesPerComponent )
+                {
+                    directoryElem.Add ( currentComponent );
+                }
+
+                // For each file on disk.
+                foreach ( var file in filesQuery )
+                {
+                    // Create the File element and add it to the current
+                    // Component element.
+                    XElement fileElement = CreateFileElement ( file );
+                    currentComponent.Add ( fileElement );
+                    if ( false == argValues.MultipleFilesPerComponent )
+                    {
+                        directoryElem.Add ( currentComponent );
+                        currentComponent = CreateComponentElement ( );
+                    }
+                }
+
+                // I'm done with this directory so bump up the component and 
+                // directory count if the user asked for that to happen.
+                componentNumber += ( argValues.IncrementValue - 2 );
+            }
+        }
+
+        /// <summary>
+        /// Initializes the <see cref="ParaffinArgParser"/> with all the
+        /// settings from the first comment block. Used when reading in a .WXS
+        /// to compare to the files on the disk.
+        /// </summary>
+        /// <param name="inputXml">
+        /// The XML string to process.
+        /// </param>
+        private static void InitializeArgumentsFromFile ( string inputXml )
+        {
+            XElement options = XElement.Parse ( inputXml );
+
+            // Save off the settings from the command line.
+            ParaffinArgParser originalArgs = argValues;
+
+            // Start the arguments from the comment section.
+            argValues = new ParaffinArgParser ( );
+
+            // Get all the easy values out.
+            argValues.CustomValue =
+                           options.Descendants ( CUSTOMELEM ).First ( ).Value;
+
+            argValues.Alias =
+                            options.Descendants ( ALIASELEM ).First ( ).Value;
+
+            argValues.StartDirectory =
+                        options.Descendants ( DIRECTORYELEM ).First ( ).Value;
+
+            argValues.IncrementValue = Convert.ToInt32 (
+                      options.Descendants ( INCREMENTELEM ).First ( ).Value ,
+                                            CultureInfo.InvariantCulture );
+
+            argValues.GenerateGuids = Convert.ToBoolean (
+                          options.Descendants ( GUIDSELEM ).First ( ).Value ,
+                                                 CultureInfo.InvariantCulture );
+
+            argValues.MultipleFilesPerComponent = Convert.ToBoolean (
+                       options.Descendants ( MULTIPLEELEM ).First ( ).Value ,
+                                             CultureInfo.InvariantCulture );
+
+            argValues.NoDirectoryRecursion = Convert.ToBoolean (
+                       options.Descendants ( NORECURSELEM ).First ( ).Value ,
+                                            CultureInfo.InvariantCulture );
+
+            directoryNumber = Convert.ToInt32 (
+               options.Descendants ( NEXTDIRECTORYNUMELEM ).First ( ).Value ,
+                                     CultureInfo.InvariantCulture );
+
+            componentNumber = Convert.ToInt32 (
+                options.Descendants ( NEXTCOMPONENTNUMBER ).First ( ).Value ,
+                                      CultureInfo.InvariantCulture );
+
+            var extNode = options.Descendants ( EXTEXCLUDEELEM );
+            foreach ( var item in extNode.Descendants ( ) )
+            {
+                argValues.ExtensionList.Add ( item.Value , true );
+            }
+
+            var dirEx = options.Descendants ( DIREEXCLUDEELEM );
+            foreach ( var item in dirEx.Descendants ( ) )
+            {
+                argValues.DirectoryExcludeList.Add ( item.Value );
+            }
+
+            // After releasing 1.0, I've added a few command line options. 
+            // Since I don't want to break existing PARAFFIN generated files,
+            // I'll not require the following options to be in existing files.
+            // If they are cool, but no sense crashing out if they aren't.
+            var win64Elems = options.Descendants ( WIN64ELEM );
+            if ( win64Elems.Count ( ) == 1 )
+            {
+                // Grab the value.
+                argValues.Win64 = Convert.ToBoolean (
+                                                 win64Elems.First ( ).Value ,
+                                                 CultureInfo.InvariantCulture );
+            }
+            else
+            {
+                // Assume false.
+                argValues.Win64 = false;
+            }
+
+            // Now that everything is read out of the original options block,
+            // add in any additional -ext and -dirExclude options specified on 
+            // the command line.
+            foreach ( var cmdLineExt in originalArgs.ExtensionList.Keys )
+            {
+                if ( false ==
+                            argValues.ExtensionList.ContainsKey ( cmdLineExt ) )
+                {
+                    argValues.ExtensionList.Add ( cmdLineExt , true );
+                }
+            }
+            foreach ( var dirExclude in originalArgs.DirectoryExcludeList )
+            {
+                if ( false ==
+                        argValues.DirectoryExcludeList.Contains ( dirExclude ) )
+                {
+                    argValues.DirectoryExcludeList.Add ( dirExclude );
+                }
+            }
+        }
+
+        /// <summary>
+        /// Adds the command line options as the first comment element under
+        /// the WiX element.
+        /// </summary>
+        /// <param name="wixElement">
+        /// The WiX element to add to.
+        /// </param>
+        private static void AddCommandLineOptionsComment ( XElement wixElement )
+        {
+            // Create the XML data for the easy stuff.
+            XElement initOptions =
+                new XElement ( CMDLINEOPTIONSELEM ,
+                    new XElement ( PRODUCEDBYELEM ,
+                                   Constants.CommentProducer ) ,
+                    new XElement ( WARNINGELEM , Constants.CommentWarning ) ,
+                    new XElement ( DATECREATE ,
+                                   DateTime.Now.ToString ( "g" ,
+                                                CultureInfo.CurrentCulture ) ) ,
+                    new XElement ( DIRECTORYELEM ,
+                                   argValues.StartDirectory ) ,
+                    new XElement ( CUSTOMELEM , argValues.CustomValue ) ,
+                    new XElement ( ALIASELEM , argValues.Alias ) ,
+                    new XElement ( INCREMENTELEM ,
+                                   argValues.IncrementValue ) ,
+                    new XElement ( GUIDSELEM , argValues.GenerateGuids ) ,
+                    new XElement ( WIN64ELEM , argValues.Win64 ) ,
+                    new XElement ( MULTIPLEELEM ,
+                                   argValues.MultipleFilesPerComponent ) ,
+                    new XElement ( NORECURSELEM ,
+                                   argValues.NoDirectoryRecursion ) );
+
+            // Add the file extension exclusions.
+            XElement extList = new XElement ( EXTEXCLUDEELEM );
+            foreach ( var item in argValues.ExtensionList )
+            {
+                extList.Add ( new XElement ( EXTELEM , item.Key ) );
+            }
+
+            initOptions.Add ( extList );
+
+            // Add the directory exclusions.
+            XElement dirExList = new XElement ( DIREEXCLUDEELEM );
+            foreach ( var item in argValues.DirectoryExcludeList )
+            {
+                dirExList.Add ( new XElement ( DIREXT , item.ToString ( ) ) );
+            }
+
+            initOptions.Add ( dirExList );
+
+            // Add the next directory number and component number so we don't
+            // overwrite an existing value.
+            XElement dirNum = new XElement ( NEXTDIRECTORYNUMELEM ,
+                                             directoryNumber );
+            initOptions.Add ( dirNum );
+            XElement compNum = new XElement ( NEXTCOMPONENTNUMBER ,
+                                              componentNumber );
+            initOptions.Add ( compNum );
+
+            // Add the XML comment.
+            XComment comment = new XComment ( initOptions.ToString ( ) );
+            wixElement.AddFirst ( comment );
+        }
+
+        /// <summary>
+        /// Adds the ComponentGroup as the first child to the Fragment element.
+        /// </summary>
+        /// <param name="fragment">
+        /// The Fragment element.
+        /// </param>
+        private static void AddComponentGroup ( XElement fragment )
+        {
+            // Grab all the Component elements and sort them by the ID 
+            // attribute.
+            var compNodes = from node in fragment.
+                                            Descendants ( nsWiX3 + "Component" )
+                            select node;
+
+            // Sort them in logical order because I'm a little bit obsessive.
+            compNodes = compNodes.OrderBy (
+                                       n => (string)n.Attribute ( "Id" ).Value ,
+                                       new LogicalStringComparer ( ) );
+
+            StringBuilder sb = new StringBuilder ( 70 );
+            sb.AppendFormat ( "group_{0}" , argValues.CustomValue );
+
+            // Ensure that all invalid characters are stripped from the ID.
+            String id = RemoveInvalidIdCharacters ( sb.ToString ( ) );
+
+            XElement groupNode = new XElement ( nsWiX3 + "ComponentGroup" ,
+                                                new XAttribute ( "Id" , id ) );
+            foreach ( var component in compNodes )
+            {
+                XElement refNode = new XElement ( nsWiX3 + "ComponentRef" ,
+                                        new XAttribute ( "Id" ,
+                                         component.Attribute ( "Id" ).Value ) );
+                groupNode.Add ( refNode );
+            }
+
+            // Add the group node as the first child of the outputFragment only
+            // if there are some components. It's perfectly reasonable to have
+            // a fragment made up of nothing but directories.
+            if ( 0 != compNodes.Count ( ) )
+            {
+                fragment.AddFirst ( groupNode );
+            }
+        }
+
+        /// <summary>
+        /// Gets the directory values initialized so the code can handle aliases
+        /// and the individual directories.
+        /// </summary>
+        private static void InitializeDirectoryValues ( )
+        {
+            fullStartDirectory = Path.GetFullPath ( argValues.StartDirectory );
+            DirectoryInfo info = new DirectoryInfo ( fullStartDirectory );
+            baseDirectoryName = info.Name;
+        }
+
+        /// <summary>
+        /// MSI only accepts IDs that are 72 characters long so I need to ensure
+        /// that the strings I use are within that limit.
+        /// </summary>
+        /// <param name="start">
+        /// The initial part of the string.
+        /// </param>
+        /// <param name="main">
+        /// The main part of the string.
+        /// </param>
+        /// <param name="uniqueId">
+        /// The unique value to append to this string.
+        /// </param>
+        /// <returns>
+        /// A unique string that is only 70 characters long and has all invalid
+        /// characters stripped.
+        /// </returns>
+        private static String CreateSeventyCharIdString ( String start ,
+                                                          String main ,
+                                                          int uniqueId )
+        {
+            const String FormatStr = "{0}_{1}_{2}";
+            const Int32 MaxLen = 70;
+
+            String uniqueStr = String.Format ( CultureInfo.InvariantCulture ,
+                                              "{0}" ,
+                                              uniqueId );
+            StringBuilder sb = new StringBuilder ( 100 );
+            sb.AppendFormat ( FormatStr , start , main , uniqueStr );
+            if ( sb.Length > MaxLen )
+            {
+                sb.Length = 0;
+                int idLen = uniqueStr.Length;
+                int startLen = start.Length;
+                int len = Math.Min ( main.Length ,
+                                     MaxLen - ( idLen + startLen ) );
+                String sub = main.Substring ( 0 , len );
+                sb.AppendFormat ( FormatStr ,
+                                  start ,
+                                  sub ,
+                                  uniqueStr );
+            }
+
+            // Turns out id strings in WiX cannot have dashes in them so 
+            // convert them to underscores.
+            String retVal = RemoveInvalidIdCharacters ( sb.ToString ( ) );
+            return ( retVal );
+        }
+
+        /// <summary>
+        /// Looks through the list of directory exclusions and returns true
+        /// if this directory is supposed to be excluded.
+        /// </summary>
+        /// <param name="directory">
+        /// The directory to check if it's got any excluded value in it.
+        /// </param>
+        /// <returns>
+        /// True  - Supposed to exclude and skip.
+        /// False - Process this directory.
+        /// </returns>
+        private static Boolean IsDirectoryExcluded ( String directory )
+        {
+            Boolean skipDirectory = false;
+
+            // If the user wanted to skip some directories, check to see
+            // if this happens to be one.
+            for ( int i = 0 ; i < argValues.DirectoryExcludeList.Count ; i++ )
+            {
+                if ( true == directory.Contains (
+                                argValues.DirectoryExcludeList [ i ] ) )
+                {
+                    skipDirectory = true;
+                    break;
+                }
+            }
+
+            return ( skipDirectory );
+        }
+
+        /// <summary>
+        /// Returns a unique name for the Directory Id attribute.
+        /// </summary>
+        /// <param name="directory">
+        /// The full directory to process.
+        /// </param>
+        /// <returns>
+        /// A string that encapsulates the directory name with a unique value
+        /// appended.
+        /// </returns>
+        private static String GenerateUniqueDirectoryIdName ( String directory )
+        {
+            // To make the Id a bit easier to read, I'm going to use a naming 
+            // scheme of "dir_<Path>_<Num>". That will put some uniqueness on 
+            // the names so they don't conflict across large installs.
+
+            // Suffix the directory with \ to ensure I find the exact match 
+            // when looking the base directory.
+            if ( false == directory.EndsWith ( "\\" ,
+                                          StringComparison.OrdinalIgnoreCase ) )
+            {
+                directory += "\\";
+            }
+
+            // Figure out where the base directory name is in this string and
+            // create a unique name for the Id attribute.
+            String exactBaseDirectory =
+                        String.Format ( CultureInfo.InvariantCulture ,
+                                        "\\{0}\\" ,
+                                        baseDirectoryName );
+            Int32 startBaseDir = directory.IndexOf ( exactBaseDirectory ,
+                                           StringComparison.OrdinalIgnoreCase );
+
+            // Get the real value and skip the preceding \\ used to find the
+            // exact base.
+            String dirIdString = directory.Substring ( startBaseDir + 1 );
+            if ( '\\' == dirIdString [ dirIdString.Length - 1 ] )
+            {
+                dirIdString = dirIdString.Substring ( 0 ,
+                                                       dirIdString.Length - 1 );
+            }
+
+            dirIdString = dirIdString.Replace ( '\\' , '.' );
+            dirIdString = CreateSeventyCharIdString ( "dir" ,
+                                                    dirIdString ,
+                                                    directoryNumber );
+
+            // Since I've used this directoryNumber, time to bump it up.
+            directoryNumber += argValues.IncrementValue;
+
+            return ( dirIdString );
+        }
+
+        /// <summary>
+        /// Creates a new Directory element.
+        /// </summary>
+        /// <param name="directory">
+        /// The file directory for this element.
+        /// </param>
+        /// <returns>
+        /// A constructed <see cref="XElement"/>.
+        /// </returns>
+        private static XElement CreateDirectoryElement ( String directory )
+        {
+            // Each directory element needs a unique value.
+            String uniqueDirID = GenerateUniqueDirectoryIdName ( directory );
+
+            // Get the long and short names for this directory.
+            DirectoryInfo info = new DirectoryInfo ( directory );
+
+            // I've got enough to create the Directory node.
+            XElement directoryNode = new XElement ( nsWiX3 + "Directory" ,
+                                        new XAttribute ( "Id" , uniqueDirID ) ,
+                                        new XAttribute ( "Name" , info.Name ) );
+            return ( directoryNode );
+        }
+
+        /// <summary>
+        /// Creates a File element for the file in <paramref name="fileName"/>.
+        /// </summary>
+        /// <param name="fileName">
+        /// The full filename to process.
+        /// </param>
+        /// <returns>
+        /// A valid <see cref="XElement"/> for the File element.
+        /// </returns>
+        private static XElement CreateFileElement ( String fileName )
+        {
+            // Create a unique filename. In a one file per component run, this
+            // will mean that the file and it's parent component will have the 
+            // same number.
+            String fileId = CreateSeventyCharIdString ( "file" ,
+                                                     argValues.CustomValue ,
+                                                     componentNumber - 1 );
+
+            // If the user wanted to group all the files into a component, I
+            // need to bump up the componentNumber to keep everything straight.
+            if ( true == argValues.MultipleFilesPerComponent )
+            {
+                componentNumber++;
+            }
+
+            XElement file = new XElement ( nsWiX3 + "File" ,
+                                           new XAttribute ( "Id" , fileId ) );
+            if ( true == IsPEFile ( fileName ) )
+            {
+                file.Add ( new XAttribute ( "Checksum" , "yes" ) );
+            }
+            fileName = BuildAliasedFilename ( fileName );
+            file.Add ( new XAttribute ( "Source" , fileName ) );
+            return ( file );
+        }
+
+        /// <summary>
+        /// Creates a WiX 3 File Element from a WiX 2 File Element.
+        /// </summary>
+        /// <param name="input">
+        /// The WiX 2.0 file element.
+        /// </param>
+        /// <returns>
+        /// The WiX 3.0 file element.
+        /// </returns>
+        private static XElement CreateWiX3FileFromWix2File ( XElement input )
+        {
+
+            XElement newFile = new XElement ( nsWiX3 + "File" );
+            foreach ( var attrib in input.Attributes ( ) )
+            {
+                newFile.SetAttributeValue ( attrib.Name , attrib.Value );
+            }
+
+            // If it's a PE binary file, add on the Checksum attribute.
+            if ( true == IsPEFile ( newFile.Attribute ( "Source" ).Value ) )
+            {
+                newFile.SetAttributeValue ( "Checksum" ,
+                                            "yes" );
+            }
+            return ( newFile );
+        }
+
+        /// <summary>
+        /// Creates a standard Component element.
+        /// </summary>
+        /// <returns>
+        /// A newly created and unique Component elements.
+        /// </returns>
+        private static XElement CreateComponentElement ( )
+        {
+            // Make sure the Id field is less than or equal to 70 characters.
+            String componentId = CreateSeventyCharIdString ( "comp" ,
+                                                     argValues.CustomValue ,
+                                                     componentNumber );
+
+            // Increment since I just used that number.
+            componentNumber++;
+
+            String guidString = "PUT-GUID-HERE";
+            if ( true == argValues.GenerateGuids )
+            {
+                Guid g = Guid.NewGuid ( );
+                guidString = g.ToString ( ).ToUpperInvariant ( );
+            }
+
+            XElement comp = new XElement ( nsWiX3 + "Component" ,
+                                   new XAttribute ( "Id" , componentId ) ,
+                                   new XAttribute ( "DiskId" , "1" ) ,
+                                   new XAttribute ( "KeyPath" , "yes" ) ,
+                                   new XAttribute ( "Guid" , guidString ) );
+
+            // Does the user want the Win64 attribute?
+            if ( true == argValues.Win64 )
+            {
+                comp.SetAttributeValue ( "Win64" , "yes" );
+            }
+
+            return ( comp );
+        }
+
+        private static String BuildAliasedFilename ( String fileName )
+        {
+            // Does the user want an alias for the base directory name?
+            if ( false == String.IsNullOrEmpty ( argValues.Alias ) )
+            {
+                fileName = fileName.Replace ( fullStartDirectory ,
+                                              argValues.Alias );
+            }
+
+            return ( fileName );
+        }
+
+        private static Boolean IsPEFile ( String fileName )
+        {
+            String ext = Path.GetExtension ( fileName );
+            ext = ext.ToUpper ( CultureInfo.CurrentCulture );
+
+            for ( int i = 0 ; i < peFileExtension.Length ; i++ )
+            {
+                if ( 0 == String.Compare ( ext ,
+                                           peFileExtension[i] ,
+                                           true ,
+                                           CultureInfo.CurrentCulture ) )
+                {
+                    return ( true );
+                }
+            }
+            return ( false );
+        }
+
+        private static String RemoveInvalidIdCharacters ( String input )
+        {
+            // WiX 2.0 does not document the actual valid characters in an Id
+            // attribute. This is especially true in Directory elements. What
+            // I'll do here is replace everything that's not in the range
+            // [0-9a-zA-Z_] with underscores. While that might make some of the
+            // Ids harder to read, I'm assured CANDLE.EXE will compile the
+            // fragment.
+            return ( Regex.Replace ( input , "[^0-9a-zA-Z_]" , "_" ) );
+        }
+
+        private static class NativeMethods
+        {
+            private const int MaxPath = 255;
+
+            [DllImport ( "shlwapi.dll" ,
+                         CharSet = CharSet.Unicode ,
+                         ExactSpelling = true )]
+            internal static extern int StrCmpLogicalW ( String x , String y );
+
+        }
+
+        /// <summary>
+        ///  Used to sort values in strings in logical order.
+        /// </summary>
+        private class LogicalStringComparer : IComparer<String>
+        {
+            /// <summary>
+            /// Calls the native logical string compare method.
+            /// </summary>
+            /// <param name="x">
+            /// The first string to compare.
+            /// </param>
+            /// <param name="y">
+            /// The second string to compare.
+            /// </param>
+            /// <returns>
+            /// Zero if the strings are identical, 1 if 
+            /// <paramref name="x"/> is greater, -1 if 
+            /// <paramref name="y"/> is greater.
+            /// </returns>
+            public int Compare ( string x , string y )
+            {
+                return ( NativeMethods.StrCmpLogicalW ( x , y ) );
+            }
+        }
+    }
+}

Added: trunk/Installer/Support/Paraffin/Paraffin.csproj
===================================================================
--- trunk/Installer/Support/Paraffin/Paraffin.csproj	                        (rev 0)
+++ trunk/Installer/Support/Paraffin/Paraffin.csproj	2012-04-22 12:47:10 UTC (rev 6585)
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>9.0.30729</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{69CEBB42-4C4B-4546-9DEE-A5183E989214}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>Wintellect.Paraffin</RootNamespace>
+    <AssemblyName>Paraffin</AssemblyName>
+    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <SignAssembly>true</SignAssembly>
+    <AssemblyOriginatorKeyFile>Wintellect.SNK</AssemblyOriginatorKeyFile>
+    <PublishUrl>publish\</PublishUrl>
+    <Install>true</Install>
+    <InstallFrom>Disk</InstallFrom>
+    <UpdateEnabled>false</UpdateEnabled>
+    <UpdateMode>Foreground</UpdateMode>
+    <UpdateInterval>7</UpdateInterval>
+    <UpdateIntervalUnits>Days</UpdateIntervalUnits>
+    <UpdatePeriodically>false</UpdatePeriodically>
+    <UpdateRequired>false</UpdateRequired>
+    <MapFileExtensions>true</MapFileExtensions>
+    <ApplicationRevision>0</ApplicationRevision>
+    <ApplicationVersion>1.0.0.%2a</ApplicationVersion>
+    <IsWebBootstrapper>false</IsWebBootstrapper>
+    <UseApplicationTrust>false</UseApplicationTrust>
+    <BootstrapperEnabled>true</BootstrapperEnabled>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>..\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
+    <CodeAnalysisRules>+!Microsoft.Design#CA1012;+!Microsoft.Design#CA2210;+!Microsoft.Design#CA1040;+!Microsoft.Design#CA1005;+!Microsoft.Design#CA1020;+!Microsoft.Design#CA1021;+!Microsoft.Design#CA1010;+!Microsoft.Design#CA1011;+!Microsoft.Design#CA1009;+!Microsoft.Design#CA1050;+!Microsoft.Design#CA1026;+!Microsoft.Design#CA1019;+!Microsoft.Design#CA1031;+!Microsoft.Design#CA1047;+!Microsoft.Design#CA1000;+!Microsoft.Design#CA1048;+!Microsoft.Design#CA1051;+!Microsoft.Design#CA1002;+!Microsoft.Design#CA1061;+!Microsoft.Design#CA1006;+!Microsoft.Design#CA1046;+!Microsoft.Design#CA1045;+!Microsoft.Design#CA1065;+!Microsoft.Design#CA1038;+!Microsoft.Design#CA1008;+!Microsoft.Design#CA1028;+!Microsoft.Design#CA1064;+!Microsoft.Design#CA1004;+!Microsoft.Design#CA1035;+!Microsoft.Design#CA1063;+!Microsoft.Design#CA1032;+!Microsoft.Design#CA1023;+!Microsoft.Design#CA1033;+!Microsoft.Design#CA1039;+!Microsoft.Design#CA1016;+!Microsoft.Design#CA1014;+!Microsoft.Design#CA1017;+!Mi
 crosoft.Design#CA1018;+!Microsoft.Design#CA1027;+!Microsoft.Design#CA1059;+!Microsoft.Design#CA1060;+!Microsoft.Design#CA1034;+!Microsoft.Design#CA1013;+!Microsoft.Design#CA1036;+!Microsoft.Design#CA1044;+!Microsoft.Design#CA1041;+!Microsoft.Design#CA1025;+!Microsoft.Design#CA1052;+!Microsoft.Design#CA1053;+!Microsoft.Design#CA1057;+!Microsoft.Design#CA1058;+!Microsoft.Design#CA1001;+!Microsoft.Design#CA1049;+!Microsoft.Design#CA1054;+!Microsoft.Design#CA1056;+!Microsoft.Design#CA1055;+!Microsoft.Design#CA1030;+!Microsoft.Design#CA1003;+!Microsoft.Design#CA1007;+!Microsoft.Design#CA1043;+!Microsoft.Design#CA1024;+!Microsoft.Globalization#CA1301;+!Microsoft.Globalization#CA1302;+!Microsoft.Globalization#CA1308;+!Microsoft.Globalization#CA1306;+!Microsoft.Globalization#CA1304;+!Microsoft.Globalization#CA1305;+!Microsoft.Globalization#CA2101;+!Microsoft.Globalization#CA1300;+!Microsoft.Globalization#CA1307;+!Microsoft.Globalization#CA1309;+!Microsoft.Interoperability#CA1403;+!M
 icrosoft.Interoperability#CA1406;+!Microsoft.Interoperability#CA1413;+!Microsoft.Interoperability#CA1402;+!Microsoft.Interoperability#CA1407;+!Microsoft.Interoperability#CA1404;+!Microsoft.Interoperability#CA1410;+!Microsoft.Interoperability#CA1411;+!Microsoft.Interoperability#CA1405;+!Microsoft.Interoperability#CA1409;+!Microsoft.Interoperability#CA1415;+!Microsoft.Interoperability#CA1408;+!Microsoft.Interoperability#CA1414;+!Microsoft.Interoperability#CA1412;+!Microsoft.Interoperability#CA1400;+!Microsoft.Interoperability#CA1401;+!Microsoft.Maintainability#CA1506;+!Microsoft.Maintainability#CA1502;+!Microsoft.Maintainability#CA1501;+!Microsoft.Maintainability#CA1505;+!Microsoft.Maintainability#CA1504;+!Microsoft.Maintainability#CA1500;+!Microsoft.Mobility#CA1600;+!Microsoft.Mobility#CA1601;+!Microsoft.Naming#CA1702;+!Microsoft.Naming#CA1700;+!Microsoft.Naming#CA1712;+!Microsoft.Naming#CA1713;+!Microsoft.Naming#CA1714;+!Microsoft.Naming#CA1709;+!Microsoft.Naming#CA1704;+!Mi
 crosoft.Naming#CA1708;+!Microsoft.Naming#CA1715;+!Microsoft.Naming#CA1710;+!Microsoft.Naming#CA1720;+!Microsoft.Naming#CA1707;+!Microsoft.Naming#CA1722;+!Microsoft.Naming#CA1711;+!Microsoft.Naming#CA1716;+!Microsoft.Naming#CA1717;+!Microsoft.Naming#CA1725;+!Microsoft.Naming#CA1719;+!Microsoft.Naming#CA1721;+!Microsoft.Naming#CA1701;+!Microsoft.Naming#CA1703;+!Microsoft.Naming#CA1724;+!Microsoft.Naming#CA1726;+!Microsoft.Performance#CA1809;+!Microsoft.Performance#CA1811;+!Microsoft.Performance#CA1812;+!Microsoft.Performance#CA1813;+!Microsoft.Performance#CA1823;+!Microsoft.Performance#CA1800;+!Microsoft.Performance#CA1805;+!Microsoft.Performance#CA1810;+!Microsoft.Performance#CA1824;+!Microsoft.Performance#CA1822;+!Microsoft.Performance#CA1815;+!Microsoft.Performance#CA1814;+!Microsoft.Performance#CA1819;+!Microsoft.Performance#CA1821;+!Microsoft.Performance#CA1804;+!Microsoft.Performance#CA1820;+!Microsoft.Performance#CA1802;+!Microsoft.Portability#CA1903;+!Microsoft.Portabi
 lity#CA1901;+!Microsoft.Portability#CA1900;+!Microsoft.Reliability#CA2001;+!Microsoft.Reliability#CA2002;+!Microsoft.Reliability#CA2003;+!Microsoft.Reliability#CA2004;+!Microsoft.Reliability#CA2006;+!Microsoft.Security#CA2116;+!Microsoft.Security#CA2117;+!Microsoft.Security#CA2105;+!Microsoft.Security#CA2115;+!Microsoft.Security#CA2102;+!Microsoft.Security#CA2104;+!Microsoft.Security#CA2122;+!Microsoft.Security#CA2114;+!Microsoft.Security#CA2123;+!Microsoft.Security#CA2111;+!Microsoft.Security#CA2108;+!Microsoft.Security#CA2107;+!Microsoft.Security#CA2103;+!Microsoft.Security#CA2118;+!Microsoft.Security#CA2109;+!Microsoft.Security#CA2119;+!Microsoft.Security#CA2106;+!Microsoft.Security#CA2112;+!Microsoft.Security#CA2120;+!Microsoft.Security#CA2121;+!Microsoft.Security#CA2126;+!Microsoft.Security#CA2124;+!Microsoft.Security#CA2127;+!Microsoft.Security#CA2128;+!Microsoft.Security#CA2129;+!Microsoft.Usage#CA2243;+!Microsoft.Usage#CA2236;+!Microsoft.Usage#CA1816;+!Microsoft.Usag
 e#CA2227;+!Microsoft.Usage#CA2213;+!Microsoft.Usage#CA2216;+!Microsoft.Usage#CA2214;+!Microsoft.Usage#CA2222;+!Microsoft.Usage#CA1806;+!Microsoft.Usage#CA2217;+!Microsoft.Usage#CA2212;+!Microsoft.Usage#CA2219;+!Microsoft.Usage#CA2201;+!Microsoft.Usage#CA2228;+!Microsoft.Usage#CA2221;+!Microsoft.Usage#CA2220;+!Microsoft.Usage#CA2240;+!Microsoft.Usage#CA2229;+!Microsoft.Usage#CA2238;+!Microsoft.Usage#CA2207;+!Microsoft.Usage#CA2208;+!Microsoft.Usage#CA2235;+!Microsoft.Usage#CA2237;+!Microsoft.Usage#CA2232;+!Microsoft.Usage#CA2223;+!Microsoft.Usage#CA2211;+!Microsoft.Usage#CA2233;+!Microsoft.Usage#CA2225;+!Microsoft.Usage#CA2226;+!Microsoft.Usage#CA2231;+!Microsoft.Usage#CA2224;+!Microsoft.Usage#CA2218;+!Microsoft.Usage#CA2234;+!Microsoft.Usage#CA2239;+!Microsoft.Usage#CA2200;+!Microsoft.Usage#CA1801;+!Microsoft.Usage#CA2242;+!Microsoft.Usage#CA2205;+!Microsoft.Usage#CA2230;+!Wintellect.DesignRules#Wintellect2002;+!Wintellect.DesignRules#Wintellect2001;+!Wintellect.DesignRules#
 Wintellect2003;+!Wintellect.DesignRules#Wintellect2004;+!Wintellect.DesignRules#Wintellect2000;+!Wintellect.DesignRules#Wintellect2006;+!Wintellect.DesignRules#Wintellect2005;+!Wintellect.DesignRules#Wintellect2007;+!Wintellect.PerformanceRules#Wintellect1000;+!Wintellect.UsageRules#Wintellect3000;+!Wintellect.UsageRules#Wintellect3003;+!Wintellect.UsageRules#Wintellect3002;+!Wintellect.UsageRules#Wintellect3005;+!Wintellect.UsageRules#Wintellect3004;+!Wintellect.UsageRules#Wintellect3001</CodeAnalysisRules>
+    <DocumentationFile>
+    </DocumentationFile>
+    <RunCodeAnalysis>true</RunCodeAnalysis>
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>..\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <CodeAnalysisRules>+!Microsoft.Design#CA1012;+!Microsoft.Design#CA2210;+!Microsoft.Design#CA1040;+!Microsoft.Design#CA1005;+!Microsoft.Design#CA1020;+!Microsoft.Design#CA1021;+!Microsoft.Design#CA1010;+!Microsoft.Design#CA1011;+!Microsoft.Design#CA1009;+!Microsoft.Design#CA1050;+!Microsoft.Design#CA1026;+!Microsoft.Design#CA1019;+!Microsoft.Design#CA1031;+!Microsoft.Design#CA1047;+!Microsoft.Design#CA1000;+!Microsoft.Design#CA1048;+!Microsoft.Design#CA1051;+!Microsoft.Design#CA1002;+!Microsoft.Design#CA1061;+!Microsoft.Design#CA1006;+!Microsoft.Design#CA1046;+!Microsoft.Design#CA1045;+!Microsoft.Design#CA1065;+!Microsoft.Design#CA1038;+!Microsoft.Design#CA1008;+!Microsoft.Design#CA1028;+!Microsoft.Design#CA1064;+!Microsoft.Design#CA1004;+!Microsoft.Design#CA1035;+!Microsoft.Design#CA1063;+!Microsoft.Design#CA1032;+!Microsoft.Design#CA1023;+!Microsoft.Design#CA1033;+!Microsoft.Design#CA1039;+!Microsoft.Design#CA1016;+!Microsoft.Design#CA1014;+!Microsoft.Design#CA1017;+!Mi
 crosoft.Design#CA1018;+!Microsoft.Design#CA1027;+!Microsoft.Design#CA1059;+!Microsoft.Design#CA1060;+!Microsoft.Design#CA1034;+!Microsoft.Design#CA1013;+!Microsoft.Design#CA1036;+!Microsoft.Design#CA1044;+!Microsoft.Design#CA1041;+!Microsoft.Design#CA1025;+!Microsoft.Design#CA1052;+!Microsoft.Design#CA1053;+!Microsoft.Design#CA1057;+!Microsoft.Design#CA1058;+!Microsoft.Design#CA1001;+!Microsoft.Design#CA1049;+!Microsoft.Design#CA1054;+!Microsoft.Design#CA1056;+!Microsoft.Design#CA1055;+!Microsoft.Design#CA1030;+!Microsoft.Design#CA1003;+!Microsoft.Design#CA1007;+!Microsoft.Design#CA1043;+!Microsoft.Design#CA1024;+!Microsoft.Globalization#CA1301;+!Microsoft.Globalization#CA1302;+!Microsoft.Globalization#CA1308;+!Microsoft.Globalization#CA1306;+!Microsoft.Globalization#CA1304;+!Microsoft.Globalization#CA1305;+!Microsoft.Globalization#CA2101;+!Microsoft.Globalization#CA1300;+!Microsoft.Globalization#CA1307;+!Microsoft.Globalization#CA1309;+!Microsoft.Interoperability#CA1403;+!M
 icrosoft.Interoperability#CA1406;+!Microsoft.Interoperability#CA1413;+!Microsoft.Interoperability#CA1402;+!Microsoft.Interoperability#CA1407;+!Microsoft.Interoperability#CA1404;+!Microsoft.Interoperability#CA1410;+!Microsoft.Interoperability#CA1411;+!Microsoft.Interoperability#CA1405;+!Microsoft.Interoperability#CA1409;+!Microsoft.Interoperability#CA1415;+!Microsoft.Interoperability#CA1408;+!Microsoft.Interoperability#CA1414;+!Microsoft.Interoperability#CA1412;+!Microsoft.Interoperability#CA1400;+!Microsoft.Interoperability#CA1401;+!Microsoft.Maintainability#CA1506;+!Microsoft.Maintainability#CA1502;+!Microsoft.Maintainability#CA1501;+!Microsoft.Maintainability#CA1505;+!Microsoft.Maintainability#CA1504;+!Microsoft.Maintainability#CA1500;+!Microsoft.Mobility#CA1600;+!Microsoft.Mobility#CA1601;+!Microsoft.Naming#CA1702;+!Microsoft.Naming#CA1700;+!Microsoft.Naming#CA1712;+!Microsoft.Naming#CA1713;+!Microsoft.Naming#CA1714;+!Microsoft.Naming#CA1709;+!Microsoft.Naming#CA1704;+!Mi
 crosoft.Naming#CA1708;+!Microsoft.Naming#CA1715;+!Microsoft.Naming#CA1710;+!Microsoft.Naming#CA1720;+!Microsoft.Naming#CA1707;+!Microsoft.Naming#CA1722;+!Microsoft.Naming#CA1711;+!Microsoft.Naming#CA1716;+!Microsoft.Naming#CA1717;+!Microsoft.Naming#CA1725;+!Microsoft.Naming#CA1719;+!Microsoft.Naming#CA1721;+!Microsoft.Naming#CA1701;+!Microsoft.Naming#CA1703;+!Microsoft.Naming#CA1724;+!Microsoft.Naming#CA1726;+!Microsoft.Performance#CA1809;+!Microsoft.Performance#CA1811;+!Microsoft.Performance#CA1812;+!Microsoft.Performance#CA1813;+!Microsoft.Performance#CA1823;+!Microsoft.Performance#CA1800;+!Microsoft.Performance#CA1805;+!Microsoft.Performance#CA1810;+!Microsoft.Performance#CA1824;+!Microsoft.Performance#CA1822;+!Microsoft.Performance#CA1815;+!Microsoft.Performance#CA1814;+!Microsoft.Performance#CA1819;+!Microsoft.Performance#CA1821;+!Microsoft.Performance#CA1804;+!Microsoft.Performance#CA1820;+!Microsoft.Performance#CA1802;+!Microsoft.Portability#CA1903;+!Microsoft.Portabi
 lity#CA1901;+!Microsoft.Portability#CA1900;+!Microsoft.Reliability#CA2001;+!Microsoft.Reliability#CA2002;+!Microsoft.Reliability#CA2003;+!Microsoft.Reliability#CA2004;+!Microsoft.Reliability#CA2006;+!Microsoft.Security#CA2116;+!Microsoft.Security#CA2117;+!Microsoft.Security#CA2105;+!Microsoft.Security#CA2115;+!Microsoft.Security#CA2102;+!Microsoft.Security#CA2104;+!Microsoft.Security#CA2122;+!Microsoft.Security#CA2114;+!Microsoft.Security#CA2123;+!Microsoft.Security#CA2111;+!Microsoft.Security#CA2108;+!Microsoft.Security#CA2107;+!Microsoft.Security#CA2103;+!Microsoft.Security#CA2118;+!Microsoft.Security#CA2109;+!Microsoft.Security#CA2119;+!Microsoft.Security#CA2106;+!Microsoft.Security#CA2112;+!Microsoft.Security#CA2120;+!Microsoft.Security#CA2121;+!Microsoft.Security#CA2126;+!Microsoft.Security#CA2124;+!Microsoft.Security#CA2127;+!Microsoft.Security#CA2128;+!Microsoft.Security#CA2129;+!Microsoft.Usage#CA2243;+!Microsoft.Usage#CA2236;+!Microsoft.Usage#CA1816;+!Microsoft.Usag
 e#CA2227;+!Microsoft.Usage#CA2213;+!Microsoft.Usage#CA2216;+!Microsoft.Usage#CA2214;+!Microsoft.Usage#CA2222;+!Microsoft.Usage#CA1806;+!Microsoft.Usage#CA2217;+!Microsoft.Usage#CA2212;+!Microsoft.Usage#CA2219;+!Microsoft.Usage#CA2201;+!Microsoft.Usage#CA2228;+!Microsoft.Usage#CA2221;+!Microsoft.Usage#CA2220;+!Microsoft.Usage#CA2240;+!Microsoft.Usage#CA2229;+!Microsoft.Usage#CA2238;+!Microsoft.Usage#CA2207;+!Microsoft.Usage#CA2208;+!Microsoft.Usage#CA2235;+!Microsoft.Usage#CA2237;+!Microsoft.Usage#CA2232;+!Microsoft.Usage#CA2223;+!Microsoft.Usage#CA2211;+!Microsoft.Usage#CA2233;+!Microsoft.Usage#CA2225;+!Microsoft.Usage#CA2226;+!Microsoft.Usage#CA2231;+!Microsoft.Usage#CA2224;+!Microsoft.Usage#CA2218;+!Microsoft.Usage#CA2234;+!Microsoft.Usage#CA2239;+!Microsoft.Usage#CA2200;+!Microsoft.Usage#CA1801;+!Microsoft.Usage#CA2242;+!Microsoft.Usage#CA2205;+!Microsoft.Usage#CA2230;+!Wintellect.DesignRules#Wintellect2002;+!Wintellect.DesignRules#Wintellect2001;+!Wintellect.DesignRules#
 Wintellect2003;+!Wintellect.DesignRules#Wintellect2004;+!Wintellect.DesignRules#Wintellect2000;+!Wintellect.DesignRules#Wintellect2006;+!Wintellect.DesignRules#Wintellect2005;+!Wintellect.DesignRules#Wintellect2007;+!Wintellect.PerformanceRules#Wintellect1000;+!Wintellect.UsageRules#Wintellect3000;+!Wintellect.UsageRules#Wintellect3003;+!Wintellect.UsageRules#Wintellect3002;+!Wintellect.UsageRules#Wintellect3005;+!Wintellect.UsageRules#Wintellect3004;+!Wintellect.UsageRules#Wintellect3001</CodeAnalysisRules>
+    <DocumentationFile>
+    </DocumentationFile>
+    <RunCodeAnalysis>true</RunCodeAnalysis>
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core">
+      <RequiredTargetFramework>3.5</RequiredTargetFramework>
+    </Reference>
+    <Reference Include="System.Xml.Linq">
+      <RequiredTargetFramework>3.5</RequiredTargetFramework>
+    </Reference>
+    <Reference Include="System.Data.DataSetExtensions">
+      <RequiredTargetFramework>3.5</RequiredTargetFramework>
+    </Reference>
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="ArgParser.cs" />
+    <Compile Include="Constants.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DesignTime>True</DesignTime>
+      <DependentUpon>Constants.resx</DependentUpon>
+    </Compile>
+    <Compile Include="GlobalSuppressions.cs" />
+    <Compile Include="Main.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="ParaffinArgParser.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="Constants.resx">
+      <SubType>Designer</SubType>
+      <Generator>ResXFileCodeGenerator</Generator>
+      <LastGenOutput>Constants.Designer.cs</LastGenOutput>
+    </EmbeddedResource>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Wintellect.SNK" />
+  </ItemGroup>
+  <ItemGroup>
+    <BootstrapperPackage Include="Microsoft.Net.Framework.2.0">
+      <Visible>False</Visible>
+      <ProductName>.NET Framework 2.0 %28x86%29</ProductName>
+      <Install>false</Install>
+    </BootstrapperPackage>
+    <BootstrapperPackage Include="Microsoft.Net.Framework.3.5">
+      <Visible>False</Visible>
+      <ProductName>.NET Framework 3.5</ProductName>
+      <Install>true</Install>
+    </BootstrapperPackage>
+    <BootstrapperPackage Include="Microsoft.Windows.Installer.3.1">
+      <Visible>False</Visible>
+      <ProductName>Windows Installer 3.1</ProductName>
+      <Install>true</Install>
+    </BootstrapperPackage>
+  </ItemGroup>
+  <ItemGroup>
+    <CodeAnalysisDictionary Include="WintellectDictionary.xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="Features.xml">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>
\ No newline at end of file

Added: trunk/Installer/Support/Paraffin/Paraffin.sln
===================================================================
--- trunk/Installer/Support/Paraffin/Paraffin.sln	                        (rev 0)
+++ trunk/Installer/Support/Paraffin/Paraffin.sln	2012-04-22 12:47:10 UTC (rev 6585)
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Paraffin", "Paraffin.csproj", "{69CEBB42-4C4B-4546-9DEE-A5183E989214}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{69CEBB42-4C4B-4546-9DEE-A5183E989214}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{69CEBB42-4C4B-4546-9DEE-A5183E989214}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{69CEBB42-4C4B-4546-9DEE-A5183E989214}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{69CEBB42-4C4B-4546-9DEE-A5183E989214}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal

Added: trunk/Installer/Support/Paraffin/ParaffinArgParser.cs
===================================================================
--- trunk/Installer/Support/Paraffin/ParaffinArgParser.cs	                        (rev 0)
+++ trunk/Installer/Support/Paraffin/ParaffinArgParser.cs	2012-04-22 12:47:10 UTC (rev 6585)
@@ -0,0 +1,439 @@
+/*------------------------------------------------------------------------------
+ * Copyright © 2007 John Robbins -- All rights reserved. 
+ -----------------------------------------------------------------------------*/
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Diagnostics;
+using System.IO;
+using System.Diagnostics.CodeAnalysis;
+
+namespace Wintellect.Paraffin
+{
+    internal class ParaffinArgParser : ArgParser
+    {
+        #region Required Creation Parameters
+        /// <summary>
+        /// The directory to process.
+        /// </summary>
+        public String StartDirectory { get; set; }
+
+        /// <summary>
+        /// The custom value to put on all components/files/dirs.
+        /// </summary>
+        public String CustomValue { get; set; }
+
+        #endregion
+
+        #region Optional Creation Parameters
+        /// <summary>
+        /// Override the DirectoryRef Id if you want the fragment files
+        /// to go somewhere esle besides the INSTALLDIR.
+        /// </summary> 
+        public String DirectoryRef { get; set; }
+
+        /// <summary>
+        /// The value to replace the starting directory in the File element
+        /// src attribute.
+        /// </summary>
+        public String Alias { get; set; }
+
+        /// <summary>
+        /// If true, all the files in a directory will be included into a 
+        /// single component.
+        /// </summary>
+        public Boolean MultipleFilesPerComponent { get; set; }
+
+        /// <summary>
+        /// The list of extensions to skip.
+        /// </summary>
+        public Dictionary<String , Boolean> ExtensionList { get; set; }
+
+        /// <summary>
+        /// If true, GUIDs will be generated for all components.
+        /// </summary>
+        public Boolean GenerateGuids { get; set; }
+
+        /// <summary>
+        /// The amount to add to each component number to leave room for 
+        /// additional component files between directories.
+        /// </summary>
+        public Int32 IncrementValue { get; set; }
+
+        /// <summary>
+        /// If true, does not recurse the directories.
+        /// </summary>
+        public Boolean NoDirectoryRecursion { get; set; }
+
+        /// <summary>
+        /// The list of directories to exclude from the processing.
+        /// </summary>
+        public List<String> DirectoryExcludeList { get; set; }
+
+        /// <summary>
+        /// If true, adds the Win64 attribute to components.
+        /// </summary>
+        public Boolean Win64 { get; set; }
+        #endregion
+
+        /// <summary>
+        /// True if the user wants to update a previously created file.
+        /// </summary>
+        public Boolean Update { get; set; }
+
+        /// <summary>
+        /// The output filename.
+        /// </summary>
+        public String FileName { get; set; }
+
+        // The private string to hold more detailed error information.
+        private String errorMessage;
+        // Indicates the error was found in OnDoneParse.
+        private Boolean errorInOnDoneParse;
+        // Indicates we've already seen the -inc switch.
+        private Boolean seenIncrementSwitch;
+
+        #region Command Line Option Constants
+        private const string k_HELP_QUESTION = "?";
+        private const string k_HELP_SHORT = "h";
+        private const string k_HELP = "help";
+        private const string k_GUIDS = "guids";
+        private const string k_GUIDS_SHORT = "g";
+        private const string k_ALIAS = "alias";
+        private const string k_ALIAS_SHORT = "a";
+        private const string k_MULTIPLE = "multiple";
+        private const string k_MULTIPLE_SHORT = "m";
+        private const string k_NORECURSE = "norecurse";
+        private const string k_NORECURSE_SHORT = "nr";
+        private const string k_DIR = "dir";
+        private const string k_DIR_SHORT = "d";
+        private const string k_CUSTOM = "custom";
+        private const string k_CUSTOM_SHORT = "c";
+        private const string k_EXT = "ext";
+        private const string k_EXT_SHORT = "e";
+        private const string k_DIREXCLUDE = "direXclude";
+        private const string k_DIREXCLUDE_SHORT = "x";
+        private const string k_INC = "inc";
+        private const string k_INC_SHORT = "i";
+        private const string k_UPDATE = "update";
+        private const string k_UPDATE_SHORT = "u";
+        private const string k_WIN64 = "win64";
+        private const string k_DIRREF = "dirref";
+        private const string k_DIRREF_SHORT = "dr";
+        #endregion
+
+        public ParaffinArgParser ( )
+            : base ( new String [] { k_HELP_QUESTION   , k_HELP_SHORT , k_HELP ,
+                                     k_GUIDS_SHORT     , k_GUIDS ,
+                                     k_MULTIPLE_SHORT  , k_MULTIPLE ,
+                                     k_NORECURSE_SHORT , k_NORECURSE ,
+                                     k_UPDATE_SHORT    , k_UPDATE ,
+                                     k_WIN64 } ,
+                     new String [] { k_DIR_SHORT        , k_DIR ,
+                                     k_CUSTOM_SHORT     , k_CUSTOM ,
+                                     k_ALIAS_SHORT      , k_ALIAS ,
+                                     k_EXT_SHORT        , k_EXT ,
+                                     k_INC_SHORT        , k_INC ,
+                                     k_DIREXCLUDE_SHORT , k_DIREXCLUDE ,
+                                     k_DIRREF           , k_DIRREF_SHORT } , 
+                     true )
+        {
+            // Set all the appropriate defaults.
+            FileName = String.Empty;
+            StartDirectory = String.Empty;
+            CustomValue = String.Empty;
+            Alias = String.Empty;
+            DirectoryRef = String.Empty;
+            MultipleFilesPerComponent = false;
+            ExtensionList = new Dictionary<String , Boolean> ( );
+            GenerateGuids = false;
+            IncrementValue = 1;
+            DirectoryExcludeList = new List<String> ( );
+
+            errorMessage = String.Empty;
+        }
+
+        [SuppressMessage ( "Microsoft.Maintainability" ,
+                           "CA1502:AvoidExcessiveComplexity" ,
+                           Justification =
+              "A switch statement using strings always generates complexity." )]
+        protected override SwitchStatus OnSwitch ( string switchSymbol ,
+                                                   string switchValue )
+        {
+            SwitchStatus ss = SwitchStatus.NoError;
+            switch ( switchSymbol )
+            {
+                case k_HELP_QUESTION:
+                case k_HELP_SHORT:
+                case k_HELP:
+                    ss = SwitchStatus.ShowUsage;
+                    break;
+
+                case k_GUIDS_SHORT:
+                case k_GUIDS:
+                    GenerateGuids = true;
+                    break;
+
+                case k_MULTIPLE_SHORT:
+                case k_MULTIPLE:
+                    MultipleFilesPerComponent = true;
+                    break;
+
+                case k_NORECURSE_SHORT:
+                case k_NORECURSE:
+                    NoDirectoryRecursion = true;
+                    break;
+
+                case k_UPDATE_SHORT:
+                case k_UPDATE:
+                    Update = true;
+                    break;
+
+                case k_WIN64:
+                    Win64 = true;
+                    break;
+
+                case k_ALIAS_SHORT:
+                case k_ALIAS:
+                    if ( false == String.IsNullOrEmpty ( Alias ) )
+                    {
+                        errorMessage = Constants.AliasMultipleSwitches;
+                        ss = SwitchStatus.Error;
+                    }
+                    else
+                    {
+                        // If the alias does not end with a \, add one to help
+                        // the user out.
+                        if ( false == switchValue.EndsWith ( "\\" ,
+                                          StringComparison.OrdinalIgnoreCase ) )
+                        {
+                            switchValue += "\\";
+                        }
+                        Alias = switchValue;
+                    }
+                    break;
+
+
+                case k_DIRREF_SHORT:
+                case k_DIRREF:
+                    if ( false == String.IsNullOrEmpty ( DirectoryRef ) )
+                    {
+                        errorMessage = Constants.DirectoryRefMultipleSwitches;
+                        ss = SwitchStatus.Error;
+                    }
+                    else
+                    {
+                        DirectoryRef = switchValue;
+                    }
+                    break;
+
+                case k_DIR_SHORT:
+                case k_DIR:
+                    if ( false == String.IsNullOrEmpty ( StartDirectory ) )
+                    {
+                        errorMessage = Constants.DirectoryMultipleSwitches;
+                        ss = SwitchStatus.Error;
+                    }
+                    else if ( true == String.IsNullOrEmpty ( switchValue ) )
+                    {
+                        errorMessage = Constants.DirectoryCannotBeEmpty;
+                        ss = SwitchStatus.Error;
+                    }
+                    else
+                    {
+                        // If the directory does not end with a \, add one.
+                        if ( false == switchValue.EndsWith ( "\\" ,
+                                          StringComparison.OrdinalIgnoreCase ) )
+                        {
+                            switchValue += "\\";
+                        }
+                        StartDirectory = switchValue;
+                    }
+                    break;
+
+                case k_CUSTOM_SHORT:
+                case k_CUSTOM:
+                    if ( false == String.IsNullOrEmpty ( CustomValue ) )
+                    {
+                        errorMessage = Constants.UniqueMultipleSwitches;
+                        ss = SwitchStatus.Error;
+                    }
+                    else if ( true == String.IsNullOrEmpty ( switchValue ) )
+                    {
+                        errorMessage = Constants.UniqueCannotBeEmpty;
+                        ss = SwitchStatus.Error;
+                    }
+                    else if ( switchValue.Length >= 65 )
+                    {
+                        errorMessage = Constants.UniqueTooLong;
+                        ss = SwitchStatus.Error;
+                    }
+                    else
+                    {
+                        CustomValue = switchValue;
+                    }
+                    break;
+
+                case k_EXT_SHORT:
+                case k_EXT:
+                    if ( true == String.IsNullOrEmpty ( switchValue ) )
+                    {
+                        errorMessage = Constants.ExtensionCannotBeEmpty;
+                        ss = SwitchStatus.Error;
+                    }
+                    else
+                    {
+                        // Does it start with a period? If not, add one to help
+                        // the user out.
+                        if ( '.' != switchValue [ 0 ] )
+                        {
+                            switchValue = "." + switchValue;
+                        }
+                        // You can have as many -ext switches as you want.
+                        ExtensionList.Add ( switchValue.ToUpperInvariant ( ) ,
+                                            true );
+                    }
+                    break;
+
+                case k_INC_SHORT:
+                case k_INC:
+                    if ( true == seenIncrementSwitch )
+                    {
+                        errorMessage = Constants.IncrementMultipleSwitches;
+                        ss = SwitchStatus.Error;
+                    }
+                    else
+                    {
+                        Int32 level = 0;
+                        if ( false == Int32.TryParse ( switchValue ,
+                                                       out level ) )
+                        {
+                            errorMessage = Constants.IncrementNoParse;
+                            ss = SwitchStatus.Error;
+                        }
+                        else if ( level <= 0 )
+                        {
+                            errorMessage = Constants.IncrementNotZero;
+                            ss = SwitchStatus.Error;
+                        }
+                        else
+                        {
+                            IncrementValue = level;
+                            seenIncrementSwitch = true;
+                        }
+                    }
+                    break;
+
+                case k_DIREXCLUDE_SHORT:
+                case k_DIREXCLUDE:
+                    DirectoryExcludeList.Add ( switchValue );
+                    break;
+
+                default:
+                    {
+                        errorMessage = Constants.UnknownCommandLineOption;
+                        ss = SwitchStatus.Error;
+                    }
+                    break;
+            }
+            return ( ss );
+        }
+
+        protected override SwitchStatus OnNonSwitch ( string value )
+        {
+            SwitchStatus ss = SwitchStatus.NoError;
+            if ( false == String.IsNullOrEmpty ( FileName ) )
+            {
+                errorMessage = Constants.OutputAlreadySpecified;
+                ss = SwitchStatus.Error;
+            }
+            else if ( true == String.IsNullOrEmpty ( value ) )
+            {
+                errorMessage = Constants.OutputCannotBeEmpty;
+                ss = SwitchStatus.Error;
+            }
+            else
+            {
+                FileName = value;
+            }
+
+            // There are no non switches allowed.
+            errorMessage = Constants.UnknownCommandLineOption;
+            return ( ss );
+        }
+
+        protected override SwitchStatus OnDoneParse ( )
+        {
+            SwitchStatus ss = SwitchStatus.NoError;
+            // The output file can never be null.
+            if ( true == string.IsNullOrEmpty ( FileName ) )
+            {
+                errorMessage = Constants.OutputCannotBeEmpty;
+                ss = SwitchStatus.Error;
+                errorInOnDoneParse = true;
+            }
+            else if ( false == Update )
+            {
+                // Check that I at least have a directory and prefix. Everything
+                // else is optional when updating files.
+                if ( true == String.IsNullOrEmpty ( StartDirectory ) )
+                {
+                    errorMessage = Constants.DirectoryCannotBeEmpty;
+                    ss = SwitchStatus.Error;
+                    errorInOnDoneParse = true;
+                }
+                else if ( false == Directory.Exists ( StartDirectory ) )
+                {
+                    errorMessage = Constants.DirectoryDoesNotExist;
+                    ss = SwitchStatus.Error;
+                    errorInOnDoneParse = true;
+                }
+                else if ( true == String.IsNullOrEmpty ( CustomValue ) )
+                {
+                    errorMessage = Constants.UniqueCannotBeEmpty;
+                    ss = SwitchStatus.Error;
+                    errorInOnDoneParse = true;
+                }
+            }
+            else
+            {
+                // The user is asking to update.
+                // Check that they didn't also specify creation options.
+                if ( ( false == String.IsNullOrEmpty ( StartDirectory ) ||
+                     ( false == String.IsNullOrEmpty ( CustomValue ) ) ) )
+                {
+                    errorMessage = Constants.MutuallyExclusiveOptions;
+                    ss = SwitchStatus.Error;
+                    errorInOnDoneParse = true;
+                }
+                // Check to at least see if the file exists.
+                if ( false == File.Exists ( FileName ) )
+                {
+                    errorMessage = Constants.UpdateFileMustExist;
+                    ss = SwitchStatus.Error;
+                    errorInOnDoneParse = true;
+                }
+            }
+            return ( ss );
+        }
+
+        public override void OnUsage ( string errorInfo )
+        {
+            ProcessModule exe = Process.GetCurrentProcess ( ).Modules [ 0 ];
+            Console.WriteLine ( Constants.UsageString ,
+                                exe.FileVersionInfo.FileVersion );
+            if ( ( false == errorInOnDoneParse ) &&
+                 ( false == String.IsNullOrEmpty ( errorInfo ) ) )
+            {
+                Console.WriteLine ( );
+                Console.WriteLine ( Constants.ErrorSwitch , errorInfo );
+            }
+            if ( false == String.IsNullOrEmpty ( errorMessage ) )
+            {
+                Console.WriteLine ( );
+                Console.WriteLine ( errorMessage );
+            }
+        }
+    }
+}


Property changes on: trunk/Installer/Support/Paraffin/Properties
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/Installer/Support/Paraffin/Properties/AssemblyInfo.cs
===================================================================
--- trunk/Installer/Support/Paraffin/Properties/AssemblyInfo.cs	                        (rev 0)
+++ trunk/Installer/Support/Paraffin/Properties/AssemblyInfo.cs	2012-04-22 12:47:10 UTC (rev 6585)
@@ -0,0 +1,41 @@
+using System;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Resources;
+
+[assembly: CLSCompliant ( true )]
+[assembly: NeutralResourcesLanguage ( "en-US" )]
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle ( "Paraffin" )]
+[assembly: AssemblyDescription ( "A better TALLOW for creating WiX fragments" )]
+[assembly: AssemblyConfiguration ( "" )]
+[assembly: AssemblyCompany ( "Wintellect" )]
+[assembly: AssemblyProduct ( "Paraffin" )]
+[assembly: AssemblyCopyright ( "Copyright © John Robbins 2007-2008" )]
+[assembly: AssemblyTrademark ( "" )]
+[assembly: AssemblyCulture ( "" )]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible ( false )]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid ( "c3298ef9-1922-4e6b-9da6-6c4f644aa5a5" )]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion ( "3.0.0.0" )]
+[assembly: AssemblyFileVersion ( "3.0.0.0" )]

Added: trunk/Installer/Support/Paraffin/Wintellect.SNK
===================================================================
(Binary files differ)


Property changes on: trunk/Installer/Support/Paraffin/Wintellect.SNK
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/Installer/Support/Paraffin/WintellectDictionary.xml
===================================================================
--- trunk/Installer/Support/Paraffin/WintellectDictionary.xml	                        (rev 0)
+++ trunk/Installer/Support/Paraffin/WintellectDictionary.xml	2012-04-22 12:47:10 UTC (rev 6585)
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<Dictionary>
+  <Words>
+    <Unrecognized>
+    </Unrecognized>
+    <Recognized>
+      <Word>Wintellect</Word>
+      <Word>Validators</Word>
+      <Word>Automator</Word>
+      <Word>mscorwks</Word>
+      <Word>plugin</Word>
+      <Word>minidump</Word>
+      <Word>loadby</Word>
+      <Word>dmp</Word>
+      <Word>pid</Word>
+      <Word>comspec</Word>
+      <Word>Hhc</Word>
+      <Word>Sln</Word>
+      <Word>Ini</Word>
+      <Word>Perf</Word>
+      <Word>Turnon</Word>
+      <Word>Vss</Word>
+      <Word>Wix</Word>
+      <Word>stdafx</Word>
+      <Word>Impl</Word>
+      <Word>Visualizers</Word>
+      <Word>Visualizer</Word>
+      <Word>guids</Word>
+      <Word>GUIDs</Word>
+      <Word>blogs</Word>
+      <Word>jrobbins</Word>
+      <Word>sourceforge</Word>
+      <Word>norecurse</Word>
+      <Word>recursing</Word>
+      <Word>Xclude</Word>
+      <Word>exdir</Word>
+      <Word>Bugslayer</Word>
+      <Word>Autogenerated</Word>
+      <Word>src</Word>
+      <Word>dirref</Word>
+    </Recognized>
+    <Deprecated>
+    </Deprecated>
+    <Compound>
+    </Compound>
+    <DiscreteExceptions>
+    </DiscreteExceptions>
+  </Words>
+  <Acronyms>
+    <CasingExceptions>
+    </CasingExceptions>
+  </Acronyms>
+</Dictionary>



More information about the mapguide-commits mailing list