List:Commits« Previous MessageNext Message »
From:Mike Lischke Date:March 2 2011 3:58pm
Subject:bzr commit into wex-installer-1.0 branch (mike.lischke:350)
View as plain text  
#At file:///D:/Work/MySQL/installer/ based on revid:reggie.burnett@stripped

  350 Mike Lischke	2011-03-02
      - Manifest Updater: Added a new command line switch to make it wait when finished (if required).
      - Closing the installer when clicking Cancel or the red-cross close button is now handled centrally in the MainForm (might need rework of certain pages to make it fully work).
      - Introduced new WorkDone property to let the main window decide when to ask for confirmation to close the application.
      - InstallWizard: Added CancelActions to allow cancelling download/installation actions if the user wants to stop.
      - InstallWizard: disable Cancel if everything was done.
      - Removed a few unnecessary Refresh() calls (they cause flicker, use Update() if really needed).
      - ListViewWex: removed unncessary background drawing call. That conflicts with tooltips.
      - InstallProgress: Back is now always possible and will stop the current actions after the user agreed.
      - InstallProgress: Properly reinit the page if we enter a second time (e.g. after stopping a process and going back and then start again).
      - InstallProgress: introduced started/finished duality instead of simple executed, to handle the internal state better.
      - InstallProgress: removed the need for a local loop for installation. Instead steps are triggered by asynchronous handling (via BeginInvoke). Removed all Thread.Sleep() and Application.DoEvents calls too. No need for them anymore.
      - InstallProgress: handle the case that there are no downloads but only install ops properly.
      - InstallProgress: show better error info when downloading fails (Notes columns gets the actual error text, plus tooltips are enabled to help viewing longer messages).
      - InstallProgress: let user confirm to continue with installation if there were download errrors.
      - RemoveProgress: set finished variable *after* the work is done. The user could decided not to start it.
      - RemoveProgress: enable Next only if at least one product is selected to be removed.
      - InstallerConfiguration: use proper default values if a the current configuration is incomplete.
      - InstallerConfiguration: removed the hack to use a manifest in the solution dir if found.
      - Product: added new error state (No mirrors found).
      - Product: improved handling and notification about certain errors.
      - Product: do not enqueue empty archive path in mirror list (gives error from WebClient).

    modified:
      ManifestUpdater/Program.cs
      Setup/Setup_Net.wixproj
      WexInstaller.Core/InstallerConfiguration.cs
      WexInstaller.Core/InstallerPanel.cs
      WexInstaller.Core/Product.cs
      WexInstaller.Core/ProductManager.cs
      WexInstaller/Controls/InstallWizardControl.cs
      WexInstaller/Controls/ListViewWex.cs
      WexInstaller/Controls/RemoveControl.cs
      WexInstaller/InstallWizard/InstallProgressPanel.Designer.cs
      WexInstaller/InstallWizard/InstallProgressPanel.cs
      WexInstaller/InstallWizard/InstallProgressPanel.resx
      WexInstaller/InstallWizard/InstallationComplete.Designer.cs
      WexInstaller/InstallWizard/InstallationComplete.cs
      WexInstaller/MainForm.cs
      WexInstaller/Properties/Resources.Designer.cs
      WexInstaller/Properties/Resources.resx
      WexInstaller/RemovePanels/RemoveProgress.Designer.cs
      WexInstaller/RemovePanels/RemoveProgress.cs
      installer-vs2010.sln
=== modified file 'ManifestUpdater/Program.cs'
=== modified file 'ManifestUpdater/Program.cs'
--- a/ManifestUpdater/Program.cs	2011-02-25 21:39:47 +0000
+++ b/ManifestUpdater/Program.cs	2011-03-02 15:58:16 +0000
@@ -13,6 +13,7 @@
         static string inFile = null;
         static string outFile = null;
         static string cacheDir = null;
+        static bool wait = true;
 
         static void Main(string[] args)
         {
@@ -21,7 +22,8 @@
             OptionSet p = new OptionSet()
               .Add("input=|in=", input => inFile=input)
               .Add("output=|out=", output => outFile=output)
-              .Add("cachedir=|cache=", dir => cacheDir=dir);
+              .Add("cachedir=|cache=", dir => cacheDir=dir)
+              .Add("wait=", dowait => wait = (dowait == "y" || dowait == "Y"));
             p.Parse(args);
 
             // load the manifest
@@ -39,9 +41,12 @@
 
             // now save the manifest
             manifest.Save(outFile);
-            //Console.WriteLine();
-            //Console.WriteLine("Manifest generation done.  Press Enter.");
-            //Console.ReadLine();
+            if (wait)
+            {
+                Console.WriteLine();
+                Console.WriteLine("Manifest generation done.  Press Enter.");
+                Console.ReadLine();
+            }
         }
 
         static void UpdateProductAndPackage(Product product, Package package)

=== modified file 'Setup/Setup_Net.wixproj'
--- a/Setup/Setup_Net.wixproj	2011-02-25 21:45:33 +0000
+++ b/Setup/Setup_Net.wixproj	2011-03-02 15:58:16 +0000
@@ -63,6 +63,14 @@
       <RefProjectOutputGroups>Binaries;Content;Satellites</RefProjectOutputGroups>
       <RefTargetDir>INSTALLLOCATION</RefTargetDir>
     </ProjectReference>
+    <ProjectReference Include="..\ManifestUpdater\ManifestUpdater.csproj">
+      <Name>ManifestUpdater</Name>
+      <Project>{57913af9-d535-4b13-a088-8df74fd47a53}</Project>
+      <Private>True</Private>
+      <DoNotHarvest>True</DoNotHarvest>
+      <RefProjectOutputGroups>Binaries;Content;Satellites</RefProjectOutputGroups>
+      <RefTargetDir>INSTALLLOCATION</RefTargetDir>
+    </ProjectReference>
     <ProjectReference Include="..\StandardPlugins\StandardPlugins.csproj">
       <Name>StandardPlugins</Name>
       <Project>{c471dbda-2afa-4ea3-970a-795133f1fe1a}</Project>
@@ -90,11 +98,11 @@
   </ItemGroup>
   <Import Project="$(WixTargetsPath)" />
   <PropertyGroup>
+    <PostBuildEvent />
+  </PropertyGroup>
+  <PropertyGroup>
     <PreBuildEvent />
   </PropertyGroup>
-  <PropertyGroup>
-    <PostBuildEvent />
-  </PropertyGroup>
   <!--
 	To modify your build process, add your task inside one of the targets below and uncomment it.
 	Other similar extension points exist, see Wix.targets.

=== modified file 'WexInstaller.Core/InstallerConfiguration.cs'
--- a/WexInstaller.Core/InstallerConfiguration.cs	2011-02-25 15:04:43 +0000
+++ b/WexInstaller.Core/InstallerConfiguration.cs	2011-03-02 15:58:16 +0000
@@ -108,13 +108,6 @@
             get { return isDefault;  }
         }
 
-#if DEBUG
-        public static string DevManifest
-        {
-            get { return @"..\..\..\setup\products.xml"; }
-        }
-#endif
-        
         #endregion
 
         public static void Save()
@@ -127,49 +120,52 @@
 
         public static void Load()
         {
-            if (HomeDir == null || HomeDir == String.Empty)
-                HomeDir = String.Format("{0}\\MySQL\\MySQL Universal Installer", 
-                                        Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData));
-
-            if (!Directory.Exists(HomeDir))
-                Directory.CreateDirectory(HomeDir);
-
-            if (File.Exists(ConfigFile))
-            {
-                XmlSerializer s = new XmlSerializer(typeof(InstallerConfigurationData));
-                TextReader r = new StreamReader(ConfigFile);
-                Instance = (InstallerConfigurationData)s.Deserialize(r);
-                isDefault = false;
-
-                // Keep in mind the installation can be unfinished when we restart, so
-                // some info is not yet set.
-                if (Instance.ProductCachePath != null &&
-                    !Instance.ProductCachePath.EndsWith(Path.DirectorySeparatorChar.ToString()))
-                    Instance.ProductCachePath += Path.DirectorySeparatorChar;
-                if (Instance.InstallationRoot != null &&
-                    !Instance.InstallationRoot.EndsWith(Path.DirectorySeparatorChar.ToString()))
-                    Instance.InstallationRoot += Path.DirectorySeparatorChar;
-            }
-            else
-            {
-                Instance.LicenseAgreement = 1;
-            }
-
-            // Consider incomplete/corrupt/non-existing config file.
-            // Set useful defaults for important values.
-            if (Instance.UpdateURLs.Count == 0)
-            {
-                UpdateURL url = new UpdateURL("http://wb.mysql.com/installer/products.xml");
-                Instance.UpdateURLs.Add(url);
-            }
-
-            Instance.UpdateTimeoutMilliseconds = 10000;
-
-            // TODO: check which of the following entries needs some better default value.
-            // Instance.UpdateCheckFrequency
-            // Instance.ProductCachePath
-            // Instance.InstallationRoot
-            // Instance.ProductCode
+          if (HomeDir == null || HomeDir == String.Empty)
+              HomeDir = String.Format("{0}\\MySQL\\MySQL Universal Installer", 
+                                      Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData));
+
+          if (!Directory.Exists(HomeDir))
+            Directory.CreateDirectory(HomeDir);
+
+          if (File.Exists(ConfigFile))
+          {
+            XmlSerializer s = new XmlSerializer(typeof(InstallerConfigurationData));
+            TextReader r = new StreamReader(ConfigFile);
+            Instance = (InstallerConfigurationData)s.Deserialize(r);
+            isDefault = false;
+          }
+          else
+          {
+            Instance.LicenseAgreement = 1;
+            Instance.UpdateCheckFrequency = 7;
+            // XXX: is this a constant GUID or rather dynamically created?
+            Instance.ProductCode = "{303BA173-69F6-4DC3-B11B-96B104C45BF9}";
+          }
+
+          // Consider incomplete/corrupt/non-existing config file.
+          // Set useful defaults for important values.
+          if (Instance.UpdateURLs.Count == 0)
+          {
+              UpdateURL url = new UpdateURL("http://wb.mysql.com/installer/products.xml");
+              Instance.UpdateURLs.Add(url);
+          }
+
+          Instance.UpdateTimeoutMilliseconds = 10000;
+
+          // Product cache and installation root defaults and sanity checks.
+          if (Instance.ProductCachePath == null || Instance.ProductCachePath == String.Empty)
+            Instance.ProductCachePath = Path.Combine(HomeDir, @"Product Cache\");
+          if (!Instance.ProductCachePath.EndsWith(Path.DirectorySeparatorChar.ToString()))
+            Instance.ProductCachePath += Path.DirectorySeparatorChar;
+          if (!Directory.Exists(Instance.ProductCachePath))
+            Directory.CreateDirectory(Instance.ProductCachePath);
+
+          if (Instance.InstallationRoot == null || Instance.InstallationRoot == String.Empty)
+            Instance.InstallationRoot = Path.Combine(
+              Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
+              @"MySQL\MySQL Universal Installer\");
+          if (!Instance.InstallationRoot.EndsWith(Path.DirectorySeparatorChar.ToString()))
+            Instance.InstallationRoot += Path.DirectorySeparatorChar;
         }
 
         public static bool IsWow64()

=== modified file 'WexInstaller.Core/InstallerPanel.cs'
--- a/WexInstaller.Core/InstallerPanel.cs	2011-02-08 21:28:42 +0000
+++ b/WexInstaller.Core/InstallerPanel.cs	2011-03-02 15:58:16 +0000
@@ -127,6 +127,7 @@
             return true;
         }
 
+        // TODO: *Ok functions are candidates for virtual properties.
         public virtual bool NextOk()
         {
             return true;
@@ -137,6 +138,11 @@
             return true;
         }
 
+        public virtual bool CancelOk()
+        {
+            return true;
+        }
+
         protected void OpenBrowser(string url)
         {
             Process.Start(url);

=== modified file 'WexInstaller.Core/Product.cs'
--- a/WexInstaller.Core/Product.cs	2011-03-02 14:57:43 +0000
+++ b/WexInstaller.Core/Product.cs	2011-03-02 15:58:16 +0000
@@ -14,841 +14,881 @@
 
 namespace WexInstaller.Core
 {
-    public delegate void ProductInstationActionEventHandler(object sender, ChainedInstallerEventArgs c);
-    public delegate void DownloadProductProgressHandler(object sender, DownloadProgressChangedEventArgs de);
-    public delegate void DownloadProductCompleteHandler(object sender, AsyncCompletedEventArgs ae);
-
-    public class Product : ProductElement
-    {
-        private bool loadedController;
-        private ProductConfigurationController controller;
-        private StringBuilder InstallActionLog;
-        private WebClient wc;
-        private BackgroundWorker bgw;
-        private bool proposedInstalled;
-
-        public List<Package> Packages { get; set; }
-
-        public string TitleWithVersion
-        {
-            get { return String.Format("{0} {1}", Title, GetPackage().ThisVersion); }
-        }
-
-        public ProductConfigurationController Controller
-        {
-            get
-            {
-                if (!loadedController)
-                    GetController();
-                return controller;
-            }
-        }
-
-        private Package GetPackage()
-        {
-            if (Packages.Count == 0) return null;
-            if (useCurrentlyInstalledPacakge && Packages.Count == 2)
-                return Packages[1];
-            return Packages[0];
-        }
-
-        public Product()
-        {
-            InstallActionLog = new StringBuilder();
-            wc = new WebClient();
-            CurrentState = ProductState.Unknown;
-            useCurrentlyInstalledPacakge = false;
-            msiVersion = FileVersionInfo.GetVersionInfo(Environment.SystemDirectory + "\\msi.dll");
-        }
-
-        public void PostInitialize(bool initalizePackage)
-        {
-            Package p = GetPackage();
-            if (initalizePackage)
-            {
-                p.PostInitialize();
-            }
-            p.UrlBase = UrlBase;
-
-            if (String.IsNullOrEmpty(p.Id) == false)  // Valid Package.
-            {
-                // Is the pacakge in our cache?
-                CurrentState = (p.FoundLocal) ? ProductState.FoundLocal : ProductState.WebRemote;
-
-                // Is the package an upgrade for another currently installed product?
-                IsUpgrade = FindReleatedInstalledProducts();
-                if (IsUpgrade)
-                    CurrentState = ProductState.WillPerformUpgrade;
-
-                // Is the package installed?
-                proposedInstalled = p.Installed;
-                if (p.Installed)
-                    CurrentState = ProductState.CurrentlyInstalled;
-            }
-        }
-
-        public string GetLog()
-        {
-            return InstallActionLog.ToString();
-        }
-
-        protected void SetProposedInstalled(bool shouldInstall)
-        {
-            if (shouldInstall == proposedInstalled) return;
-            proposedInstalled = shouldInstall;
-        }
-
-        [XmlAttribute("upgradeId")]
-        public string UpgradeId { get; set; }
-
-        [XmlAttribute("urlBaseDir")]
-        public string UrlBase { get; set; }
-
-        [XmlIgnore]
-        public bool ProposedInstalled
-        {
-            get { return proposedInstalled; }
-            set { SetProposedInstalled(value); }
-        }
-
-        [XmlIgnore]
-        public bool Installed 
-        {
-            get
-            {
-                return GetPackage().Installed;
-            }
-        }
-
-        [XmlIgnore]
-        public bool FoundLocal 
-        { 
-            get
-            {
-                return GetPackage().FoundLocal;
-            }
-        }
-
-        [XmlIgnore]
-        public bool IsUpgrade { get; private set; }
-
-        [XmlIgnore]
-        public ProductState CurrentState { get; private set; }
-
-        [XmlIgnore]
-        private Queue<string> MirrorList { get; set; }
-
-        private bool useCurrentlyInstalledPacakge;
-        [XmlIgnore]
-        public bool UseCurrentlyInstalledPackage 
-        {
-            get
-            {
-                return useCurrentlyInstalledPacakge;
-            }
-            set
-            {
-                if (useCurrentlyInstalledPacakge != value)
-                {
-                    useCurrentlyInstalledPacakge = value;
-                    PostInitialize(false);
-                }
-            }
-        }
-
-        [XmlIgnore]
-        public bool IsServerProduct
-        {
-            get { return Name.StartsWith("mysql-server"); }
-        }
-
-        public bool HasChanges()
-        {
-            bool hasChanges;
-            hasChanges = Installed != ProposedInstalled;
-            foreach (ProductFeature feature in GetPackage().Features)
-                hasChanges |= feature.HasChanges();
-            return hasChanges;
-        }
-
-        public string GetUpgradeDisplayString()
-        {
-            string upgradeDisplayString = String.Empty;
-            if (IsUpgrade && Packages.Count == 2)
-            {
-                upgradeDisplayString = String.Format("Upgrade {0} {1} to {2} {3}", Title, Packages[1].ThisVersion, Title, Packages[0].ThisVersion);
-            }
-
-            return upgradeDisplayString;
-        }
-
-        public string GetInstalledProductRegistryKey(string keyName)
-        {
-            string keyValue = String.Empty;
-            Package pack = GetPackage();
-            pack.RegistryEntries.TryGetValue(keyName, out keyValue);
-            return keyValue;
-        }
-
-        public List<ProductFeature> GetProductFeatures()
-        {
-            Package pack = GetPackage();
-            return pack.Features;
-        }
-
-        // MSI Installation in a background worker thread.
-        public event ProductInstationActionEventHandler ProductInstallationProgressChanged;
-
-        protected void DoInstallationProgressChange(ChainedInstallerEventArgs c)
-        {
-            if (ProductInstallationProgressChanged != null)
-                ProductInstallationProgressChanged(this, c);
-        }
-
-        private void ChainedInstallerUpdated(object sender, ChainedInstallerEventArgs c)
-        {
-            bgw.ReportProgress(0, c);
-        }
-
-        private void ExecuteMSI(PackageParameters parameters)
-        {
-            Debug.Assert(bgw == null) ;
-            bgw = new BackgroundWorker();
-            bgw.WorkerReportsProgress = true;
-            bgw.WorkerSupportsCancellation = false;
-            bgw.DoWork += new DoWorkEventHandler(bgw_DoExecuteMSI);
-            bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunExecuteMSICompleted);
-            bgw.ProgressChanged += new ProgressChangedEventHandler(bgw_ExecuteMSIProgressChanged);
-            bgw.RunWorkerAsync(parameters);
-        }
-
-        private void bgw_DoExecuteMSI(object sender, DoWorkEventArgs e)
-        {
-            uint[] returnCodes = null;
-            PackageParameters parameters = e.Argument as PackageParameters;
-            ChainedInstaller i = new ChainedInstaller();
-
-            i.AddPackage(parameters);
-            i.Updated += new ChainedInstallerUpdateEventHandler(ChainedInstallerUpdated);
-            try
-            {
-                ChainedInstallerEventArgs cStart = new ChainedInstallerEventArgs();
-                cStart.Action = ChainedInstallerAction.StartInstallation;
-                ChainedInstallerUpdated(this, cStart);
-
-                returnCodes = i.Install();
-            }
-            finally
-            {
-            }
-            e.Result = returnCodes[0];
-
-            ChainedInstallerEventArgs cEnd = new ChainedInstallerEventArgs();
-            cEnd.Action = ChainedInstallerAction.EndInstallation;
-            ChainedInstallerUpdated(this, cEnd);
-
-            return;
-        }
-
-        private void bgw_RunExecuteMSICompleted(object sender, RunWorkerCompletedEventArgs e)
-        {
-            if (e.Result.ToString() == "0")
-            {
-                Logger.LogInformation(String.Format("{0}'s change state request passed.", Name));
-                GetPackage().UpdateOptionalParameters();
-                CurrentState += 1;
-            }
-            else
-            {
-                Logger.LogError(String.Format("{0}'s change state request failed.", Name));
-                CurrentState += 2;
-            }
-            bgw.Dispose();
-            bgw = null;
-
-            ChainedInstallerEventArgs c = new ChainedInstallerEventArgs();
-            c.Action = ChainedInstallerAction.FinalAction;
-            c.ExitCode = (uint) Convert.ToInt32(e.Result);
+  public delegate void ProductInstationActionEventHandler(object sender, ChainedInstallerEventArgs c);
+  public delegate void DownloadProductProgressHandler(object sender, DownloadProgressChangedEventArgs de);
+  public delegate void DownloadProductCompleteHandler(object sender, AsyncCompletedEventArgs ae);
+
+  public class Product : ProductElement
+  {
+    private bool loadedController;
+    private ProductConfigurationController controller;
+    private StringBuilder InstallActionLog;
+    private WebClient wc;
+    private BackgroundWorker bgw;
+    private bool proposedInstalled;
+
+    public List<Package> Packages { get; set; }
+
+    public string TitleWithVersion
+    {
+        get { return String.Format("{0} {1}", Title, GetPackage().ThisVersion); }
+    }
+
+    public ProductConfigurationController Controller
+    {
+        get
+        {
+            if (!loadedController)
+                GetController();
+            return controller;
+        }
+    }
+
+    private Package GetPackage()
+    {
+        if (Packages.Count == 0) return null;
+        if (useCurrentlyInstalledPackage && Packages.Count == 2)
+            return Packages[1];
+        return Packages[0];
+    }
+
+    public Product()
+    {
+        InstallActionLog = new StringBuilder();
+        wc = new WebClient();
+        CurrentState = ProductState.Unknown;
+        useCurrentlyInstalledPackage = false;
+        msiVersion = FileVersionInfo.GetVersionInfo(Environment.SystemDirectory + "\\msi.dll");
+    }
+
+    public void PostInitialize(bool initalizePackage)
+    {
+        Package p = GetPackage();
+        if (initalizePackage)
+        {
+            p.PostInitialize();
+        }
+        p.UrlBase = UrlBase;
+
+        if (String.IsNullOrEmpty(p.Id) == false)  // Valid Package.
+        {
+            // Is the package in our cache?
+            CurrentState = (p.FoundLocal) ? ProductState.FoundLocal : ProductState.WebRemote;
+
+            // Is the package an upgrade for another currently installed product?
+            IsUpgrade = FindReleatedInstalledProducts();
+            if (IsUpgrade)
+                CurrentState = ProductState.WillPerformUpgrade;
+
+            // Is the package installed?
+            proposedInstalled = p.Installed;
+            if (p.Installed)
+                CurrentState = ProductState.CurrentlyInstalled;
+        }
+    }
+
+    public string GetLog()
+    {
+        return InstallActionLog.ToString();
+    }
+
+    protected void SetProposedInstalled(bool shouldInstall)
+    {
+        if (shouldInstall == proposedInstalled) return;
+        proposedInstalled = shouldInstall;
+    }
+
+    [XmlAttribute("upgradeId")]
+    public string UpgradeId { get; set; }
+
+    [XmlAttribute("urlBaseDir")]
+    public string UrlBase { get; set; }
+
+    [XmlIgnore]
+    public bool ProposedInstalled
+    {
+        get { return proposedInstalled; }
+        set { SetProposedInstalled(value); }
+    }
+
+    [XmlIgnore]
+    public bool Installed 
+    {
+        get
+        {
+            return GetPackage().Installed;
+        }
+    }
+
+    [XmlIgnore]
+    public bool FoundLocal 
+    { 
+        get
+        {
+            return GetPackage().FoundLocal;
+        }
+    }
+
+    [XmlIgnore]
+    public bool IsUpgrade { get; private set; }
+
+    [XmlIgnore]
+    public ProductState CurrentState { get; private set; }
+
+    [XmlIgnore]
+    private Queue<string> MirrorList { get; set; }
+
+    private bool useCurrentlyInstalledPackage;
+    [XmlIgnore]
+    public bool UseCurrentlyInstalledPackage 
+    {
+        get
+        {
+            return useCurrentlyInstalledPackage;
+        }
+        set
+        {
+            if (useCurrentlyInstalledPackage != value)
+            {
+                useCurrentlyInstalledPackage = value;
+                PostInitialize(false);
+            }
+        }
+    }
+
+    [XmlIgnore]
+    public bool IsServerProduct
+    {
+        get { return Name.StartsWith("mysql-server"); }
+    }
+
+    public bool HasChanges()
+    {
+      bool hasChanges;
+      hasChanges = Installed != ProposedInstalled;
+      foreach (ProductFeature feature in GetPackage().Features)
+        hasChanges |= feature.HasChanges();
+      return hasChanges;
+    }
+    
+    public string GetUpgradeDisplayString()
+    {
+        string upgradeDisplayString = String.Empty;
+        if (IsUpgrade && Packages.Count == 2)
+        {
+            upgradeDisplayString = String.Format("Upgrade {0} {1} to {2} {3}", Title, Packages[1].ThisVersion, Title, Packages[0].ThisVersion);
+        }
+
+        return upgradeDisplayString;
+    }
+
+    public string GetInstalledProductRegistryKey(string keyName)
+    {
+        string keyValue = String.Empty;
+        Package pack = GetPackage();
+        pack.RegistryEntries.TryGetValue(keyName, out keyValue);
+        return keyValue;
+    }
+
+    public List<ProductFeature> GetProductFeatures()
+    {
+        Package pack = GetPackage();
+        return pack.Features;
+    }
+
+    // MSI Installation in a background worker thread.
+    public event ProductInstationActionEventHandler ProductInstallationProgressChanged;
+
+    protected void DoInstallationProgressChange(ChainedInstallerEventArgs c)
+    {
+        if (ProductInstallationProgressChanged != null)
+            ProductInstallationProgressChanged(this, c);
+    }
+
+    private void ChainedInstallerUpdated(object sender, ChainedInstallerEventArgs c)
+    {
+        bgw.ReportProgress(0, c);
+    }
+
+    private void ExecuteMSI(PackageParameters parameters)
+    {
+        Debug.Assert(bgw == null) ;
+        bgw = new BackgroundWorker();
+        bgw.WorkerReportsProgress = true;
+        bgw.WorkerSupportsCancellation = false;
+        bgw.DoWork += new DoWorkEventHandler(bgw_DoExecuteMSI);
+        bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunExecuteMSICompleted);
+        bgw.ProgressChanged += new ProgressChangedEventHandler(bgw_ExecuteMSIProgressChanged);
+        bgw.RunWorkerAsync(parameters);
+    }
+
+    private void bgw_DoExecuteMSI(object sender, DoWorkEventArgs e)
+    {
+        uint[] returnCodes = null;
+        PackageParameters parameters = e.Argument as PackageParameters;
+        ChainedInstaller i = new ChainedInstaller();
+
+        i.AddPackage(parameters);
+        i.Updated += new ChainedInstallerUpdateEventHandler(ChainedInstallerUpdated);
+        try
+        {
+            ChainedInstallerEventArgs cStart = new ChainedInstallerEventArgs();
+            cStart.Action = ChainedInstallerAction.StartInstallation;
+            ChainedInstallerUpdated(this, cStart);
+
+            returnCodes = i.Install();
+        }
+        finally
+        {
+        }
+        e.Result = returnCodes[0];
+
+        ChainedInstallerEventArgs cEnd = new ChainedInstallerEventArgs();
+        cEnd.Action = ChainedInstallerAction.EndInstallation;
+        ChainedInstallerUpdated(this, cEnd);
+
+        return;
+    }
+
+    private void bgw_RunExecuteMSICompleted(object sender, RunWorkerCompletedEventArgs e)
+    {
+        if (e.Result.ToString() == "0")
+        {
+            Logger.LogInformation(String.Format("{0}'s change state request passed.", Name));
+            GetPackage().UpdateOptionalParameters();
+            CurrentState += 1;
+        }
+        else
+        {
+            Logger.LogError(String.Format("{0}'s change state request failed.", Name));
+            CurrentState += 2;
+        }
+        bgw.Dispose();
+        bgw = null;
+
+        ChainedInstallerEventArgs c = new ChainedInstallerEventArgs();
+        c.Action = ChainedInstallerAction.FinalAction;
+        c.ExitCode = (uint) Convert.ToInt32(e.Result);
+        DoInstallationProgressChange(c);
+
+        return;
+    }
+
+    private void bgw_ExecuteMSIProgressChanged(object sender, ProgressChangedEventArgs e)
+    {
+        if (e.ProgressPercentage == 0)
+        {
+            ChainedInstallerEventArgs c = e.UserState as ChainedInstallerEventArgs;
+            switch (c.Action)
+            {
+                case ChainedInstallerAction.StartInstallation:
+                    InstallActionLog.AppendLine(c.Message);
+                    if (CurrentState == ProductState.InstallStarted ||
+                        CurrentState == ProductState.UpdateStarted ||
+                        CurrentState == ProductState.RemoveStarted)
+                        CurrentState += 1;
+                    break;
+                case ChainedInstallerAction.HandleActionData:
+                    InstallActionLog.AppendLine(c.Message);
+                    break;
+                case ChainedInstallerAction.HandleActionStart:
+                    InstallActionLog.AppendLine(c.Message);
+                    break;
+                case ChainedInstallerAction.ProgressSetRange:
+                    InstallActionLog.AppendLine(String.Format("Min: {0} Max: {1}", c.ProgressMin.ToString(), c.ProgressMax.ToString()));
+                    break;
+                case ChainedInstallerAction.ProgressSetStep:
+                    InstallActionLog.AppendLine(String.Format("Step: {0}", c.ProgressStep.ToString()));
+                    break;
+                case ChainedInstallerAction.ProgressSetPosition:
+                    InstallActionLog.AppendLine(String.Format("Position: {0}", c.ProgressPosition.ToString()));
+                    break;
+                case ChainedInstallerAction.ProgressSingleStep:
+                    InstallActionLog.AppendLine("DoStep");
+                    break;
+                case ChainedInstallerAction.EndInstallation:
+                    InstallActionLog.AppendLine(c.Message);
+                    break;
+                case ChainedInstallerAction.LogEvent:
+                    InstallActionLog.AppendLine(c.Message);
+                    break;
+                case ChainedInstallerAction.TerminateUI:
+                    InstallActionLog.AppendLine(c.Message);
+                    break;
+            }
             DoInstallationProgressChange(c);
-
-            return;
-        }
-
-        private void bgw_ExecuteMSIProgressChanged(object sender, ProgressChangedEventArgs e)
-        {
-            if (e.ProgressPercentage == 0)
-            {
-                ChainedInstallerEventArgs c = e.UserState as ChainedInstallerEventArgs;
-                switch (c.Action)
-                {
-                    case ChainedInstallerAction.StartInstallation:
-                        InstallActionLog.AppendLine(c.Message);
-                        if (CurrentState == ProductState.InstallStarted ||
-                            CurrentState == ProductState.UpdateStarted ||
-                            CurrentState == ProductState.RemoveStarted)
-                            CurrentState += 1;
-                        break;
-                    case ChainedInstallerAction.HandleActionData:
-                        InstallActionLog.AppendLine(c.Message);
-                        break;
-                    case ChainedInstallerAction.HandleActionStart:
-                        InstallActionLog.AppendLine(c.Message);
-                        break;
-                    case ChainedInstallerAction.ProgressSetRange:
-                        InstallActionLog.AppendLine(String.Format("Min: {0} Max: {1}", c.ProgressMin.ToString(), c.ProgressMax.ToString()));
-                        break;
-                    case ChainedInstallerAction.ProgressSetStep:
-                        InstallActionLog.AppendLine(String.Format("Step: {0}", c.ProgressStep.ToString()));
-                        break;
-                    case ChainedInstallerAction.ProgressSetPosition:
-                        InstallActionLog.AppendLine(String.Format("Position: {0}", c.ProgressPosition.ToString()));
-                        break;
-                    case ChainedInstallerAction.ProgressSingleStep:
-                        InstallActionLog.AppendLine("DoStep");
-                        break;
-                    case ChainedInstallerAction.EndInstallation:
-                        InstallActionLog.AppendLine(c.Message);
-                        break;
-                    case ChainedInstallerAction.LogEvent:
-                        InstallActionLog.AppendLine(c.Message);
-                        break;
-                    case ChainedInstallerAction.TerminateUI:
-                        InstallActionLog.AppendLine(c.Message);
-                        break;
-                }
-                DoInstallationProgressChange(c);
-            }
-        }
-
-        private string GetInstallPath()
-        {
-            string fullInstallPath = InstallerConfiguration.ProposedInstallationPath;
-            if (!fullInstallPath.EndsWith("\\"))
-                fullInstallPath += "\\";
-            fullInstallPath += TitleWithVersion.Replace('/', ' ');
-
-            if (Name == "Connector-odbc" || Name.Contains("mysql-server"))
-            {
-                fullInstallPath = fullInstallPath.Remove(fullInstallPath.LastIndexOf('.'));
-            }
-            if (Name == "Workbench")
-            {
-                fullInstallPath = fullInstallPath.Remove(fullInstallPath.LastIndexOf('.')) + " CE";
-            }
+        }
+    }
+
+    private string GetInstallPath()
+    {
+        string fullInstallPath = InstallerConfiguration.ProposedInstallationPath;
+        if (!fullInstallPath.EndsWith("\\"))
             fullInstallPath += "\\";
-
-            return fullInstallPath;
-        }
-
-        public void Install()
-        {
-            Package p = GetPackage();
-            if (p != null)
-            {
-                PackageParameters parameters = new PackageParameters(p.FullPath, InstallAction.Install);
-                parameters.CommandLine = String.Format("ALLUSERS=1 INSTALLDIR=\"{0}\" {1}", GetInstallPath(), p.GetCommandLine());
-
-                Logger.LogInformation(String.Format("Beginning installation of {0} with options {1}", Name, parameters.CommandLine));
-                CurrentState = ProductState.InstallStarted;
-                ExecuteMSI(parameters);
-            }
-
-            return;
-        }
-
-        public void Remove()
-        {
-            Package p = GetPackage();
-
-            if (p != null)
-            {
-                PackageParameters parameters = new PackageParameters(p.FullPath, InstallAction.Uninstall);
-                if (p.FoundLocal == false)
-                {
-                    parameters.UseProductCode = true;
-                    parameters.Path = p.Id;
-                }
-
-                Logger.LogInformation(String.Format("Beginning removal of {0}.", Name));
-                CurrentState = ProductState.RemoveStarted;
-                ExecuteMSI(parameters);
-            }
-
-            return;
-        }
-
-        public void Update()
-        {
-            Package p = GetPackage();
-            if (p != null && p.FullPath.Length > 0)
-            {
-                PackageParameters parameters = new PackageParameters(p.FullPath, InstallAction.Reinstall);
-                parameters.CommandLine = p.GetCommandLine();
-
-                Logger.LogInformation(String.Format("Beginning update of {0} with options {1}", Name, parameters.CommandLine));
-                CurrentState = ProductState.UpdateStarted;
-                ExecuteMSI(parameters);
-            }
-            else
-                CurrentState = ProductState.UpdateFailed;
-
-            return;
-        }
-
-        public void MakeChanges()
-        {
-            if (Installed && ProposedInstalled)
-            {
-                Update();
-            }
-            else if (!Installed && ProposedInstalled)
-            {
-                    Install();
-            }
-            else if (Installed && !ProposedInstalled)
-                Remove();
-        }
-
-        public long GetInstallationSizeEstimate()
-        {
-            return GetPackage().GetInstallationSizeEstimate();
-        }
-
-        private string GetMSIProductInfo(string productId, string propertyName)
-        {
-            int productInfoLength = 512;
-            StringBuilder productInfo = new StringBuilder(productInfoLength);
-            MSIEnumError status = MsiInterop.MsiGetProductInfo(productId, propertyName, productInfo, ref productInfoLength);
-
-            return (status == MSIEnumError.Success) ? productInfo.ToString() : String.Empty;
-        }
-
-        private FileVersionInfo msiVersion;
-        private bool PopulateRelatePackageFeatures(Package targetPackage)
-        {
-            bool FoundInstalledPackage = false;
-
-            // Disable MSI UI.
-            InstallUILevel internalUILevel = InstallUILevel.None;
-
-            if (msiVersion.FileMajorPart >= 5)
-            {
-                // Allow to installer to elevate, even in silent mode
-                internalUILevel |= InstallUILevel.UACOnly;
-            }
-            MsiInterop.MsiSetInternalUI(internalUILevel, IntPtr.Zero);
-
-            UIntPtr packageHandle = UIntPtr.Zero;
-            uint openProductResult = MsiInterop.MsiOpenProduct(targetPackage.Id, ref packageHandle);
-            if (openProductResult == 0)
-            {
-                UIntPtr databaseHandle = MsiInterop.MsiGetActiveDatabase(packageHandle);
-                if (databaseHandle != UIntPtr.Zero)
-                {
-                    UIntPtr view = UIntPtr.Zero;
-                    MSIEnumError queryResult = MsiInterop.MsiDatabaseOpenView(databaseHandle,
-                                                                              "SELECT DISTINCT Feature.Feature, Feature.Feature_Parent, Feature.Title, Feature.Description, Feature.Display, FeatureComponents.Feature_ FROM Feature, FeatureComponents WHERE Feature.Feature = FeatureComponents.Feature_ ORDER BY Feature.Feature_Parent",
-                                                                              ref view);
-                    if (queryResult == MSIEnumError.Success)
+        fullInstallPath += TitleWithVersion.Replace('/', ' ');
+
+        if (Name == "Connector-odbc" || Name.Contains("mysql-server"))
+        {
+            fullInstallPath = fullInstallPath.Remove(fullInstallPath.LastIndexOf('.'));
+        }
+        if (Name == "Workbench")
+        {
+            fullInstallPath = fullInstallPath.Remove(fullInstallPath.LastIndexOf('.')) + " CE";
+        }
+        fullInstallPath += "\\";
+
+        return fullInstallPath;
+    }
+
+    public void Install()
+    {
+        Package p = GetPackage();
+        if (p != null)
+        {
+            PackageParameters parameters = new PackageParameters(p.FullPath, InstallAction.Install);
+            parameters.CommandLine = String.Format("ALLUSERS=1 INSTALLDIR=\"{0}\" {1}", GetInstallPath(), p.GetCommandLine());
+
+            Logger.LogInformation(String.Format("Beginning installation of {0} with options {1}", Name, parameters.CommandLine));
+            CurrentState = ProductState.InstallStarted;
+            ExecuteMSI(parameters);
+        }
+
+        return;
+    }
+
+    public void Remove()
+    {
+        Package p = GetPackage();
+
+        if (p != null)
+        {
+            PackageParameters parameters = new PackageParameters(p.FullPath, InstallAction.Uninstall);
+            if (p.FoundLocal == false)
+            {
+                parameters.UseProductCode = true;
+                parameters.Path = p.Id;
+            }
+
+            Logger.LogInformation(String.Format("Beginning removal of {0}.", Name));
+            CurrentState = ProductState.RemoveStarted;
+            ExecuteMSI(parameters);
+        }
+
+        return;
+    }
+
+    public void Update()
+    {
+        Package p = GetPackage();
+        if (p != null && p.FullPath.Length > 0)
+        {
+            PackageParameters parameters = new PackageParameters(p.FullPath, InstallAction.Reinstall);
+            parameters.CommandLine = p.GetCommandLine();
+
+            Logger.LogInformation(String.Format("Beginning update of {0} with options {1}", Name, parameters.CommandLine));
+            CurrentState = ProductState.UpdateStarted;
+            ExecuteMSI(parameters);
+        }
+        else
+            CurrentState = ProductState.UpdateFailed;
+
+        return;
+    }
+
+    public void MakeChanges()
+    {
+        if (Installed && ProposedInstalled)
+        {
+            Update();
+        }
+        else if (!Installed && ProposedInstalled)
+        {
+                Install();
+        }
+        else if (Installed && !ProposedInstalled)
+            Remove();
+    }
+
+    public long GetInstallationSizeEstimate()
+    {
+        return GetPackage().GetInstallationSizeEstimate();
+    }
+
+    private string GetMSIProductInfo(string productId, string propertyName)
+    {
+        int productInfoLength = 512;
+        StringBuilder productInfo = new StringBuilder(productInfoLength);
+        MSIEnumError status = MsiInterop.MsiGetProductInfo(productId, propertyName, productInfo, ref productInfoLength);
+
+        return (status == MSIEnumError.Success) ? productInfo.ToString() : String.Empty;
+    }
+
+    private FileVersionInfo msiVersion;
+    private bool PopulateRelatePackageFeatures(Package targetPackage)
+    {
+        bool FoundInstalledPackage = false;
+
+        // Disable MSI UI.
+        InstallUILevel internalUILevel = InstallUILevel.None;
+
+        if (msiVersion.FileMajorPart >= 5)
+        {
+            // Allow to installer to elevate, even in silent mode
+            internalUILevel |= InstallUILevel.UACOnly;
+        }
+        MsiInterop.MsiSetInternalUI(internalUILevel, IntPtr.Zero);
+
+        UIntPtr packageHandle = UIntPtr.Zero;
+        uint openProductResult = MsiInterop.MsiOpenProduct(targetPackage.Id, ref packageHandle);
+        if (openProductResult == 0)
+        {
+            UIntPtr databaseHandle = MsiInterop.MsiGetActiveDatabase(packageHandle);
+            if (databaseHandle != UIntPtr.Zero)
+            {
+                UIntPtr view = UIntPtr.Zero;
+                MSIEnumError queryResult = MsiInterop.MsiDatabaseOpenView(databaseHandle,
+                                                                          "SELECT DISTINCT Feature.Feature, Feature.Feature_Parent, Feature.Title, Feature.Description, Feature.Display, FeatureComponents.Feature_ FROM Feature, FeatureComponents WHERE Feature.Feature = FeatureComponents.Feature_ ORDER BY Feature.Feature_Parent",
+                                                                          ref view);
+                if (queryResult == MSIEnumError.Success)
+                {
+                    uint executeResult = MsiInterop.MsiViewExecute(view, UIntPtr.Zero);
+                    if (executeResult == 0)
                     {
-                        uint executeResult = MsiInterop.MsiViewExecute(view, UIntPtr.Zero);
-                        if (executeResult == 0)
+                        UIntPtr results = UIntPtr.Zero;
+                        MSIEnumError fetchResults = MSIEnumError.NoMoreItems;
+                        do
                         {
-                            UIntPtr results = UIntPtr.Zero;
-                            MSIEnumError fetchResults = MSIEnumError.NoMoreItems;
-                            do
+                            fetchResults = MsiInterop.MsiViewFetch(view, ref results);
+                            if (fetchResults == MSIEnumError.Success)
                             {
-                                fetchResults = MsiInterop.MsiViewFetch(view, ref results);
-                                if (fetchResults == MSIEnumError.Success)
-                                {
-                                    //Create a new ProductFeature and use these values. 
-                                    ProductFeature pf = new ProductFeature();
+                                //Create a new ProductFeature and use these values. 
+                                ProductFeature pf = new ProductFeature();
 
-                                    int valueLength = 256;
-                                    StringBuilder value = new StringBuilder(valueLength);
+                                int valueLength = 256;
+                                StringBuilder value = new StringBuilder(valueLength);
                                     
-                                    uint recordResult = MsiInterop.MsiRecordGetString(results, 1, value, ref valueLength);
-                                    if (recordResult == 0)
+                                uint recordResult = MsiInterop.MsiRecordGetString(results, 1, value, ref valueLength);
+                                if (recordResult == 0)
+                                {
+                                    pf.Name = value.ToString();
+                                }
+                                value.Remove(0, value.Length);
+                                valueLength = 256;
+
+                                recordResult = MsiInterop.MsiRecordGetString(results, 2, value, ref valueLength);
+                                if (recordResult == 0)
+                                {
+                                    if (value.Length == 0)
                                     {
-                                        pf.Name = value.ToString();
+                                        targetPackage.Features.Add(pf);
                                     }
-                                    value.Remove(0, value.Length);
-                                    valueLength = 256;
-
-                                    recordResult = MsiInterop.MsiRecordGetString(results, 2, value, ref valueLength);
-                                    if (recordResult == 0)
+                                    else
                                     {
-                                        if (value.Length == 0)
-                                        {
-                                            targetPackage.Features.Add(pf);
-                                        }
-                                        else
-                                        {
-                                            string parentName = value.ToString();
-                                            foreach (ProductFeature parentFeature in targetPackage.Features)
+                                        string parentName = value.ToString();
+                                        foreach (ProductFeature parentFeature in targetPackage.Features)
+                                        {
+                                            if (parentFeature.Name == parentName)
                                             {
-                                                if (parentFeature.Name == parentName)
-                                                {
-                                                    parentFeature.Features.Add(pf);
-                                                    break;
-                                                }
+                                                parentFeature.Features.Add(pf);
+                                                break;
                                             }
                                         }
                                     }
-                                    value.Remove(0, value.Length);
-                                    valueLength = 256;
-
-                                    recordResult = MsiInterop.MsiRecordGetString(results, 3, value, ref valueLength);
-                                    if (recordResult == 0)
-                                    {
-                                        pf.Title = value.ToString();
-                                    }
-                                    value.Remove(0, value.Length);
-                                    valueLength = 256;
-
-                                    recordResult = MsiInterop.MsiRecordGetString(results, 4, value, ref valueLength);
-                                    if (recordResult == 0)
-                                    {
-                                        pf.Description = value.ToString();
-                                    }
-                                    value.Remove(0, value.Length);
-                                    valueLength = 256;
-
-                                    recordResult = MsiInterop.MsiRecordGetString(results, 5, value, ref valueLength);
-                                    if (recordResult == 0)
-                                    {
-                                        pf.Display = value.ToString();
-                                    }
-                                    value.Remove(0, value.Length);
-                                    valueLength = 256;
-
-                                    recordResult = MsiInterop.MsiRecordGetString(results, 6, value, ref valueLength);
-                                    if (recordResult == 0)
-                                    {
-                                        pf.HasComponents = value.ToString();
-                                    }
-                                    value.Remove(0, value.Length);
-                                    valueLength = 256;
-                                }
-                                MsiInterop.MsiCloseHandle(results);
-                            }
-                            while (fetchResults == MSIEnumError.Success);
-
-                            if (fetchResults != MSIEnumError.NoMoreItems)
-                            {
-                                Logger.LogError("Failed to completely fetch features from database.");
-                            }
-                            else
-                            {
-                                FoundInstalledPackage = true;
-                            }
+                                }
+                                value.Remove(0, value.Length);
+                                valueLength = 256;
+
+                                recordResult = MsiInterop.MsiRecordGetString(results, 3, value, ref valueLength);
+                                if (recordResult == 0)
+                                {
+                                    pf.Title = value.ToString();
+                                }
+                                value.Remove(0, value.Length);
+                                valueLength = 256;
+
+                                recordResult = MsiInterop.MsiRecordGetString(results, 4, value, ref valueLength);
+                                if (recordResult == 0)
+                                {
+                                    pf.Description = value.ToString();
+                                }
+                                value.Remove(0, value.Length);
+                                valueLength = 256;
+
+                                recordResult = MsiInterop.MsiRecordGetString(results, 5, value, ref valueLength);
+                                if (recordResult == 0)
+                                {
+                                    pf.Display = value.ToString();
+                                }
+                                value.Remove(0, value.Length);
+                                valueLength = 256;
+
+                                recordResult = MsiInterop.MsiRecordGetString(results, 6, value, ref valueLength);
+                                if (recordResult == 0)
+                                {
+                                    pf.HasComponents = value.ToString();
+                                }
+                                value.Remove(0, value.Length);
+                                valueLength = 256;
+                            }
+                            MsiInterop.MsiCloseHandle(results);
+                        }
+                        while (fetchResults == MSIEnumError.Success);
+
+                        if (fetchResults != MSIEnumError.NoMoreItems)
+                        {
+                            Logger.LogError("Failed to completely fetch features from database.");
                         }
                         else
                         {
-                            Logger.LogError("Failed to execute custom feature query");
+                            FoundInstalledPackage = true;
                         }
                     }
                     else
                     {
-                        Logger.LogError("Failed to open custom feature view.");
+                        Logger.LogError("Failed to execute custom feature query");
                     }
-                    MsiInterop.MsiCloseHandle(view);
                 }
                 else
                 {
-                    Logger.LogError("Failed to open database handle.");
+                    Logger.LogError("Failed to open custom feature view.");
                 }
-                MsiInterop.MsiCloseHandle(databaseHandle);
+                MsiInterop.MsiCloseHandle(view);
             }
             else
             {
-                Logger.LogError("Failed to open pacakge handle.");
-            }
-            MsiInterop.MsiCloseHandle(packageHandle);
-
-            return FoundInstalledPackage;
-        }
-
-        private bool FindReleatedInstalledProducts()
-        {
-            bool FoundUpgradeableProducts = false;
-
-            if (UpgradeId.Length > 0)
-            {
-                StringBuilder ProductCode = new StringBuilder(39);
-                uint CurrentIndex = 0;
-                MSIEnumError rc = MSIEnumError.Success;
-
-                do
-                {
-                    if (ProductCode.Length > 0)  // else this.Installed == true;
-                    {
-                        Package p = GetPackage();
-                        string relatedProductCode = ProductCode.ToString();
-                        if (relatedProductCode != p.Id)
-                        {
-                            if (Packages.Count == 2)
-                            {
-                                // No need to search again because there can be only 2 packages.
-                                FoundUpgradeableProducts = true;
-                            }
-                            else
-                            {
-                                Package possibleOlderVersion = new Package();
-                                possibleOlderVersion.Id = relatedProductCode;
-
-                                if (p.ThisVersion.Substring(0, 3) == possibleOlderVersion.ThisVersion.Substring(0, 3)) // Same MAJOR.MINOR family
-                                {
-                                    int olderMinor = Convert.ToInt32(possibleOlderVersion.ThisVersion.Substring(possibleOlderVersion.ThisVersion.LastIndexOf('.') + 1));
-                                    int thisMinor = Convert.ToInt32(p.ThisVersion.Substring(p.ThisVersion.LastIndexOf('.') + 1));
-
-                                    if (thisMinor > olderMinor)
-                                    {
-                                        FoundUpgradeableProducts = PopulateRelatePackageFeatures(possibleOlderVersion);
-                                        if (FoundUpgradeableProducts == true)
-                                        {
-                                            possibleOlderVersion.PostInitialize();
-                                            possibleOlderVersion.UrlBase = p.UrlBase;
-                                            Packages.Add(possibleOlderVersion);
-                                        }
-                                    }
-                                    else
-                                    {
-                                        // The install will fail because a newer version of the MSI is available.  
-                                        // Maybe, the current package should be replaced with a from memory version since everything is INSTALLED?
-                                        Logger.LogError("A newer product has been found but we're not sure what to do with it yet so hold this place.");
-                                    }
-                                }
-                            }
-                        }
-                        ProductCode.Remove(0, ProductCode.Length);
-                    }
-                    try
-                    {
-                        rc = (MSIEnumError)MsiInterop.MsiEnumRelatedProducts(UpgradeId, 0, CurrentIndex++, ProductCode);
-                    }
-                    catch (Exception e)
-                    {
-                        InstallActionLog.AppendLine(e.Message);
-                    }
-                }
-                while (rc == MSIEnumError.Success);
-
-                if (rc != MSIEnumError.NoMoreItems)
-                {
-                    // Record the real error.
-                    InstallActionLog.AppendLine(String.Format("Found error: {0}", rc.ToString()));
-                }
-            }
-
-            return FoundUpgradeableProducts;
-        }
-
-        // Download
-        private void DownloadExternalIPString(Object sender, DownloadStringCompletedEventArgs e)
-        {
-            if (!e.Cancelled && e.Error == null)
-            {
-                MirrorList = new Queue<string>();
-
-                string mirrorList = (string)e.Result;
-                mirrorList = Regex.Replace(mirrorList, "<ip", "<IP");
-                mirrorList = Regex.Replace(mirrorList, "ip>", "IP>");
-
-                // Parse the mirror list here.
-                XmlRootAttribute thisRequest = new XmlRootAttribute("request");
-                XmlSerializer s = new XmlSerializer(typeof(IP[]), thisRequest);
-                TextReader w = new StringReader(mirrorList);
-                IP[] httpMirrors = (IP[])s.Deserialize(w);
-                if (httpMirrors.Length > 0)
-                {
-                    Queue<string> altMirrorList = new Queue<string>();
-
-                    foreach (Mirror m in httpMirrors[0].Mirrors)
-                    {
-                        if (m.Primary == "1")
-                        {
-                            if (m.Http_Url != null)
-                                MirrorList.Enqueue(m.Http_Url);
+                Logger.LogError("Failed to open database handle.");
+            }
+            MsiInterop.MsiCloseHandle(databaseHandle);
+        }
+        else
+        {
+            Logger.LogError("Failed to open pacakge handle.");
+        }
+        MsiInterop.MsiCloseHandle(packageHandle);
+
+        return FoundInstalledPackage;
+    }
+
+    private bool FindReleatedInstalledProducts()
+    {
+        bool FoundUpgradeableProducts = false;
+
+        if (UpgradeId.Length > 0)
+        {
+            StringBuilder ProductCode = new StringBuilder(39);
+            uint CurrentIndex = 0;
+            MSIEnumError rc = MSIEnumError.Success;
+
+            do
+            {
+                if (ProductCode.Length > 0)  // else this.Installed == true;
+                {
+                    Package p = GetPackage();
+                    string relatedProductCode = ProductCode.ToString();
+                    if (relatedProductCode != p.Id)
+                    {
+                        if (Packages.Count == 2)
+                        {
+                            // No need to search again because there can be only 2 packages.
+                            FoundUpgradeableProducts = true;
                         }
                         else
                         {
-                            if (m.Http_Url != null)
-                                altMirrorList.Enqueue(m.Http_Url);
+                            Package possibleOlderVersion = new Package();
+                            possibleOlderVersion.Id = relatedProductCode;
+
+                            if (p.ThisVersion.Substring(0, 3) == possibleOlderVersion.ThisVersion.Substring(0, 3)) // Same MAJOR.MINOR family
+                            {
+                                int olderMinor = Convert.ToInt32(possibleOlderVersion.ThisVersion.Substring(possibleOlderVersion.ThisVersion.LastIndexOf('.') + 1));
+                                int thisMinor = Convert.ToInt32(p.ThisVersion.Substring(p.ThisVersion.LastIndexOf('.') + 1));
+
+                                if (thisMinor > olderMinor)
+                                {
+                                    FoundUpgradeableProducts = PopulateRelatePackageFeatures(possibleOlderVersion);
+                                    if (FoundUpgradeableProducts == true)
+                                    {
+                                        possibleOlderVersion.PostInitialize();
+                                        possibleOlderVersion.UrlBase = p.UrlBase;
+                                        Packages.Add(possibleOlderVersion);
+                                    }
+                                }
+                                else
+                                {
+                                    // The install will fail because a newer version of the MSI is available.  
+                                    // Maybe, the current package should be replaced with a from memory version since everything is INSTALLED?
+                                    Logger.LogError("A newer product has been found but we're not sure what to do with it yet so hold this place.");
+                                }
+                            }
                         }
                     }
-
-                    foreach (string m in altMirrorList)
-                    {
-                        MirrorList.Enqueue(m);
-                    }
-
-                    if (String.IsNullOrEmpty(httpMirrors[0].Archive) == false)
-                        MirrorList.Enqueue(httpMirrors[0].Archive);
+                    ProductCode.Remove(0, ProductCode.Length);
                 }
-                MirrorList.Enqueue(GetPackage().RemotePath);
-
-                DownloadProduct();
-            }
-        }
-
-        private void DownloadMirrorList(string fileName)
-        {
-            Uri mirrorUri = new Uri(String.Format("http://dev.mysql.com/downloads/get_mirror.php?filename={0}", fileName));
-
-            wc.DownloadStringCompleted += new DownloadStringCompletedEventHandler(DownloadExternalIPString);
-            try
-            {
-                wc.DownloadStringAsync(mirrorUri);
-            }
-            catch
-            {
-                CurrentState = ProductState.DownloadError;
-            }
-        }
-
-        public event DownloadProductCompleteHandler DownloadProductCompleted;
-
-        public event DownloadProductProgressHandler DownloadProductProgressChanged;
-
-        private void DownloadProductComplete(object sender, AsyncCompletedEventArgs e)
-        {
-            if (e.Cancelled == false && e.Error == null)
-            {
                 try
                 {
-                    Package p = GetPackage();
-                    File.Delete(p.FullPath);
-                    File.Move(p.TempFileName, p.FullPath);
-                    CurrentState = ProductState.DownloadSuccess;
+                    rc = (MSIEnumError)MsiInterop.MsiEnumRelatedProducts(UpgradeId, 0, CurrentIndex++, ProductCode);
                 }
-                catch
+                catch (Exception e)
                 {
-                    // Log the failed file operation.
-                    CurrentState = ProductState.Unknown;
+                    InstallActionLog.AppendLine(e.Message);
                 }
             }
+            while (rc == MSIEnumError.Success);
+
+            if (rc != MSIEnumError.NoMoreItems)
+            {
+                // Record the real error.
+                InstallActionLog.AppendLine(String.Format("Found error: {0}", rc.ToString()));
+            }
+        }
+
+        return FoundUpgradeableProducts;
+    }
+
+    // Download
+    private void DownloadExternalIPString(Object sender, DownloadStringCompletedEventArgs e)
+    {
+      if (!e.Cancelled && e.Error == null)
+      {
+        MirrorList = new Queue<string>();
+
+        string mirrorList = (string)e.Result;
+        mirrorList = Regex.Replace(mirrorList, "<ip", "<IP");
+        mirrorList = Regex.Replace(mirrorList, "ip>", "IP>");
+
+        // Parse the mirror list here.
+        XmlRootAttribute thisRequest = new XmlRootAttribute("request");
+        XmlSerializer s = new XmlSerializer(typeof(IP[]), thisRequest);
+        TextReader w = new StringReader(mirrorList);
+        IP[] httpMirrors = (IP[])s.Deserialize(w);
+        if (httpMirrors.Length > 0)
+        {
+          Queue<string> altMirrorList = new Queue<string>();
+
+          foreach (Mirror m in httpMirrors[0].Mirrors)
+          {
+            if (m.Primary == "1")
+            {
+              if (m.Http_Url != null)
+                MirrorList.Enqueue(m.Http_Url);
+            }
             else
             {
-                CurrentState = ProductState.DownloadError;
+              if (m.Http_Url != null)
+                altMirrorList.Enqueue(m.Http_Url);
+            }
+          }
+
+          foreach (string m in altMirrorList)
+          {
+            MirrorList.Enqueue(m);
+          }
+
+          if (String.IsNullOrEmpty(httpMirrors[0].Archive) == false)
+              MirrorList.Enqueue(httpMirrors[0].Archive);
+        }
+
+        string packagePath = GetPackage().RemotePath;
+        if (!String.IsNullOrEmpty(packagePath))
+          MirrorList.Enqueue(packagePath);
+
+        DownloadProduct();
+      }
+    }
+
+    private void DownloadMirrorList(string fileName)
+    {
+        Uri mirrorUri = new Uri(String.Format("http://dev.mysql.com/downloads/get_mirror.php?filename={0}", fileName));
+
+        wc.DownloadStringCompleted += new DownloadStringCompletedEventHandler(DownloadExternalIPString);
+        try
+        {
+            wc.DownloadStringAsync(mirrorUri);
+        }
+        catch
+        {
+            CurrentState = ProductState.DownloadError;
+        }
+    }
+
+    public event DownloadProductCompleteHandler DownloadProductCompleted;
+
+    public event DownloadProductProgressHandler DownloadProductProgressChanged;
+
+    private void DownloadProductComplete(object sender, AsyncCompletedEventArgs e)
+    {
+        if (e.Cancelled == false && e.Error == null)
+        {
+            try
+            {
                 Package p = GetPackage();
-
-                if (File.Exists(p.TempFileName))
-                {
-                    File.Delete(p.TempFileName);
-                }
-            }
-
-            if (DownloadProductCompleted != null)
-            {
-                DownloadProductCompleted(this, e);
-            }
-
-            return;
-        }
-
-        private void DownloadProductProgress(object sender, DownloadProgressChangedEventArgs e)
-        {
-            if (CurrentState == ProductState.DownloadStarted)
-                CurrentState = ProductState.DownloadInProgress;
-
-            if (CurrentState == ProductState.DownloadInProgress)
-            {
-                if (DownloadProductProgressChanged != null)
-                {
-                    DownloadProductProgressChanged(this, e);
-                }
-            }
-
-            return;
-        }
-
-        private void DownloadProduct()
-        {
-            Package p = GetPackage();
-
-            if (p != null)
-            {
-                foreach (string mirror in MirrorList)
-                {
-                    Uri mirrorUri = new Uri(mirror);
-
-                    try
-                    {
-                        Logger.LogInformation(String.Format("Attempting to download {0} from {1}.", p.FileName, mirror));
-                        wc.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadProductComplete);
-                        wc.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownloadProductProgress);
-                        wc.DownloadFileAsync(mirrorUri, p.TempFileName, mirror);
-                        break;
-                    }
-                    catch (Exception e)
-                    {
-                        CurrentState = ProductState.DownloadMirrorSelection;
-                        Logger.LogError(e.Message);
-                    }
-                }
-
-
-                if (CurrentState != ProductState.DownloadStarted)
-                    CurrentState = ProductState.DownloadError;
-            }
-        }
-
-        public void Download()
-        {
-            Package p = GetPackage();
-
-            if (p != null)
-            {
-                if (p.FoundLocal)
-                {
-                    Logger.LogInformation(String.Format("Product {0} found local.", Name));
-                    CurrentState = ProductState.FoundLocal;
-                }
-                else
-                {
-                    CurrentState = ProductState.DownloadStarted;
-                    DownloadMirrorList(p.FileName);
-                }
-            }
-        }
-
-        // Configuration Controller.
-        private void GetController()
-        {
-            if (loadedController) return;
-            controller = PluginManager.GetController(Name);
-            if (controller != null)
-                controller.Owner = this;
-            loadedController = true;
-        }
-
-    }
-
-    public enum ProductState
-    {
-        Unknown = -1,
-        WebRemote = 1,
-        FoundLocal,
-        WillPerformUpgrade,
-        CurrentlyInstalled,
-        DownloadStarted,
-        DownloadMirrorSelection,
-        DownloadInProgress,
-        DownloadSuccess,
-        DownloadError,
-        InstallStarted,
-        InstallInProgress,
-        InstallSuccess,
-        InstallError,
-        RemoveStarted,
-        RemoveInProgress,
-        RemoveSuccess,
-        RemoveFailed,
-        UpdateStarted,
-        UpdateInProgress,
-        UpdateSuccess,
-        UpdateFailed
-    }
-
+                File.Delete(p.FullPath);
+                File.Move(p.TempFileName, p.FullPath);
+                CurrentState = ProductState.DownloadSuccess;
+            }
+            catch
+            {
+                // Log the failed file operation.
+                CurrentState = ProductState.Unknown;
+            }
+        }
+        else
+        {
+            if (e.Cancelled)
+                CurrentState = ProductState.DownloadCancelled;
+            else
+                CurrentState = ProductState.DownloadError;
+            Package p = GetPackage();
+
+            if (File.Exists(p.TempFileName))
+            {
+                File.Delete(p.TempFileName);
+            }
+        }
+
+        if (DownloadProductCompleted != null)
+        {
+            DownloadProductCompleted(this, e);
+        }
+
+        return;
+    }
+
+    private void DownloadProductProgress(object sender, DownloadProgressChangedEventArgs e)
+    {
+        if (CurrentState == ProductState.DownloadStarted)
+            CurrentState = ProductState.DownloadInProgress;
+
+        if (CurrentState == ProductState.DownloadInProgress)
+        {
+            if (DownloadProductProgressChanged != null)
+            {
+                DownloadProductProgressChanged(this, e);
+            }
+        }
+
+        return;
+    }
+
+    private void DownloadProduct()
+    {
+      Package p = GetPackage();
+
+      if (p == null)
+      {
+        Logger.LogError("Attempt to download product with no package defined.");
+        return;
+      }
+      if (MirrorList.Count == 0)
+      {
+        Logger.LogError(String.Format("No mirrors to dowload from for {0}", p.FileName));
+        CurrentState = ProductState.DownloadNoMirror;
+        if (DownloadProductCompleted != null)
+        {
+          Exception e = new Exception("No mirror available to download this product.");
+          AsyncCompletedEventArgs args = new AsyncCompletedEventArgs(e, true, null);
+          DownloadProductCompleted(this, args);
+        }
+        return;
+      }
+
+      foreach (string mirror in MirrorList)
+      {
+        Uri mirrorUri = new Uri(mirror);
+        try
+        {
+          Logger.LogInformation(String.Format("Attempting to download {0} from {1}.", p.FileName, mirror));
+          wc.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadProductComplete);
+          wc.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownloadProductProgress);
+          wc.DownloadFileAsync(mirrorUri, p.TempFileName, mirror);
+          break;
+        }
+        catch (Exception e)
+        {
+          CurrentState = ProductState.DownloadMirrorSelection;
+          Logger.LogError(e.Message);
+        }
+      }
+
+      if (CurrentState != ProductState.DownloadStarted)
+        CurrentState = ProductState.DownloadError;
+    }
+
+    public void Download()
+    {
+        Package p = GetPackage();
+
+        if (p != null)
+        {
+            if (p.FoundLocal)
+            {
+                Logger.LogInformation(String.Format("Product {0} found local.", Name));
+                CurrentState = ProductState.FoundLocal;
+            }
+            else
+            {
+                CurrentState = ProductState.DownloadStarted;
+                DownloadMirrorList(p.FileName);
+            }
+        }
+    }
+
+    /// <summary>
+    /// Stops the current download or installation process if there is one running.
+    /// </summary>
+    public void CancelActions()
+    {
+      switch (CurrentState)
+      {
+        case ProductState.DownloadStarted:
+        case ProductState.DownloadInProgress:
+          wc.CancelAsync();
+          break;
+
+        case ProductState.InstallStarted:
+        case ProductState.InstallInProgress:
+          if (bgw.WorkerSupportsCancellation)
+            bgw.CancelAsync();
+          break;;
+      }
+    }
+
+    // Configuration Controller.
+    private void GetController()
+    {
+        if (loadedController) return;
+        controller = PluginManager.GetController(Name);
+        if (controller != null)
+            controller.Owner = this;
+        loadedController = true;
+    }
+
+  }
+
+  public enum ProductState
+  {
+    Unknown = -1,
+    WebRemote = 1,
+    FoundLocal,
+    WillPerformUpgrade,
+    CurrentlyInstalled,
+    DownloadStarted,
+    DownloadMirrorSelection,
+    DownloadInProgress,
+    DownloadSuccess,
+    DownloadError,
+    DownloadCancelled,
+    DownloadNoMirror,
+    InstallStarted,
+    InstallInProgress,
+    InstallSuccess,
+    InstallError,
+    RemoveStarted,
+    RemoveInProgress,
+    RemoveSuccess,
+    RemoveFailed,
+    UpdateStarted,
+    UpdateInProgress,
+    UpdateSuccess,
+    UpdateFailed
+  }
 }

=== modified file 'WexInstaller.Core/ProductManager.cs'
--- a/WexInstaller.Core/ProductManager.cs	2011-02-25 15:04:43 +0000
+++ b/WexInstaller.Core/ProductManager.cs	2011-03-02 15:58:16 +0000
@@ -147,49 +147,29 @@
             return args[1];   // this should be the default catalog name
         }
 
-        /// <summary>
-        /// Attempts to load a product manifest file. Strategy is:
-        /// - Look for a development version of the file if we running under a debugger. If it exists
-        ///   use it as temporary manifest.
-        /// - See if there is a temporary manifest (either from the first step or a regular one). If one
-        ///   exists and it is younger than the regular manifest then make this the regular one.
-        /// - If a regular manifest exists load it.
-        /// </summary>
         private static void LoadManifestWithCheckForTemp()
         {
-            // Use development version of the manifest if we are in debug mode.
-            ProductManifest tempManifest;
-            string tempSourcePath;
+          // first we need to see if there is any temp manifest file to check
+          ProductManifest tempManifest = ObjectifyManifest(InstallerConfiguration.TempProductsManifest);
+          ProductManifest currentManifest = ObjectifyManifest(InstallerConfiguration.ProductsManifest);
 
-#if DEBUG
-            tempManifest = ObjectifyManifest(InstallerConfiguration.DevManifest);
-            tempSourcePath = InstallerConfiguration.DevManifest;
-#endif
-            // First we need to see if there is any temp manifest file to check.
-            if (tempManifest == null)
+          if (tempManifest == null)
+            manifest = currentManifest;
+          else
+          {
+            if (currentManifest == null || tempManifest.Version > currentManifest.Version)
             {
-                tempManifest = ObjectifyManifest(InstallerConfiguration.TempProductsManifest);
-                tempSourcePath = InstallerConfiguration.TempProductsManifest;
+              manifest = tempManifest;
+              currentManifest = null;
+              File.Copy(InstallerConfiguration.TempProductsManifest, InstallerConfiguration.ProductsManifest, true);
             }
-            ProductManifest currentManifest = ObjectifyManifest(InstallerConfiguration.ProductsManifest);
-
-            if (tempManifest == null)
-                manifest = currentManifest;
             else
             {
-                if (currentManifest == null || tempManifest.Version > currentManifest.Version)
-                {
-                    manifest = tempManifest;
-                    currentManifest = null;
-                    File.Copy(tempSourcePath, InstallerConfiguration.ProductsManifest, true);
-                }
-                else
-                {
-                    manifest = currentManifest;
-                    tempManifest = null;
-                }
-                File.Delete(InstallerConfiguration.TempProductsManifest);
+              manifest = currentManifest;
+              tempManifest = null;
             }
+            File.Delete(InstallerConfiguration.TempProductsManifest);
+          }
         }
 
         public static event DownloadManifestProgressHandler DownloadManifestProgressChanged;

=== modified file 'WexInstaller/Controls/InstallWizardControl.cs'
--- a/WexInstaller/Controls/InstallWizardControl.cs	2011-02-09 17:35:42 +0000
+++ b/WexInstaller/Controls/InstallWizardControl.cs	2011-03-02 15:58:16 +0000
@@ -38,6 +38,9 @@
             InstallerPanel.StatusChanged += statusChanged;
         }
 
+        // True if we are done here.
+        public bool WorkDone { get; set; }
+
         private void AdjustTabs()
         {
             // By default, don't display apply updates.
@@ -183,11 +186,21 @@
             this.BringToFront();
         }
 
+        /// <summary>
+        /// Returns true if the current install step will be stopped and we can shut down the
+        /// entire application.
+        /// </summary>
+        public bool CancelActions()
+        {
+            return CurrentPanel.Cancel();
+        }
+
         void InstallerPanel_StatusChanged(object sender, EventArgs e)
         {
             InstallerPanel panel = sender as InstallerPanel;
             Back.Enabled = panel.BackOk();
             Next.Enabled = panel.NextOk();
+            Cancel.Enabled = panel.CancelOk();
         }
 
         private void UpdateWizardPage(int newPage)
@@ -195,10 +208,9 @@
             int currentIndex = wizardPages.SelectedIndex;
             currentIndex += newPage;
 
-            if (currentIndex >= wizardPages.TabCount)
-            {
-                Application.Exit();
-            }
+            WorkDone = currentIndex >= wizardPages.TabCount;
+            if (WorkDone)
+                (ParentForm as MainForm).Close();
             else if (currentIndex < 0)
             {
                 (this.ParentForm as MainForm).GoBack();
@@ -207,7 +219,7 @@
             {
                 string tabName = wizardPages.TabPages[currentIndex].Text;
                 installWizardSideBarControl1.SelectTab(tabName);
-                Refresh();
+                Update();
                 wizardPages.SelectedIndex = Math.Max(currentIndex, 0);
             }
         }
@@ -241,12 +253,8 @@
 
         private void Cancel_Click(object sender, EventArgs e)
         {
-            if (CurrentPanel.Cancel())
-            {
-                DialogResult result = MessageBox.Show("Are you sure you wish to cancel?", "Confirm Cancelation", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation);
-                if (result == DialogResult.Yes)
-                    Application.Exit();
-            }
+            // This call will ask the user if he agrees to close the installer.
+            (ParentForm as MainForm).Close();
         }
 
         private void Help_Click(object sender, EventArgs e)
@@ -261,7 +269,6 @@
         private void wizardPages_SelectedIndexChanged(object sender, EventArgs e)
         {
             InstallerPanel panel = wizardPages.SelectedTab.Controls[0] as InstallerPanel;
-            panel.Refresh();
             panel.Activate();
         }
     }

=== modified file 'WexInstaller/Controls/ListViewWex.cs'
--- a/WexInstaller/Controls/ListViewWex.cs	2011-02-09 17:35:42 +0000
+++ b/WexInstaller/Controls/ListViewWex.cs	2011-03-02 15:58:16 +0000
@@ -7,177 +7,177 @@
 
 namespace WexInstaller.Core
 {
-    class ListViewWex : ListView
-    {
-        private int progressbarrow = 0;
-        private int progressbarcol = 0;
-        private ImageList dummyList;
-
-        public ListViewWex()
-        {
-            OwnerDraw = true;
-            DoubleBuffered = true;
-
-            // we do this so our rows have the right height.  
-            // we really should be querying the size of the product images instead
-            // of using 32 but this will do for now.
-            dummyList = new ImageList();
-            dummyList.ImageSize = new Size(24, 24);
-            SmallImageList = dummyList;
-            GhostProductImages = true;
-        }
-
-        public bool GhostProductImages { get; set; }
-
-        private void PositionProgressBar()
-        {
-            Rectangle rowRect = this.GetItemRect(ProgressBarRow, ItemBoundsPortion.Entire);
-
-            int progressBarColLeft = 0;
-            int progressBarColWidth = 0;
-            foreach (ColumnHeader col in this.Columns)
-            {
-                if (col.DisplayIndex == ProgressBarCol)
-                {
-                    progressBarColWidth = col.Width;
-                    break;
-                }
-                progressBarColLeft += col.Width;
-            }
-
-            ProgressBar.Height = 17;
-            ProgressBar.Width = progressBarColWidth - 4;
-            ProgressBar.Location = new Point(rowRect.Left + progressBarColLeft + 2,
-                                             rowRect.Top + (rowRect.Height - ProgressBar.Height)/2);
-
-            return;
-        }
-
-        protected override void WndProc(ref Message m)
-        {
-            switch (m.Msg)
-            {
-                case 0x000F:  // WM_PAINT
-                    if (View != View.Details)
-                        break;
-
-                    if (ProgressBar != null)
-                    {
-                        // If the control has a progress bar defined, update it's location
-                        PositionProgressBar();
-                        ProgressBar.Visible = (ProgressBar.Top < this.Font.Height) ? false : ProgressBar.Enabled;
-                        ProgressBar.Refresh();
-                    }
-
-                    break;
-            }
-            base.WndProc(ref m);
-        }
-
-        public int ProgressBarRow
-        {
-            get
-            {
-                return progressbarrow;
-            }
-            set
-            {
-                progressbarrow = (value < this.Items.Count) ? value : this.Items.Count;
-                if (progressbarrow < 0)
-                    progressbarrow = 0;
-            }
-        }
-        public int ProgressBarCol
-        {
-            get
-            {
-                return progressbarcol;
-            }
-            set
-            {
-                progressbarcol = (value < this.Columns.Count) ? value : this.Columns.Count;
-                if (progressbarcol < 0)
-                    progressbarcol = 0;
-            }
-        }
-        public ProgressBar ProgressBar { get; set; }
-
-        public void ShowProgressBar(bool visible)
-        {
-            if (ProgressBar == null)
-            {
-                ProgressBar = new ProgressBar();
-                ProgressBar.BackColor = this.BackColor;
-                ProgressBar.Name = "ListViewProgressBar";
-                ProgressBar.Style = System.Windows.Forms.ProgressBarStyle.Continuous;
-                Controls.Add(this.ProgressBar);
-            }
-
-            ProgressBar.Visible = visible;
-            ProgressBar.Enabled = visible;
+  class ListViewWex : ListView
+  {
+    private int progressbarrow = 0;
+    private int progressbarcol = 0;
+    private ImageList dummyList;
+
+    public ListViewWex()
+    {
+        OwnerDraw = true;
+        DoubleBuffered = true;
+
+        // we do this so our rows have the right height.  
+        // we really should be querying the size of the product images instead
+        // of using 32 but this will do for now.
+        dummyList = new ImageList();
+        dummyList.ImageSize = new Size(24, 24);
+        SmallImageList = dummyList;
+        GhostProductImages = true;
+    }
+
+    public bool GhostProductImages { get; set; }
+
+    private void PositionProgressBar()
+    {
+        Rectangle rowRect = this.GetItemRect(ProgressBarRow, ItemBoundsPortion.Entire);
+
+        int progressBarColLeft = 0;
+        int progressBarColWidth = 0;
+        foreach (ColumnHeader col in this.Columns)
+        {
+            if (col.DisplayIndex == ProgressBarCol)
+            {
+                progressBarColWidth = col.Width;
+                break;
+            }
+            progressBarColLeft += col.Width;
+        }
+
+        ProgressBar.Height = 17;
+        ProgressBar.Width = progressBarColWidth - 4;
+        ProgressBar.Location = new Point(rowRect.Left + progressBarColLeft + 2,
+                                          rowRect.Top + (rowRect.Height - ProgressBar.Height)/2);
+
+        return;
+    }
+
+    protected override void WndProc(ref Message m)
+    {
+      switch (m.Msg)
+      {
+        case 0x000F:  // WM_PAINT
+          // XXX: use Win32.cs from Workbench for *many* of these constants.
+          if (View != View.Details)
+            break;
+
+          // XXX: Postion the progress bar on *each* paint cycle? There are better options.
+          if (ProgressBar != null)
+          {
+            // If the control has a progress bar defined, update it's location
             PositionProgressBar();
+            ProgressBar.Visible = (ProgressBar.Top < this.Font.Height) ? false : ProgressBar.Enabled;
             ProgressBar.Refresh();
-        }
-
-        protected override void OnDrawColumnHeader(DrawListViewColumnHeaderEventArgs e)
-        {
-            e.DrawDefault = true;
-            base.OnDrawColumnHeader(e);
-        }
-
-        protected override void OnDrawItem(DrawListViewItemEventArgs e)
-        {
-
-            e.DrawDefault = false;
-            e.DrawBackground();
-
-            base.OnDrawItem(e);
-        }
-
-        protected override void OnDrawSubItem(DrawListViewSubItemEventArgs e)
-        {
-            if (e.SubItem == e.Item.SubItems[1])
-            {
-                e.DrawDefault = false;
-                Product product = e.Item.Tag as Product;
-
-                Image icon = product.Icon;
-                if (GhostProductImages && e.Item.StateImageIndex != 0)
-                    icon = GhostIcon(product.Icon);
-                Point pt = e.Bounds.Location;
-                pt.Y += (e.Bounds.Height - SmallImageList.ImageSize.Height) / 2;
-                e.Graphics.DrawImage(icon, pt.X+4, pt.Y, 
-                    SmallImageList.ImageSize.Width, 
-                    SmallImageList.ImageSize.Height);
-
-                pt = e.Bounds.Location;
-                Size stringSize = e.Graphics.MeasureString(e.SubItem.Text, e.SubItem.Font).ToSize();
-                pt.Y += (e.Bounds.Height - stringSize.Height) / 2;
-
-                e.Graphics.DrawString(e.SubItem.Text, e.SubItem.Font,
-                    new SolidBrush(e.SubItem.ForeColor),
-                    pt.X + SmallImageList.ImageSize.Width + 9, pt.Y);
-            }
-            else
-            {
-                e.DrawDefault = true;
-            }
-            base.OnDrawSubItem(e);
-        }
-
-        private Image GhostIcon(Image image)
-        {
-            Bitmap b = new Bitmap(image);
-            for (int y = 0; y < b.Height; y++)
-                for (int x = 0; x < b.Width; x++)
-                {
-                    Color c = b.GetPixel(x, y);
-                    int red = c.R + (255 - c.R)*3/4;
-                    int green = c.G + (255 - c.G)*3/4;
-                    int blue = c.B + (255 - c.B)*3/4;
-                    b.SetPixel(x, y, Color.FromArgb(red, green, blue));
-                }
-            return b;
-        }
-    }
+          }
+
+          break;
+      }
+      base.WndProc(ref m);
+    }
+
+    public int ProgressBarRow
+    {
+        get
+        {
+            return progressbarrow;
+        }
+        set
+        {
+            progressbarrow = (value < this.Items.Count) ? value : this.Items.Count;
+            if (progressbarrow < 0)
+                progressbarrow = 0;
+        }
+    }
+    public int ProgressBarCol
+    {
+        get
+        {
+            return progressbarcol;
+        }
+        set
+        {
+            progressbarcol = (value < this.Columns.Count) ? value : this.Columns.Count;
+            if (progressbarcol < 0)
+                progressbarcol = 0;
+        }
+    }
+    public ProgressBar ProgressBar { get; set; }
+
+    public void ShowProgressBar(bool visible)
+    {
+        if (ProgressBar == null)
+        {
+            ProgressBar = new ProgressBar();
+            ProgressBar.BackColor = this.BackColor;
+            ProgressBar.Name = "ListViewProgressBar";
+            ProgressBar.Style = System.Windows.Forms.ProgressBarStyle.Continuous;
+            Controls.Add(this.ProgressBar);
+        }
+
+        ProgressBar.Visible = visible;
+        ProgressBar.Enabled = visible;
+        PositionProgressBar();
+        ProgressBar.Refresh();
+    }
+
+    protected override void OnDrawColumnHeader(DrawListViewColumnHeaderEventArgs e)
+    {
+        e.DrawDefault = true;
+        base.OnDrawColumnHeader(e);
+    }
+
+    protected override void OnDrawItem(DrawListViewItemEventArgs e)
+    {
+      e.DrawDefault = false;
+      base.OnDrawItem(e);
+    }
+
+    protected override void OnDrawSubItem(DrawListViewSubItemEventArgs e)
+    {
+      if (e.SubItem == e.Item.SubItems[1])
+      {
+        // Main product icon and text.
+        e.DrawDefault = false;
+        Product product = e.Item.Tag as Product;
+
+        Image icon = product.Icon;
+        if (GhostProductImages && e.Item.StateImageIndex != 0)
+            icon = GhostIcon(product.Icon);
+        Point pt = e.Bounds.Location;
+        pt.Y += (e.Bounds.Height - SmallImageList.ImageSize.Height) / 2;
+        e.Graphics.DrawImage(icon, pt.X+4, pt.Y, 
+            SmallImageList.ImageSize.Width, 
+            SmallImageList.ImageSize.Height);
+
+        pt = e.Bounds.Location;
+        Size stringSize = e.Graphics.MeasureString(e.SubItem.Text, e.SubItem.Font).ToSize();
+        pt.Y += (e.Bounds.Height - stringSize.Height) / 2;
+
+        e.Graphics.DrawString(e.SubItem.Text, e.SubItem.Font,
+            new SolidBrush(e.SubItem.ForeColor),
+            pt.X + SmallImageList.ImageSize.Width + 9, pt.Y);
+      }
+      else
+      {
+        e.DrawDefault = true;
+      }
+      base.OnDrawSubItem(e);
+    }
+
+    private Image GhostIcon(Image image)
+    {
+        Bitmap b = new Bitmap(image);
+        for (int y = 0; y < b.Height; y++)
+            for (int x = 0; x < b.Width; x++)
+            {
+                Color c = b.GetPixel(x, y);
+                int red = c.R + (255 - c.R)*3/4;
+                int green = c.G + (255 - c.G)*3/4;
+                int blue = c.B + (255 - c.B)*3/4;
+                b.SetPixel(x, y, Color.FromArgb(red, green, blue));
+            }
+        return b;
+    }
+  }
 }

=== modified file 'WexInstaller/Controls/RemoveControl.cs'
--- a/WexInstaller/Controls/RemoveControl.cs	2011-02-07 21:39:59 +0000
+++ b/WexInstaller/Controls/RemoveControl.cs	2011-03-02 15:58:16 +0000
@@ -1,108 +1,92 @@
 using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Drawing;
-using System.Linq;
-using System.Text;
 using System.Windows.Forms;
-using WexInstaller.Properties;
-using WexInstaller.Core;
 
 namespace WexInstaller
 {
-    public partial class RemoveControl : UserControl
-    {
-        private EventHandler statusChanged;
-
-        public RemoveControl()
-        {
-            InitializeComponent();
-            statusChanged = new EventHandler(InstallerPanel_StatusChanged);
-            InstallerPanel.StatusChanged += statusChanged;
-            wizardPages.SelectedIndex = 0;
-        }
-
-        void InstallerPanel_StatusChanged(object sender, EventArgs e)
-        {
-            InstallerPanel panel = sender as InstallerPanel;
-            Back.Enabled = panel.BackOk();
-            Next.Enabled = panel.NextOk();
-        }
-
-        private void UpdateWizardPage(int newPage)
-        {
-            int currentIndex = wizardPages.SelectedIndex;
-            currentIndex += newPage;
-
-            if (currentIndex >= wizardPages.TabCount)
-            {
-                Application.Exit();
-            }
-            else if (currentIndex < 0)
-            {
-                (this.ParentForm as MainForm).GoBack();
-            }
-            else
-            {
-                Refresh();
-                wizardPages.SelectedIndex = Math.Max(currentIndex, 0);
-            }
-        }
-
-        private InstallerPanel CurrentPanel
-        {
-            get
-            {
-                return wizardPages.SelectedTab.Controls[0] as InstallerPanel;
-            }
-        }
-
-        public void ShowWizard()
-        {
-            wizardPages_SelectedIndexChanged(this, null);
-            this.BringToFront();
-        }
-
-        private void Next_Click(object sender, EventArgs e)
-        {
-            if (CurrentPanel.Next())
-            {
-                UpdateWizardPage(1);
-            }
-        }
-
-        private void Back_Click(object sender, EventArgs e)
-        {
-            if (CurrentPanel.Back())
-            {
-                UpdateWizardPage(-1);
-            }
-        }
-
-        private void Cancel_Click(object sender, EventArgs e)
-        {
-            if (CurrentPanel.Cancel())
-            {
-                DialogResult result = MessageBox.Show("Are you sure you wish to cancel?", "Confirm Cancelation", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation);
-                if (result == DialogResult.Yes)
-                    Application.Exit();
-            }
-        }
-
-        private void Help_Click(object sender, EventArgs e)
-        {
-        }
-
-        private void wizardPages_Deselected(object sender, TabControlEventArgs e)
-        {
-            (e.TabPage.Controls[0] as InstallerPanel).Deactivate();
-        }
-
-        private void wizardPages_SelectedIndexChanged(object sender, EventArgs e)
-        {
-            InstallerPanel panel = wizardPages.SelectedTab.Controls[0] as InstallerPanel;
-            panel.Refresh();
-            panel.Activate();
-        }
-    }
+  public partial class RemoveControl : UserControl
+  {
+    private EventHandler statusChanged;
+    public bool WorkDone { get; set; }
+
+    public RemoveControl()
+    {
+      InitializeComponent();
+      statusChanged = new EventHandler(InstallerPanel_StatusChanged);
+      InstallerPanel.StatusChanged += statusChanged;
+      wizardPages.SelectedIndex = 0;
+    }
+
+    void InstallerPanel_StatusChanged(object sender, EventArgs e)
+    {
+      InstallerPanel panel = sender as InstallerPanel;
+      Back.Enabled = panel.BackOk();
+      Next.Enabled = panel.NextOk();
+    }
+
+    private void UpdateWizardPage(int newPage)
+    {
+      int currentIndex = wizardPages.SelectedIndex;
+      currentIndex += newPage;
+
+      WorkDone = currentIndex >= wizardPages.TabCount;
+      if (WorkDone)
+        (ParentForm as MainForm).Close();
+      else
+        if (currentIndex < 0)
+          (this.ParentForm as MainForm).GoBack();
+        else
+          wizardPages.SelectedIndex = Math.Max(currentIndex, 0);
+    }
+
+    private InstallerPanel CurrentPanel
+    {
+      get
+      {
+        return wizardPages.SelectedTab.Controls[0] as InstallerPanel;
+      }
+    }
+
+    public void ShowWizard()
+    {
+      wizardPages_SelectedIndexChanged(this, null);
+      this.BringToFront();
+    }
+
+    private void Next_Click(object sender, EventArgs e)
+    {
+      if (CurrentPanel.Next())
+      {
+        UpdateWizardPage(1);
+      }
+    }
+
+    private void Back_Click(object sender, EventArgs e)
+    {
+      if (CurrentPanel.Back())
+      {
+        UpdateWizardPage(-1);
+      }
+    }
+
+    private void Cancel_Click(object sender, EventArgs e)
+    {
+      // This call will ask the user if he agrees to close the installer.
+      (ParentForm as MainForm).Close();
+    }
+
+    private void Help_Click(object sender, EventArgs e)
+    {
+    }
+
+    private void wizardPages_Deselected(object sender, TabControlEventArgs e)
+    {
+      (e.TabPage.Controls[0] as InstallerPanel).Deactivate();
+    }
+
+    private void wizardPages_SelectedIndexChanged(object sender, EventArgs e)
+    {
+      InstallerPanel panel = wizardPages.SelectedTab.Controls[0] as InstallerPanel;
+      panel.Activate();
+    }
+  }
 }

=== modified file 'WexInstaller/InstallWizard/InstallProgressPanel.Designer.cs'
--- a/WexInstaller/InstallWizard/InstallProgressPanel.Designer.cs	2011-02-04 16:34:12 +0000
+++ b/WexInstaller/InstallWizard/InstallProgressPanel.Designer.cs	2011-03-02 15:58:16 +0000
@@ -29,163 +29,164 @@
         /// </summary>
         private void InitializeComponent()
         {
-            this.components = new System.ComponentModel.Container();
-            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(InstallProgress));
-            this.productList = new WexInstaller.Core.ListViewWex();
-            this.stateHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
-            this.productColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
-            this.statusColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
-            this.progressColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
-            this.notesColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
-            this.stateImages = new System.Windows.Forms.ImageList(this.components);
-            this.actionLabel = new System.Windows.Forms.Label();
-            this.subTitleLabel = new System.Windows.Forms.Label();
-            this.enableDetails = new System.Windows.Forms.Button();
-            this.detailsText = new System.Windows.Forms.TextBox();
-            this.detailsPanel = new System.Windows.Forms.Panel();
-            this.detailsPanel.SuspendLayout();
-            this.SuspendLayout();
-            // 
-            // productList
-            // 
-            this.productList.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
-                        | System.Windows.Forms.AnchorStyles.Left)
-                        | System.Windows.Forms.AnchorStyles.Right)));
-            this.productList.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
+          this.components = new System.ComponentModel.Container();
+          System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(InstallProgress));
+          this.productList = new WexInstaller.Core.ListViewWex();
+          this.stateHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
+          this.productColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
+          this.statusColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
+          this.progressColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
+          this.notesColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
+          this.stateImages = new System.Windows.Forms.ImageList(this.components);
+          this.actionLabel = new System.Windows.Forms.Label();
+          this.subTitleLabel = new System.Windows.Forms.Label();
+          this.enableDetails = new System.Windows.Forms.Button();
+          this.detailsText = new System.Windows.Forms.TextBox();
+          this.detailsPanel = new System.Windows.Forms.Panel();
+          this.detailsPanel.SuspendLayout();
+          this.SuspendLayout();
+          // 
+          // productList
+          // 
+          this.productList.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+                      | System.Windows.Forms.AnchorStyles.Left)
+                      | System.Windows.Forms.AnchorStyles.Right)));
+          this.productList.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
             this.stateHeader,
             this.productColumn,
             this.statusColumn,
             this.progressColumn,
             this.notesColumn});
-            this.productList.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
-            this.productList.GhostProductImages = false;
-            this.productList.Location = new System.Drawing.Point(3, 3);
-            this.productList.MultiSelect = false;
-            this.productList.Name = "productList";
-            this.productList.OwnerDraw = true;
-            this.productList.ProgressBar = null;
-            this.productList.ProgressBarCol = 0;
-            this.productList.ProgressBarRow = 0;
-            this.productList.ShowGroups = false;
-            this.productList.Size = new System.Drawing.Size(507, 351);
-            this.productList.StateImageList = this.stateImages;
-            this.productList.TabIndex = 2;
-            this.productList.UseCompatibleStateImageBehavior = false;
-            this.productList.View = System.Windows.Forms.View.Details;
-            // 
-            // stateHeader
-            // 
-            this.stateHeader.Text = "";
-            this.stateHeader.Width = 20;
-            // 
-            // productColumn
-            // 
-            this.productColumn.Text = "Product";
-            this.productColumn.Width = 224;
-            // 
-            // statusColumn
-            // 
-            this.statusColumn.Text = "Status";
-            this.statusColumn.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
-            this.statusColumn.Width = 117;
-            // 
-            // progressColumn
-            // 
-            this.progressColumn.Text = "Progress";
-            this.progressColumn.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
-            this.progressColumn.Width = 70;
-            // 
-            // notesColumn
-            // 
-            this.notesColumn.Text = "Notes";
-            this.notesColumn.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
-            this.notesColumn.Width = 65;
-            // 
-            // stateImages
-            // 
-            this.stateImages.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("stateImages.ImageStream")));
-            this.stateImages.TransparentColor = System.Drawing.Color.Transparent;
-            this.stateImages.Images.SetKeyName(0, "check_mark.png");
-            this.stateImages.Images.SetKeyName(1, "Download.png");
-            this.stateImages.Images.SetKeyName(2, "Modify.png");
-            this.stateImages.Images.SetKeyName(3, "Problem.png");
-            this.stateImages.Images.SetKeyName(4, "warning_sign.png");
-            // 
-            // actionLabel
-            // 
-            this.actionLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
-            this.actionLabel.AutoSize = true;
-            this.actionLabel.Font = new System.Drawing.Font("Tahoma", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
-            this.actionLabel.Location = new System.Drawing.Point(3, 363);
-            this.actionLabel.Name = "actionLabel";
-            this.actionLabel.Size = new System.Drawing.Size(337, 16);
-            this.actionLabel.TabIndex = 5;
-            this.actionLabel.Text = "Click [Execute] to install or update the following packages";
-            // 
-            // subTitleLabel
-            // 
-            this.subTitleLabel.AutoSize = true;
-            this.subTitleLabel.Font = new System.Drawing.Font("Tahoma", 9.75F);
-            this.subTitleLabel.Location = new System.Drawing.Point(17, 75);
-            this.subTitleLabel.Name = "subTitleLabel";
-            this.subTitleLabel.Size = new System.Drawing.Size(234, 16);
-            this.subTitleLabel.TabIndex = 7;
-            this.subTitleLabel.Text = "The following products will be installed.";
-            // 
-            // enableDetails
-            // 
-            this.enableDetails.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
-            this.enableDetails.AutoSize = true;
-            this.enableDetails.BackColor = System.Drawing.SystemColors.Control;
-            this.enableDetails.Location = new System.Drawing.Point(3, 360);
-            this.enableDetails.Name = "enableDetails";
-            this.enableDetails.Size = new System.Drawing.Size(89, 23);
-            this.enableDetails.TabIndex = 1;
-            this.enableDetails.Text = "&Show Details >";
-            this.enableDetails.UseVisualStyleBackColor = false;
-            this.enableDetails.Visible = false;
-            this.enableDetails.Click += new System.EventHandler(this.enableDetails_Click);
-            // 
-            // detailsText
-            // 
-            this.detailsText.BackColor = System.Drawing.SystemColors.ControlLightLight;
-            this.detailsText.Location = new System.Drawing.Point(17, 298);
-            this.detailsText.Multiline = true;
-            this.detailsText.Name = "detailsText";
-            this.detailsText.ReadOnly = true;
-            this.detailsText.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
-            this.detailsText.Size = new System.Drawing.Size(513, 192);
-            this.detailsText.TabIndex = 9;
-            // 
-            // detailsPanel
-            // 
-            this.detailsPanel.Controls.Add(this.productList);
-            this.detailsPanel.Controls.Add(this.enableDetails);
-            this.detailsPanel.Controls.Add(this.actionLabel);
-            this.detailsPanel.Location = new System.Drawing.Point(17, 104);
-            this.detailsPanel.Name = "detailsPanel";
-            this.detailsPanel.Size = new System.Drawing.Size(513, 386);
-            this.detailsPanel.TabIndex = 10;
-            // 
-            // InstallProgress
-            // 
-            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
-            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
-            this.Controls.Add(this.detailsPanel);
-            this.Controls.Add(this.subTitleLabel);
-            this.Controls.Add(this.detailsText);
-            this.DoubleBuffered = true;
-            this.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
-            this.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
-            this.Name = "InstallProgress";
-            this.Size = new System.Drawing.Size(560, 499);
-            this.Controls.SetChildIndex(this.detailsText, 0);
-            this.Controls.SetChildIndex(this.subTitleLabel, 0);
-            this.Controls.SetChildIndex(this.detailsPanel, 0);
-            this.detailsPanel.ResumeLayout(false);
-            this.detailsPanel.PerformLayout();
-            this.ResumeLayout(false);
-            this.PerformLayout();
+          this.productList.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+          this.productList.GhostProductImages = false;
+          this.productList.Location = new System.Drawing.Point(3, 3);
+          this.productList.MultiSelect = false;
+          this.productList.Name = "productList";
+          this.productList.OwnerDraw = true;
+          this.productList.ProgressBar = null;
+          this.productList.ProgressBarCol = 0;
+          this.productList.ProgressBarRow = 0;
+          this.productList.ShowGroups = false;
+          this.productList.ShowItemToolTips = true;
+          this.productList.Size = new System.Drawing.Size(507, 351);
+          this.productList.StateImageList = this.stateImages;
+          this.productList.TabIndex = 2;
+          this.productList.UseCompatibleStateImageBehavior = false;
+          this.productList.View = System.Windows.Forms.View.Details;
+          // 
+          // stateHeader
+          // 
+          this.stateHeader.Text = "";
+          this.stateHeader.Width = 20;
+          // 
+          // productColumn
+          // 
+          this.productColumn.Text = "Product";
+          this.productColumn.Width = 224;
+          // 
+          // statusColumn
+          // 
+          this.statusColumn.Text = "Status";
+          this.statusColumn.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
+          this.statusColumn.Width = 117;
+          // 
+          // progressColumn
+          // 
+          this.progressColumn.Text = "Progress";
+          this.progressColumn.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
+          this.progressColumn.Width = 70;
+          // 
+          // notesColumn
+          // 
+          this.notesColumn.Text = "Notes";
+          this.notesColumn.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
+          this.notesColumn.Width = 65;
+          // 
+          // stateImages
+          // 
+          this.stateImages.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("stateImages.ImageStream")));
+          this.stateImages.TransparentColor = System.Drawing.Color.Transparent;
+          this.stateImages.Images.SetKeyName(0, "check_mark.png");
+          this.stateImages.Images.SetKeyName(1, "Download.png");
+          this.stateImages.Images.SetKeyName(2, "Modify.png");
+          this.stateImages.Images.SetKeyName(3, "Problem.png");
+          this.stateImages.Images.SetKeyName(4, "warning_sign.png");
+          // 
+          // actionLabel
+          // 
+          this.actionLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+          this.actionLabel.AutoSize = true;
+          this.actionLabel.Font = new System.Drawing.Font("Tahoma", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+          this.actionLabel.Location = new System.Drawing.Point(3, 363);
+          this.actionLabel.Name = "actionLabel";
+          this.actionLabel.Size = new System.Drawing.Size(337, 16);
+          this.actionLabel.TabIndex = 5;
+          this.actionLabel.Text = "Click [Execute] to install or update the following packages";
+          // 
+          // subTitleLabel
+          // 
+          this.subTitleLabel.AutoSize = true;
+          this.subTitleLabel.Font = new System.Drawing.Font("Tahoma", 9.75F);
+          this.subTitleLabel.Location = new System.Drawing.Point(17, 75);
+          this.subTitleLabel.Name = "subTitleLabel";
+          this.subTitleLabel.Size = new System.Drawing.Size(234, 16);
+          this.subTitleLabel.TabIndex = 7;
+          this.subTitleLabel.Text = "The following products will be installed.";
+          // 
+          // enableDetails
+          // 
+          this.enableDetails.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+          this.enableDetails.AutoSize = true;
+          this.enableDetails.BackColor = System.Drawing.SystemColors.Control;
+          this.enableDetails.Location = new System.Drawing.Point(3, 360);
+          this.enableDetails.Name = "enableDetails";
+          this.enableDetails.Size = new System.Drawing.Size(89, 23);
+          this.enableDetails.TabIndex = 1;
+          this.enableDetails.Text = "&Show Details >";
+          this.enableDetails.UseVisualStyleBackColor = false;
+          this.enableDetails.Visible = false;
+          this.enableDetails.Click += new System.EventHandler(this.enableDetails_Click);
+          // 
+          // detailsText
+          // 
+          this.detailsText.BackColor = System.Drawing.SystemColors.ControlLightLight;
+          this.detailsText.Location = new System.Drawing.Point(17, 298);
+          this.detailsText.Multiline = true;
+          this.detailsText.Name = "detailsText";
+          this.detailsText.ReadOnly = true;
+          this.detailsText.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
+          this.detailsText.Size = new System.Drawing.Size(513, 192);
+          this.detailsText.TabIndex = 9;
+          // 
+          // detailsPanel
+          // 
+          this.detailsPanel.Controls.Add(this.productList);
+          this.detailsPanel.Controls.Add(this.enableDetails);
+          this.detailsPanel.Controls.Add(this.actionLabel);
+          this.detailsPanel.Location = new System.Drawing.Point(17, 104);
+          this.detailsPanel.Name = "detailsPanel";
+          this.detailsPanel.Size = new System.Drawing.Size(513, 386);
+          this.detailsPanel.TabIndex = 10;
+          // 
+          // InstallProgress
+          // 
+          this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+          this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+          this.Controls.Add(this.detailsPanel);
+          this.Controls.Add(this.subTitleLabel);
+          this.Controls.Add(this.detailsText);
+          this.DoubleBuffered = true;
+          this.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+          this.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
+          this.Name = "InstallProgress";
+          this.Size = new System.Drawing.Size(560, 499);
+          this.Controls.SetChildIndex(this.detailsText, 0);
+          this.Controls.SetChildIndex(this.subTitleLabel, 0);
+          this.Controls.SetChildIndex(this.detailsPanel, 0);
+          this.detailsPanel.ResumeLayout(false);
+          this.detailsPanel.PerformLayout();
+          this.ResumeLayout(false);
+          this.PerformLayout();
 
         }
 

=== modified file 'WexInstaller/InstallWizard/InstallProgressPanel.cs'
--- a/WexInstaller/InstallWizard/InstallProgressPanel.cs	2011-03-02 14:57:43 +0000
+++ b/WexInstaller/InstallWizard/InstallProgressPanel.cs	2011-03-02 15:58:16 +0000
@@ -8,395 +8,28 @@
 using System.Net;
 using System.Diagnostics;
 using WexInstaller.Properties;
-using System.Threading;
 using WexInstaller.Controls;
 
 namespace WexInstaller
 {
-    public partial class InstallProgress : InstallerPanel
-    {
-        private Dictionary<string, int> progressLevels;
-        private bool nextOk;
-        private bool backOk;
-        private bool executed;
-        private bool installedProductsNeedConfig;
-        private int leftToInstall;
-
-        ListViewItem installingItem;
-        int start, stop, installStep, installPos;
-        int overall_progress;
-        private bool showingDetails;
-
-        public InstallProgress()
-        {
-            InitializeComponent();
-            backOk = true;
-            nextOk = true;
-            executed = false;
-            installedProductsNeedConfig = false;
-            progressLevels = new Dictionary<string, int>();
-            ListViewHelper.EnableDoubleBuffer(productList);
-        }
-
-        public override bool NextOk()
-        {
-            return nextOk;
-        }
-
-        public override bool BackOk()
-        {
-            return backOk;
-        }
-
-        public override void Activate()
-        {
-            productList.Items.Clear();
-            foreach (ProductCategory pc in ProductManager.ProductCategories)
-            {
-                foreach (Product p in pc.Products)
-                {
-                    if (p.IsUpgrade ||
-                        (p.ProposedInstalled && p.HasChanges()))
-                    {
-                        ListViewItem item = new ListViewItem(String.Empty);
-                        item.Name = p.Name;
-                        item.Tag = p;
-                        item.SubItems.Add(p.TitleWithVersion);
-                        item.SubItems.Add(GetProductStateString(p.CurrentState));
-                        item.SubItems.Add(String.Empty);
-                        item.SubItems.Add(String.Empty);
-                        productList.Items.Add(item);
-                    }
-                }
-            }
-
-            if (productList.Items.Count > 0)
-                installingItem = productList.Items[0];
-
-            if (!executed)
-            {
-                NextButton.Text = Properties.Resources.NextButtonExecuteText;
-                NextButton.Refresh();
-            }
-
-            base.Activate();
-        }
-
-        public override bool Next()
-        {
-            if (!executed)
-            {
-                nextOk = false;
-                NextButton.Text = Properties.Resources.NextButtonDefaultText;
-                NextButton.Enabled = false;
-                NextButton.Refresh();
-
-                backOk = false;
-
-                actionLabel.Visible = false;
-                enableDetails.Visible = true;
-                SignalChange();
-
-                DownloadPackages();
-                InstallNextPackage();
-
-                return false;
-            }
-            return base.Next();
-        }
-
-        public override bool Back()
-        {
-            if (!executed)
-            {
-                NextButton.Text = Properties.Resources.NextButtonDefaultText;
-                NextButton.Refresh();
-            }
-
-            return base.Back();
-        }
-
-        private string GetProductStateString(ProductState s)
-        {
-            string textStatus = null;
-
-            switch (s)
-            {
-                case ProductState.Unknown:
-                    textStatus = String.Empty;
-                    break;
-                case ProductState.WebRemote:
-                    textStatus = "To be downloaded.";
-                    break;
-                case ProductState.FoundLocal:
-                    textStatus = "To be installed.";
-                    break;
-                case ProductState.WillPerformUpgrade:
-                    textStatus = "To be upgraded.";
-                    break;
-                case ProductState.CurrentlyInstalled:
-                    textStatus = "Installed locally.";
-                    break;
-                case ProductState.DownloadStarted:
-                    textStatus = "Download started.";
-                    break;
-                case ProductState.DownloadInProgress:
-                    textStatus = "Downloading.";
-                    break;
-                case ProductState.DownloadSuccess:
-                    textStatus = "Download success.";
-                    break;
-                case ProductState.DownloadError:
-                    textStatus = "Download failed.";
-                    break;
-                case ProductState.InstallStarted:
-                    textStatus = "Install started.";
-                    break;
-                case ProductState.InstallInProgress:
-                    textStatus = "Installing.";
-                    break;
-                case ProductState.InstallSuccess:
-                    textStatus = "Install success.";
-                    break;
-                case ProductState.InstallError:
-                    textStatus = "Install error.";
-                    break;
-                case ProductState.RemoveStarted:
-                    textStatus = "Remove started.";
-                    break;
-                case ProductState.RemoveInProgress:
-                    textStatus = "Removing.";
-                    break;
-                case ProductState.RemoveSuccess:
-                    textStatus = "Successfully removed.";
-                    break;
-                case ProductState.RemoveFailed:
-                    textStatus = "Failed to remove.";
-                    break;
-                case ProductState.UpdateStarted:
-                    textStatus = "Update started.";
-                    break;
-                case ProductState.UpdateInProgress:
-                    textStatus = "Updating.";
-                    break;
-                case ProductState.UpdateSuccess:
-                    textStatus = "Update success.";
-                    break;
-                case ProductState.UpdateFailed:
-                    textStatus = "Update failed.";
-                    break;
-                default:
-                    textStatus = String.Empty;
-                    break;
-            }
-
-            return textStatus;
-        }
-
-        private void AddToDetailsText(int index, string message)
-        {
-            detailsText.AppendText(String.Format("{0}: {1}{2}", 
-                                                 (index + 1), 
-                                                 message, 
-                                                 Environment.NewLine
-                                                 )
-                                   );
-        }
-
-        private void DownloadPackages()
-        {
-            foreach (ListViewItem item in productList.Items)
-            {
-                Product p = (Product)item.Tag;
-                if (p.FoundLocal) continue;
-
-                p.DownloadProductProgressChanged += new DownloadProductProgressHandler(product_DownloadProductProgressChanged);
-                p.DownloadProductCompleted += new DownloadProductCompleteHandler(product_DownloadProductCompleted);
-                progressLevels.Add(p.Name, -1);
-                p.Download();
-                Thread.Sleep(0);
-                Application.DoEvents();
-            }
-        }
-
-        void product_DownloadProductCompleted(object sender, AsyncCompletedEventArgs e)
-        {
-            Product p = (sender as Product);
-            ListViewItem item = productList.Items.Find(p.Name, false)[0];
-            Debug.Assert(item != null);
-            item.StateImageIndex = (int)InstallProgressState.None;
-            item.SubItems[2].Text = GetProductStateString(p.CurrentState);
-            item.SubItems[3].Text = String.Empty;
-
-            AddToDetailsText(item.Index, String.Format("Download of product '{0}' {1}",
-                                                       p.Name,
-                                                       item.SubItems[2].Text));
-        }
-
-        void product_DownloadProductProgressChanged(object sender, DownloadProgressChangedEventArgs e)
-        {
-            Product p = (sender as Product);
-            ListViewItem item = productList.Items.Find(p.Name, false)[0];
-            Debug.Assert(item != null);
-
-            if (progressLevels[p.Name] == -1)
-            {
-                AddToDetailsText(item.Index, String.Format("Download of product '{0}' started from {1}",
-                                                           p.Name,
-                                                           e.UserState.ToString()));
-            }
-
-            progressLevels[p.Name] = e.ProgressPercentage;
-            int progress = 0;
-            foreach (int val in progressLevels.Values)
-                progress += val;
-
-            if (item.StateImageIndex != (int)InstallProgressState.Download && e.BytesReceived > 0)
-                item.StateImageIndex = (int)InstallProgressState.Download;
-            item.SubItems[2].Text = GetProductStateString(p.CurrentState);
-            item.SubItems[3].Text = String.Format(Resources.StatusPercentage, e.ProgressPercentage);
-        }
-
-        private void InstallNextPackage()
-        {
-            do
-            {
-                leftToInstall = productList.Items.Count;
-                foreach (ListViewItem item in productList.Items)
-                {
-                    Product p = (Product)item.Tag;
-                    if (p.CurrentState == ProductState.WebRemote ||
-                        p.CurrentState == ProductState.DownloadStarted ||
-                        p.CurrentState == ProductState.DownloadMirrorSelection ||
-                        p.CurrentState == ProductState.DownloadInProgress ||
-                        p.CurrentState == ProductState.InstallInProgress ||
-                        p.CurrentState == ProductState.UpdateInProgress ||
-                        p.CurrentState == ProductState.RemoveInProgress)
-                    {
-                        continue;
-                    }
-                    if (p.CurrentState != ProductState.DownloadSuccess &&
-                        p.CurrentState != ProductState.FoundLocal &&
-                        p.CurrentState != ProductState.WillPerformUpgrade &&
-                        p.CurrentState != ProductState.CurrentlyInstalled)
-                    {
-                        leftToInstall--;
-                        continue;
-                    }
-                    installingItem = item;
-                    p.ProductInstallationProgressChanged += new ProductInstationActionEventHandler(ProductInstallationProgressChanged);
-                    p.MakeChanges();
-                    return;
-                }
-                Application.DoEvents();
-                Thread.Sleep(0);
-            }
-            while (leftToInstall > 0);
-
-            if (installedProductsNeedConfig == false)
-                (ParentControl as InstallWizardControl).RemoveConfigPages("Configuration");
-            nextOk = true;
-            NextButton.Enabled = true;
-            NextButton.Refresh();
-
-            executed = true;
-        }
-
-        private void ProductInstallationProgressChanged(object sender, ChainedInstallerEventArgs e)
-        {
-            Product p = (sender as Product);
-            ListViewItem item = productList.Items.Find(p.Name, false)[0];
-            Debug.Assert(item != null);
-
-            switch (e.Action)
-            {
-                case ChainedInstallerAction.StartInstallation:
-                    overall_progress = 0;
-                    AddToDetailsText(item.Index, String.Format("Installation of product '{0}'start.", p.Name));
-                    AddToDetailsText(item.Index, e.Message);
-                    lock (installingItem)
-                    {
-                        installingItem.StateImageIndex = (int)InstallProgressState.Installing;
-                        installingItem.SubItems[2].Text = GetProductStateString(p.CurrentState);
-                    }
-                    break;
-                case ChainedInstallerAction.HandleActionData:
-                    AddToDetailsText(item.Index, e.Message);
-                    break;
-                case ChainedInstallerAction.HandleActionStart:
-                    AddToDetailsText(item.Index, e.Message);
-                    break;
-                case ChainedInstallerAction.ProgressSetRange:
-                    start = e.ProgressMin;
-                    stop = e.ProgressMax;
-                    installStep = 1;
-                    installPos = 0;
-                    if (overall_progress > 0)
-                        overall_progress = 50;
-                    AddToDetailsText(item.Index, String.Format("Min: {0} Max: {1}", e.ProgressMin.ToString(), e.ProgressMax.ToString()));
-                    break;
-                case ChainedInstallerAction.ProgressSetStep:
-                    installStep = e.ProgressStep;
-                    break;
-                case ChainedInstallerAction.ProgressSetPosition:
-                    if (e.ProgressPosition == stop && installPos == 0) return;
-                    SetPosition(e.ProgressPosition);
-                    break;
-                case ChainedInstallerAction.ProgressSingleStep:
-                    SetPosition(installPos + installStep);
-                    break;
-                case ChainedInstallerAction.EndInstallation:
-                    AddToDetailsText(item.Index, e.Message);
-                    break;
-                case ChainedInstallerAction.FinalAction:
-                    AddToDetailsText(item.Index, "Final actions.");
-                    lock (installingItem)
-                    {
-                        installingItem.StateImageIndex = (int)InstallProgressState.Ok;
-                        installingItem.SubItems[2].Text = GetProductStateString(p.CurrentState);
-                        installingItem.SubItems[3].Text = String.Empty;
-                    }
-                    AddToDetailsText(item.Index, installingItem.SubItems[2].Text);
-                    if (p.Controller != null)
-                    {
-                        installedProductsNeedConfig = true;
-                        p.Controller.PostAction();
-                    }
-                    InstallNextPackage();
-                    break;
-                case ChainedInstallerAction.LogEvent:
-                    AddToDetailsText(item.Index, e.Message);
-                    break;
-            }
-        }
-
-        private void SetPosition(int newPos)
-        {
-            installPos = newPos;
-            float stagePos = (float)installPos / (float)(stop - start);
-            int progress = (int)(50.0f * stagePos);
-            if (overall_progress >= 50)
-                progress += 50;
-            overall_progress = Math.Max(overall_progress, progress);
-            lock (installingItem)
-            {
-                installingItem.SubItems[3].Text = String.Format(Resources.StatusPercentage, overall_progress);
-            }
-        }
-
-        private void enableDetails_Click(object sender, EventArgs e)
-        {
-            showingDetails = !showingDetails;
-            enableDetails.Text = showingDetails ? "< &Hide Details" : "&Show Details >";
-            if (showingDetails)
-                detailsPanel.SetBounds(detailsPanel.Left, detailsPanel.Top,
-                    detailsPanel.Width, detailsText.Top - detailsPanel.Top - 5);
-            else
-                detailsPanel.SetBounds(detailsPanel.Left, detailsPanel.Top,
-                    detailsPanel.Width, detailsText.Bottom - detailsPanel.Top + 2);
-        }
-
-    }
+  public partial class InstallProgress : InstallerPanel
+  {
+    #region Members and constants
+
+    private Dictionary<string, int> progressLevels;
+    private bool nextOk; // Note: Back is always enabled and can be used to cancel the current op.
+    private bool finished;
+    private bool started;
+    private bool installedProductsNeedConfig;
+
+    private int leftToDownload;
+    private int leftToInstall;
+    private int downloadErrorCount;
+
+    ListViewItem installingItem;
+    int start, stop, installStep, installPos;
+    int overall_progress;
+    private bool showingDetails;
 
     enum InstallProgressState : int
     {
@@ -407,4 +40,466 @@
         Problem = 3,
         Warning = 4
     }
+
+    #endregion
+
+    #region Construction and setup
+
+    public InstallProgress()
+    {
+        InitializeComponent();
+        nextOk = true;
+        installedProductsNeedConfig = false;
+        progressLevels = new Dictionary<string, int>();
+        ListViewHelper.EnableDoubleBuffer(productList);
+    }
+
+    #endregion
+
+    public override bool NextOk()
+    {
+      return nextOk;
+    }
+
+    public override void Activate()
+    {
+      // Reset transient values if a previous run did not finish.
+      if (!finished)
+      {
+        started = false;
+        nextOk = true;
+        progressLevels.Clear();
+        NextButton.Text = Properties.Resources.NextButtonExecuteText;
+      }
+
+      // Refresh the list of installations to perform. Count how many downloads and
+      // installations we have on the way.
+      productList.Items.Clear();
+      leftToDownload = 0;
+      foreach (ProductCategory pc in ProductManager.ProductCategories)
+      {
+        foreach (Product p in pc.Products)
+        {
+          if (p.IsUpgrade || (p.ProposedInstalled && p.HasChanges()))
+          {
+            if (!p.FoundLocal)
+              leftToDownload++;
+
+            ListViewItem item = new ListViewItem(String.Empty);
+            item.Name = p.Name;
+            item.Tag = p;
+            item.SubItems.Add(p.TitleWithVersion);
+            if ((p.CurrentState == ProductState.DownloadCancelled) ||
+              (p.CurrentState == ProductState.DownloadError) ||
+              (p.CurrentState == ProductState.DownloadNoMirror) ||
+              (p.CurrentState == ProductState.DownloadStarted))
+              p.PostInitialize(false); // Reset to initial state.
+            item.SubItems.Add(GetProductStateString(p.CurrentState));
+            item.SubItems.Add(String.Empty);
+            item.SubItems.Add(String.Empty);
+            productList.Items.Add(item);
+          }
+          leftToInstall = productList.Items.Count;
+        }
+      }
+
+      base.Activate();
+    }
+
+    public override bool Next()
+    {
+      if (!finished)
+      {
+        started = true;
+        nextOk = false;
+        NextButton.Text = Properties.Resources.NextButtonDefaultText;
+        NextButton.Enabled = false;
+        actionLabel.Visible = false;
+        enableDetails.Visible = true;
+        SignalChange();
+
+        // Asynchronously start the task chain.
+        // Begin with downloading the packages or go directly to the installation process
+        // if we haven't anything to download.
+        if (leftToDownload > 0)
+          BeginInvoke(new MethodInvoker(DownloadPackages));
+        else
+          BeginInvoke(new MethodInvoker(InstallNextPackage));
+
+        return false;
+      }
+      return base.Next();
+    }
+
+    /// <summary>
+    /// Called when the user clicked the Back button. If we did not finish with everything
+    /// we have to ask the user to cancel remaining actions.
+    /// </summary>
+    /// <returns>True, if we are done or the user agreed to stop current actions, otherwise false.</returns>
+    public override bool Back()
+    {
+      if (started && !finished)
+      {
+        // Check if we are not yet finished because of errors for all remaining operations.
+        // If so just allow to go back. Otherwise ask to stop ongoing processes.
+        if (leftToInstall == 0 && (downloadErrorCount == leftToInstall))
+          return base.Back();
+
+        // Stop all remaining actions.
+        string message;
+        if (leftToDownload > 0)
+          message = Resources.ConfirmStopDownloadsAndGoBack;
+        else
+          message = Resources.ConfirmStopInstallationAndGoBack;
+        DialogResult answer = MessageBox.Show(message, Resources.ConfirmCancellationTitle,
+          MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation);
+        if (answer == DialogResult.Yes)
+        {
+          Cancel();
+          NextButton.Text = Properties.Resources.NextButtonDefaultText;
+        }
+        else
+          return false;
+      }
+      return base.Back();
+    }
+
+    /// <summary>
+    /// Called when the user clicked the cancel button or the close button of the main form.
+    /// Stops all running downloads or installation processes.
+    public override bool Cancel()
+    {
+      foreach (ListViewItem item in productList.Items)
+      {
+        Product p = (Product)item.Tag;
+        p.CancelActions();
+      }
+      return base.Cancel();
+    }
+
+    private string GetProductStateString(ProductState s)
+    {
+      string textStatus = null;
+
+      switch (s)
+      {
+        case ProductState.Unknown:
+          textStatus = String.Empty;
+          break;
+        case ProductState.WebRemote:

+          textStatus = "To be downloaded.";
+          break;
+        case ProductState.FoundLocal:
+          textStatus = "To be installed.";
+          break;
+        case ProductState.WillPerformUpgrade:
+          textStatus = "To be upgraded.";
+          break;
+        case ProductState.CurrentlyInstalled:
+          textStatus = "Installed locally.";
+          break;
+        case ProductState.DownloadStarted:
+          textStatus = "Download started.";
+          break;
+        case ProductState.DownloadInProgress:
+          textStatus = "Downloading.";
+          break;
+        case ProductState.DownloadSuccess:
+          textStatus = "Download success.";
+          break;
+        case ProductState.DownloadError:
+          textStatus = "Download failed.";
+          break;
+        case ProductState.DownloadNoMirror:
+          textStatus = "No Mirror Found";
+          break;
+        case ProductState.InstallStarted:
+          textStatus = "Install started.";
+          break;
+        case ProductState.InstallInProgress:
+          textStatus = "Installing.";
+          break;
+        case ProductState.InstallSuccess:
+          textStatus = "Install success.";
+          break;
+        case ProductState.InstallError:
+          textStatus = "Install error.";
+          break;
+        case ProductState.RemoveStarted:
+          textStatus = "Remove started.";
+          break;
+        case ProductState.RemoveInProgress:
+          textStatus = "Removing.";
+          break;
+        case ProductState.RemoveSuccess:
+          textStatus = "Successfully removed.";
+          break;
+        case ProductState.RemoveFailed:
+          textStatus = "Failed to remove.";
+          break;
+        case ProductState.UpdateStarted:
+          textStatus = "Update started.";
+          break;
+        case ProductState.UpdateInProgress:
+          textStatus = "Updating.";
+          break;
+        case ProductState.UpdateSuccess:
+          textStatus = "Update success.";
+          break;
+        case ProductState.UpdateFailed:
+          textStatus = "Update failed.";
+          break;
+        case ProductState.DownloadCancelled:
+          textStatus = "Download canceled";
+          break;
+        default:
+          textStatus = "Wrong product state";
+          break;
+      }
+
+      return textStatus;
+    }
+
+    private void AddToDetailsText(int index, string message)
+    {
+        detailsText.AppendText(String.Format("{0}: {1}{2}", 
+                                              (index + 1), 
+                                              message, 
+                                              Environment.NewLine
+                                              )
+                                );
+    }
+
+    private void DownloadPackages()
+    {
+      foreach (ListViewItem item in productList.Items)
+      {
+        Product p = (Product)item.Tag;
+        if (p.FoundLocal)
+          continue;
+
+        p.DownloadProductProgressChanged += new DownloadProductProgressHandler(product_DownloadProductProgressChanged);
+        p.DownloadProductCompleted += new DownloadProductCompleteHandler(product_DownloadProductCompleted);
+        progressLevels.Add(p.Name, -1);
+        p.Download();
+      }
+    }
+
+    void product_DownloadProductCompleted(object sender, AsyncCompletedEventArgs e)
+    {
+      Product p = (sender as Product);
+      ListViewItem currentItem = productList.Items.Find(p.Name, false)[0];
+      Debug.Assert(currentItem != null);
+
+      currentItem.StateImageIndex = (int)InstallProgressState.None;
+      currentItem.SubItems[2].Text = GetProductStateString(p.CurrentState);
+      if ((p.CurrentState == ProductState.DownloadError) ||
+        (p.CurrentState == ProductState.DownloadCancelled) ||
+        (p.CurrentState == ProductState.DownloadNoMirror))
+      {
+        downloadErrorCount++;
+        currentItem.SubItems[4].Text = e.Error.Message;
+        currentItem.SubItems[3].Text = Resources.TryAgainLink;
+      }
+      else
+        currentItem.SubItems[3].Text = String.Empty;
+
+      AddToDetailsText(currentItem.Index, String.Format("Download of product '{0}' {1}",
+                                                  p.Name,
+                                                  currentItem.SubItems[2].Text));
+      lock (this)
+      {
+        leftToDownload--;
+        if (leftToDownload == 0)
+        {
+          // After all downloads finished check if we got errors and ask back if the user
+          // still wants to install anything if there were any.
+          if (downloadErrorCount < leftToInstall)
+          {
+            DialogResult answer = MessageBox.Show(
+              Resources.ContinueAfterDownloadErrorText, Resources.ConfirmContinueTitle,
+              MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation);
+            if (answer == DialogResult.Yes)
+              BeginInvoke(new MethodInvoker(InstallNextPackage));
+          }
+        }
+      }
+    }
+
+    void product_DownloadProductProgressChanged(object sender, DownloadProgressChangedEventArgs e)
+    {
+      Product p = (sender as Product);
+      ListViewItem[] items = productList.Items.Find(p.Name, false);
+      if (items.Length == 0) // Can happen when we cancel everything.
+        return;
+
+      ListViewItem item = items[0];
+      if (progressLevels[p.Name] == -1)
+      {
+        string source = (e.UserState == null) ? "unknown" : e.UserState.ToString();
+        AddToDetailsText(item.Index, String.Format("Download of product '{0}' started from {1}",
+                                                    p.Name, source));
+      }
+
+      progressLevels[p.Name] = e.ProgressPercentage;
+      int progress = 0;
+      foreach (int val in progressLevels.Values)
+        progress += val;
+
+      if (item.StateImageIndex != (int)InstallProgressState.Download && e.BytesReceived > 0)
+        item.StateImageIndex = (int)InstallProgressState.Download;
+      item.SubItems[2].Text = GetProductStateString(p.CurrentState);
+      item.SubItems[3].Text = String.Format(Resources.StatusPercentage, e.ProgressPercentage);
+    }
+
+    /// <summary>
+    /// Look through our list of packages to install and pick the next pending one.
+    /// Order in the list does not matter.
+    /// </summary>
+    private void InstallNextPackage()
+    {
+      foreach (ListViewItem item in productList.Items)
+      {
+        Product p = (Product)item.Tag;
+        if (p.CurrentState == ProductState.WebRemote ||
+          p.CurrentState == ProductState.DownloadStarted ||
+          p.CurrentState == ProductState.DownloadMirrorSelection ||
+          p.CurrentState == ProductState.DownloadInProgress ||
+          p.CurrentState == ProductState.InstallInProgress ||
+          p.CurrentState == ProductState.UpdateInProgress ||
+          p.CurrentState == ProductState.RemoveInProgress)
+        {
+          continue;
+        }
+        if (p.CurrentState != ProductState.DownloadSuccess &&
+          p.CurrentState != ProductState.FoundLocal &&
+          p.CurrentState != ProductState.WillPerformUpgrade &&
+          p.CurrentState != ProductState.CurrentlyInstalled)
+        {
+          leftToInstall--;
+          continue;
+        }
+        installingItem = item;
+        p.ProductInstallationProgressChanged += new ProductInstationActionEventHandler(ProductInstallationProgressChanged);
+        p.MakeChanges();
+        break;
+      }
+    }
+
+    /// <summary>
+    /// Called after the last product was installed/updated (also if that process failed).
+    /// </summary>
+    private void InstallationFinished()
+    {
+      if (installedProductsNeedConfig == false)
+        (ParentControl as InstallWizardControl).RemoveConfigPages("Configuration");
+      nextOk = true;
+      NextButton.Enabled = true;
+
+      finished = true;
+    }
+
+    private void ProductInstallationProgressChanged(object sender, ChainedInstallerEventArgs e)
+    {
+      Product p = (sender as Product);
+      ListViewItem item = productList.Items.Find(p.Name, false)[0];
+      Debug.Assert(item != null);
+
+      switch (e.Action)
+      {
+        case ChainedInstallerAction.StartInstallation:
+          overall_progress = 0;
+          AddToDetailsText(item.Index, String.Format("Installation of product '{0}'start.", p.Name));
+          AddToDetailsText(item.Index, e.Message);
+          lock (installingItem)
+          {
+              installingItem.StateImageIndex = (int)InstallProgressState.Installing;
+              installingItem.SubItems[2].Text = GetProductStateString(p.CurrentState);
+          }
+          break;
+        case ChainedInstallerAction.HandleActionData:
+          AddToDetailsText(item.Index, e.Message);
+          break;
+        case ChainedInstallerAction.HandleActionStart:
+          AddToDetailsText(item.Index, e.Message);
+          break;
+        case ChainedInstallerAction.ProgressSetRange:
+          start = e.ProgressMin;
+          stop = e.ProgressMax;
+          installStep = 1;
+          installPos = 0;
+          if (overall_progress > 0)
+              overall_progress = 50;
+          AddToDetailsText(item.Index, String.Format("Min: {0} Max: {1}", e.ProgressMin.ToString(), e.ProgressMax.ToString()));
+          break;
+        case ChainedInstallerAction.ProgressSetStep:
+          installStep = e.ProgressStep;
+          break;
+        case ChainedInstallerAction.ProgressSetPosition:
+          if (e.ProgressPosition == stop && installPos == 0) return;
+          SetPosition(e.ProgressPosition);
+          break;
+        case ChainedInstallerAction.ProgressSingleStep:
+          SetPosition(installPos + installStep);
+          break;
+        case ChainedInstallerAction.EndInstallation:
+          AddToDetailsText(item.Index, e.Message);
+          break;
+        case ChainedInstallerAction.FinalAction:
+          AddToDetailsText(item.Index, "Final actions.");
+          lock (installingItem)
+          {
+              installingItem.StateImageIndex = (int)InstallProgressState.Ok;
+              installingItem.SubItems[2].Text = GetProductStateString(p.CurrentState);
+              installingItem.SubItems[3].Text = String.Empty;
+          }
+          AddToDetailsText(item.Index, installingItem.SubItems[2].Text);
+          if (p.Controller != null)
+          {
+              installedProductsNeedConfig = true;
+              p.Controller.PostAction();
+          }
+          lock (this)
+          {
+            leftToInstall--;
+            if (leftToInstall > 0)
+              BeginInvoke(new MethodInvoker(InstallNextPackage));
+            else
+              InstallationFinished();
+          }
+          break;
+        case ChainedInstallerAction.LogEvent:
+          AddToDetailsText(item.Index, e.Message);
+          break;
+      }
+    }
+
+    private void SetPosition(int newPos)
+    {
+        installPos = newPos;
+        float stagePos = (float)installPos / (float)(stop - start);
+        int progress = (int)(50.0f * stagePos);
+        if (overall_progress >= 50)
+            progress += 50;
+        overall_progress = Math.Max(overall_progress, progress);
+        lock (installingItem)
+        {
+            installingItem.SubItems[3].Text = String.Format(Resources.StatusPercentage, overall_progress);
+        }
+    }
+
+    private void enableDetails_Click(object sender, EventArgs e)
+    {
+        showingDetails = !showingDetails;
+        enableDetails.Text = showingDetails ? "< &Hide Details" : "&Show Details >";
+        if (showingDetails)
+            detailsPanel.SetBounds(detailsPanel.Left, detailsPanel.Top,
+                detailsPanel.Width, detailsText.Top - detailsPanel.Top - 5);
+        else
+            detailsPanel.SetBounds(detailsPanel.Left, detailsPanel.Top,
+                detailsPanel.Width, detailsText.Bottom - detailsPanel.Top + 2);
+    }
+
+  }
+
 }

=== modified file 'WexInstaller/InstallWizard/InstallProgressPanel.resx'
--- a/WexInstaller/InstallWizard/InstallProgressPanel.resx	2011-02-04 00:06:25 +0000
+++ b/WexInstaller/InstallWizard/InstallProgressPanel.resx	2011-03-02 15:58:16 +0000
@@ -125,7 +125,7 @@
         AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w
         LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0
         ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAADy
-        EQAAAk1TRnQBSQFMAgEBBQEAAVgBAAFYAQABEAEAARABAAT/ARkBAAj/AUIBTQE2BwABNgMAASgDAAFA
+        EQAAAk1TRnQBSQFMAgEBBQEAAWABAAFgAQABEAEAARABAAT/ARkBAAj/AUIBTQE2BwABNgMAASgDAAFA
         AwABIAMAAQEBAAEYBgABGBIAMP+QAAP/AQcBfAGYAQUBdgGSAQUBdgGSAQUBdgGSAQUBdgGSAQUBegGW
         AQUBegGWAQUBegGWAQUBdgGSAQUBdgGSAQUBdgGSAQUBdgGSAQUBegGWASkBiAGgA/+QAAEPAZcBtwEL
         AbwB4gEGAcMB6AECAbsB5AECAbsB5AECAcEB5QECAYsBpwFGAWQBegE2AawBzwECAbsB5AEBAbQB3AEB
@@ -205,6 +205,6 @@
 </value>
   </data>
   <metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
-    <value>25</value>
+    <value>52</value>
   </metadata>
 </root>
\ No newline at end of file

=== modified file 'WexInstaller/InstallWizard/InstallationComplete.Designer.cs'
--- a/WexInstaller/InstallWizard/InstallationComplete.Designer.cs	2011-02-04 16:44:54 +0000
+++ b/WexInstaller/InstallWizard/InstallationComplete.Designer.cs	2011-03-02 15:58:16 +0000
@@ -94,7 +94,6 @@
             this.DoubleBuffered = true;
             this.Name = "InstallationComplete";
             this.Size = new System.Drawing.Size(560, 499);
-            this.Load += new System.EventHandler(this.InstallationComplete_Load);
             this.Controls.SetChildIndex(this.copyEmail, 0);
             this.Controls.SetChildIndex(this.actionLabel, 0);
             this.Controls.SetChildIndex(this.copyClipboard, 0);

=== modified file 'WexInstaller/InstallWizard/InstallationComplete.cs'
--- a/WexInstaller/InstallWizard/InstallationComplete.cs	2011-02-08 14:32:28 +0000
+++ b/WexInstaller/InstallWizard/InstallationComplete.cs	2011-03-02 15:58:16 +0000
@@ -24,6 +24,11 @@
             return false;
         }
 
+        public override bool CancelOk()
+        {
+            return false;
+        }
+
         public override bool Next()
         {
             if (startWorkbench.Checked == true)
@@ -88,17 +93,13 @@
             Process.Start(email);
         }
 
-        private void InstallationComplete_Load(object sender, EventArgs e)
+        public override void Activate()
         {
+            base.Activate();
+
             if (NextButton != null)
-            {
                 NextButton.Text = "&Finish";
-                NextButton.Refresh();
-            }
-        }
 
-        public override void Activate()
-        {
             foreach (ProductCategory pc in ProductManager.ProductCategories)
             {
                 foreach (Product p in pc.Products)

=== modified file 'WexInstaller/MainForm.cs'
--- a/WexInstaller/MainForm.cs	2011-02-25 15:04:43 +0000
+++ b/WexInstaller/MainForm.cs	2011-03-02 15:58:16 +0000
@@ -1,131 +1,164 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Drawing;
-using System.Text;
+using System.Drawing;
 using System.Windows.Forms;
+
 using WexInstaller.Core;
+using WexInstaller.Properties;
 
 namespace WexInstaller
 {
-    public partial class MainForm : Form
-    {
-        private UserControl last;
-        private UserControl current;
-
-        public MainForm()
-        {
-            InitializeComponent();
-            if (InstallerConfiguration.IsDefault)
-            {
-                // Set the form to a good default position. We have to compute that manually though
-                // as the StartPosition property has no effect here.
-                Screen currentScreen = Screen.PrimaryScreen;
-                Rectangle workingArea = currentScreen.WorkingArea;
-                Location = new Point((workingArea.Width - Width) / 2, (workingArea.Height - Height) / 2);
-            }
-            else
-                this.SetDesktopLocation(InstallerConfiguration.Location.X, InstallerConfiguration.Location.Y);
-        }
-
-        public void DoInstall()
-        {
-            Logger.LogTrace("MainForm.DoInstall() called.");
-
-            last = welcomeControl;
-            current = installWizardControl;
-            current.Enabled = true;
-            last.Enabled = false;
-            installWizardControl.ShowWizard();
-        }
-
-        public void DoModify()
-        {
-            Logger.LogTrace("MainForm.DoModify() called.");
-
-            foreach (ProductCategory pc in ProductManager.ProductCategories)
-            {
-                foreach (Product p in pc.Products)
-                {
-                    p.UseCurrentlyInstalledPackage = true;
-                }
-            }
-
-            installWizardControl.ShowUpdateCheck(false);
-            installWizardControl.ShowFeatureSelection(true);
-            installWizardControl.ShowType(false);
-            installWizardControl.ShowDetailedUpdate(false);
-            DoInstall();
-        }
-
-        public void DoUpdate()
-        {
-            Logger.LogTrace("MainForm.DoUpdate() called.");
-
-            foreach (ProductCategory pc in ProductManager.ProductCategories)
-            {
-                foreach (Product p in pc.Products)
-                {
-                    p.UseCurrentlyInstalledPackage = false;
-                }
-            }
-
-            installWizardControl.ShowDetailedUpdate(true);
-            installWizardControl.ShowFeatureSelection(false);
-            installWizardControl.ShowType(false);
-            installWizardControl.ShowUpdateCheck(false);
-            DoInstall();
-        }
-
-        public void RemoveAll()
-        {
-            Logger.LogTrace("MainForm.RemoveAll() called.");
-
-            foreach (ProductCategory pc in ProductManager.ProductCategories)
-            {
-                foreach (Product p in pc.Products)
-                {
-                    p.UseCurrentlyInstalledPackage = true;
-                }
-            }
-
-            last = welcomeControl;
-            current = removeControl;
-            current.Enabled = true;
-            last.Enabled = false;
-            removeControl.ShowWizard();
-        }
-
-        public void GoBack()
-        {
-            last.Enabled = true;
-            current.Enabled = false;
-            last.Refresh();
-            last.BringToFront();
-        }
-
-        public void ShowAbout()
-        {
-            last = welcomeControl;
-            current = aboutPage;
-            current.Enabled = true;
-            last.Enabled = false;
-            aboutPage.BringToFront();
-        }
-
-        public void ShowResources()
-        {
-            last = welcomeControl;
-            current = resourcesPage;
-            current.Enabled = true;
-            last.Enabled = false;
-            resourcesPage.BringToFront();
-        }
-
-        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
-        {
-            InstallerConfiguration.Location = Location;
-        }
-    }
-
+  public partial class MainForm : Form
+  {
+    private UserControl last;
+    private UserControl current;
+
+    public MainForm()
+    {
+      InitializeComponent();
+      current = welcomeControl;
+      if (InstallerConfiguration.IsDefault)
+      {
+        // Set the form to a good default position. We have to compute that manually though
+        // as the StartPosition property has no effect here.
+        Screen currentScreen = Screen.PrimaryScreen;
+        Rectangle workingArea = currentScreen.WorkingArea;
+        Location = new Point((workingArea.Width - Width) / 2, (workingArea.Height - Height) / 2);
+      }
+      else
+        this.SetDesktopLocation(InstallerConfiguration.Location.X, InstallerConfiguration.Location.Y);
+    }
+
+    public void DoInstall()
+    {
+      Logger.LogTrace("MainForm.DoInstall() called.");
+
+      last = welcomeControl;
+      current = installWizardControl;
+      current.Enabled = true;
+      last.Enabled = false;
+      installWizardControl.ShowWizard();
+    }
+
+    public void DoModify()
+    {
+      Logger.LogTrace("MainForm.DoModify() called.");
+
+      foreach (ProductCategory pc in ProductManager.ProductCategories)
+      {
+          foreach (Product p in pc.Products)
+          {
+              p.UseCurrentlyInstalledPackage = true;
+          }
+      }
+
+      installWizardControl.ShowUpdateCheck(false);
+      installWizardControl.ShowFeatureSelection(true);
+      installWizardControl.ShowType(false);
+      installWizardControl.ShowDetailedUpdate(false);
+      DoInstall();
+    }
+
+    public void DoUpdate()
+    {
+      Logger.LogTrace("MainForm.DoUpdate() called.");
+
+      foreach (ProductCategory pc in ProductManager.ProductCategories)
+      {
+        foreach (Product p in pc.Products)
+        {
+          p.UseCurrentlyInstalledPackage = false;
+        }
+      }
+
+      installWizardControl.ShowDetailedUpdate(true);
+      installWizardControl.ShowFeatureSelection(false);
+      installWizardControl.ShowType(false);
+      installWizardControl.ShowUpdateCheck(false);
+      DoInstall();
+    }
+
+    public void RemoveAll()
+    {
+        Logger.LogTrace("MainForm.RemoveAll() called.");
+
+        foreach (ProductCategory pc in ProductManager.ProductCategories)
+        {
+            foreach (Product p in pc.Products)
+            {
+                p.UseCurrentlyInstalledPackage = true;
+            }
+        }
+
+        last = welcomeControl;
+        current = removeControl;
+        current.Enabled = true;
+        last.Enabled = false;
+        removeControl.ShowWizard();
+    }
+
+    public void GoBack()
+    {
+      if (last != null)
+      {
+        current.Enabled = false;
+        current = last;
+        last = null;
+        current.Enabled = true;
+        current.BringToFront();
+      }
+    }
+
+    public void ShowAbout()
+    {
+      last = welcomeControl;
+      current = aboutPage;
+      current.Enabled = true;
+      last.Enabled = false;
+      aboutPage.BringToFront();
+    }
+
+    public void ShowResources()
+    {
+      last = welcomeControl;
+      current = resourcesPage;
+      current.Enabled = true;
+      last.Enabled = false;
+      resourcesPage.BringToFront();
+    }
+
+    /// <summary>
+    /// Determine if the current action and ultimately the application
+    /// can be stopped and closed.
+    /// </summary>
+    /// <returns>True if we can shut down, false otherwise.</returns>
+    public bool CanClose()
+    {
+      bool result = (current == welcomeControl);
+      if (!result && (current == installWizardControl))
+          result = installWizardControl.WorkDone;
+
+      if (!result)
+      {
+        DialogResult answer = MessageBox.Show(
+          Resources.ConfirmCancelAndQuit, Resources.ConfirmCancellationTitle,
+          MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation);
+        result = (answer == DialogResult.Yes);
+        if (result && current == installWizardControl)
+          result = installWizardControl.CancelActions();
+      }
+      return result;
+    }
+
+    #region Event handling
+
+    private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
+    {
+        InstallerConfiguration.Location = Location;
+
+        // Cancel current actions if the user agrees.
+        e.Cancel = !CanClose();
+    }
+
+    #endregion
+
+  }
 }

=== modified file 'WexInstaller/Properties/Resources.Designer.cs'
--- a/WexInstaller/Properties/Resources.Designer.cs	2011-02-28 17:58:53 +0000
+++ b/WexInstaller/Properties/Resources.Designer.cs	2011-03-02 15:58:16 +0000
@@ -220,6 +220,33 @@
         }
         
         /// <summary>
+        ///   Looks up a localized string similar to Are you sure you wish to cancel and quit the installer?.
+        /// </summary>
+        internal static string ConfirmCancelAndQuit {
+            get {
+                return ResourceManager.GetString("ConfirmCancelAndQuit", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Confirm Cancellation.
+        /// </summary>
+        internal static string ConfirmCancellationTitle {
+            get {
+                return ResourceManager.GetString("ConfirmCancellationTitle", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Continue after download errors.
+        /// </summary>
+        internal static string ConfirmContinueTitle {
+            get {
+                return ResourceManager.GetString("ConfirmContinueTitle", resourceCulture);
+            }
+        }
+        
+        /// <summary>
         ///   Looks up a localized string similar to Are you sure you want to delete the server datafiles?.
         /// </summary>
         internal static string ConfirmDataRemovalText {
@@ -256,6 +283,33 @@
         }
         
         /// <summary>
+        ///   Looks up a localized string similar to There are unfinished downloads. Do you want to cancel them and go back?.
+        /// </summary>
+        internal static string ConfirmStopDownloadsAndGoBack {
+            get {
+                return ResourceManager.GetString("ConfirmStopDownloadsAndGoBack", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Are you sure you wish to stop the installation and go back?.
+        /// </summary>
+        internal static string ConfirmStopInstallationAndGoBack {
+            get {
+                return ResourceManager.GetString("ConfirmStopInstallationAndGoBack", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Not all products could be downloaded successfully. Do you still wish to install the remaining products?.
+        /// </summary>
+        internal static string ContinueAfterDownloadErrorText {
+            get {
+                return ResourceManager.GetString("ContinueAfterDownloadErrorText", resourceCulture);
+            }
+        }
+        
+        /// <summary>
         ///   Looks up a localized string similar to User must manually select which products and features will be installed..
         /// </summary>
         internal static string CustomSetupTypeDesc {
@@ -651,6 +705,15 @@
         }
         
         /// <summary>
+        ///   Looks up a localized string similar to ~Try again~.
+        /// </summary>
+        internal static string TryAgainLink {
+            get {
+                return ResourceManager.GetString("TryAgainLink", resourceCulture);
+            }
+        }
+        
+        /// <summary>
         ///   Looks up a localized string similar to There as an error downloading the product catalog.  Please check your connection and try again later..
         /// </summary>
         internal static string UnableToDownloadProductCatalog {

=== modified file 'WexInstaller/Properties/Resources.resx'
--- a/WexInstaller/Properties/Resources.resx	2011-02-28 17:58:53 +0000
+++ b/WexInstaller/Properties/Resources.resx	2011-03-02 15:58:16 +0000
@@ -375,4 +375,25 @@
   <data name="ConfirmDataRemovalTitle" xml:space="preserve">
     <value>Confirm Data Removal</value>
   </data>
+  <data name="ConfirmCancelAndQuit" xml:space="preserve">
+    <value>Are you sure you wish to cancel and quit the installer?</value>
+  </data>
+  <data name="ConfirmCancellationTitle" xml:space="preserve">
+    <value>Confirm Cancellation</value>
+  </data>
+  <data name="TryAgainLink" xml:space="preserve">
+    <value>~Try again~</value>
+  </data>
+  <data name="ConfirmStopDownloadsAndGoBack" xml:space="preserve">
+    <value>There are unfinished downloads. Do you want to cancel them and go back?</value>
+  </data>
+  <data name="ConfirmStopInstallationAndGoBack" xml:space="preserve">
+    <value>Are you sure you wish to stop the installation and go back?</value>
+  </data>
+  <data name="ConfirmContinueTitle" xml:space="preserve">
+    <value>Continue after download errors</value>
+  </data>
+  <data name="ContinueAfterDownloadErrorText" xml:space="preserve">
+    <value>Not all products could be downloaded successfully. Do you still wish to install the remaining products?</value>
+  </data>
 </root>
\ No newline at end of file

=== modified file 'WexInstaller/RemovePanels/RemoveProgress.Designer.cs'
--- a/WexInstaller/RemovePanels/RemoveProgress.Designer.cs	2011-02-04 16:34:12 +0000
+++ b/WexInstaller/RemovePanels/RemoveProgress.Designer.cs	2011-03-02 15:58:16 +0000
@@ -29,118 +29,119 @@
         /// </summary>
         private void InitializeComponent()
         {
-            this.enableDetails = new System.Windows.Forms.Button();
-            this.detailedLog = new System.Windows.Forms.TextBox();
-            this.packageListLabel = new System.Windows.Forms.Label();
-            this.productList = new WexInstaller.Core.ListViewWex();
-            this.blankHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
-            this.productHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
-            this.progressHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
-            this.detailsPanel = new System.Windows.Forms.Panel();
-            this.detailsPanel.SuspendLayout();
-            this.SuspendLayout();
-            // 
-            // enableDetails
-            // 
-            this.enableDetails.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
-            this.enableDetails.AutoSize = true;
-            this.enableDetails.BackColor = System.Drawing.SystemColors.Control;
-            this.enableDetails.Location = new System.Drawing.Point(3, 355);
-            this.enableDetails.Name = "enableDetails";
-            this.enableDetails.Size = new System.Drawing.Size(89, 23);
-            this.enableDetails.TabIndex = 1;
-            this.enableDetails.Text = "&Show Details >";
-            this.enableDetails.UseVisualStyleBackColor = false;
-            this.enableDetails.Visible = false;
-            this.enableDetails.Click += new System.EventHandler(this.enableDetails_Click);
-            // 
-            // detailedLog
-            // 
-            this.detailedLog.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
-            this.detailedLog.Location = new System.Drawing.Point(17, 325);
-            this.detailedLog.Multiline = true;
-            this.detailedLog.Name = "detailedLog";
-            this.detailedLog.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
-            this.detailedLog.Size = new System.Drawing.Size(513, 165);
-            this.detailedLog.TabIndex = 17;
-            // 
-            // packageListLabel
-            // 
-            this.packageListLabel.AutoSize = true;
-            this.packageListLabel.Font = new System.Drawing.Font("Tahoma", 10F);
-            this.packageListLabel.Location = new System.Drawing.Point(14, 81);
-            this.packageListLabel.Name = "packageListLabel";
-            this.packageListLabel.Size = new System.Drawing.Size(244, 17);
-            this.packageListLabel.TabIndex = 15;
-            this.packageListLabel.Text = "The following products will be removed";
-            // 
-            // productList
-            // 
-            this.productList.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
-                        | System.Windows.Forms.AnchorStyles.Left)
-                        | System.Windows.Forms.AnchorStyles.Right)));
-            this.productList.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
-            this.productList.CheckBoxes = true;
-            this.productList.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
+          this.enableDetails = new System.Windows.Forms.Button();
+          this.detailedLog = new System.Windows.Forms.TextBox();
+          this.packageListLabel = new System.Windows.Forms.Label();
+          this.productList = new WexInstaller.Core.ListViewWex();
+          this.blankHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
+          this.productHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
+          this.progressHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
+          this.detailsPanel = new System.Windows.Forms.Panel();
+          this.detailsPanel.SuspendLayout();
+          this.SuspendLayout();
+          // 
+          // enableDetails
+          // 
+          this.enableDetails.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+          this.enableDetails.AutoSize = true;
+          this.enableDetails.BackColor = System.Drawing.SystemColors.Control;
+          this.enableDetails.Location = new System.Drawing.Point(3, 355);
+          this.enableDetails.Name = "enableDetails";
+          this.enableDetails.Size = new System.Drawing.Size(89, 23);
+          this.enableDetails.TabIndex = 1;
+          this.enableDetails.Text = "&Show Details >";
+          this.enableDetails.UseVisualStyleBackColor = false;
+          this.enableDetails.Visible = false;
+          this.enableDetails.Click += new System.EventHandler(this.enableDetails_Click);
+          // 
+          // detailedLog
+          // 
+          this.detailedLog.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
+          this.detailedLog.Location = new System.Drawing.Point(17, 325);
+          this.detailedLog.Multiline = true;
+          this.detailedLog.Name = "detailedLog";
+          this.detailedLog.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
+          this.detailedLog.Size = new System.Drawing.Size(513, 165);
+          this.detailedLog.TabIndex = 17;
+          // 
+          // packageListLabel
+          // 
+          this.packageListLabel.AutoSize = true;
+          this.packageListLabel.Font = new System.Drawing.Font("Tahoma", 10F);
+          this.packageListLabel.Location = new System.Drawing.Point(14, 81);
+          this.packageListLabel.Name = "packageListLabel";
+          this.packageListLabel.Size = new System.Drawing.Size(244, 17);
+          this.packageListLabel.TabIndex = 15;
+          this.packageListLabel.Text = "The following products will be removed";
+          // 
+          // productList
+          // 
+          this.productList.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+                      | System.Windows.Forms.AnchorStyles.Left)
+                      | System.Windows.Forms.AnchorStyles.Right)));
+          this.productList.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
+          this.productList.CheckBoxes = true;
+          this.productList.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
             this.blankHeader,
             this.productHeader,
             this.progressHeader});
-            this.productList.GhostProductImages = false;
-            this.productList.Location = new System.Drawing.Point(3, 3);
-            this.productList.Name = "productList";
-            this.productList.OwnerDraw = true;
-            this.productList.ProgressBar = null;
-            this.productList.ProgressBarCol = 0;
-            this.productList.ProgressBarRow = 0;
-            this.productList.Size = new System.Drawing.Size(507, 339);
-            this.productList.TabIndex = 16;
-            this.productList.UseCompatibleStateImageBehavior = false;
-            this.productList.View = System.Windows.Forms.View.Details;
-            this.productList.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.productList_ColumnClick);
-            // 
-            // blankHeader
-            // 
-            this.blankHeader.Text = "";
-            this.blankHeader.Width = 20;
-            // 
-            // productHeader
-            // 
-            this.productHeader.Text = "      Product";
-            this.productHeader.Width = 407;
-            // 
-            // progressHeader
-            // 
-            this.progressHeader.Text = "Progress";
-            this.progressHeader.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
-            this.progressHeader.Width = 69;
-            // 
-            // detailsPanel
-            // 
-            this.detailsPanel.Controls.Add(this.productList);
-            this.detailsPanel.Controls.Add(this.enableDetails);
-            this.detailsPanel.Location = new System.Drawing.Point(17, 107);
-            this.detailsPanel.Name = "detailsPanel";
-            this.detailsPanel.Size = new System.Drawing.Size(513, 383);
-            this.detailsPanel.TabIndex = 18;
-            // 
-            // RemoveProgress
-            // 
-            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
-            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
-            this.Caption = "Remove Products";
-            this.Controls.Add(this.detailsPanel);
-            this.Controls.Add(this.detailedLog);
-            this.Controls.Add(this.packageListLabel);
-            this.Name = "RemoveProgress";
-            this.Size = new System.Drawing.Size(560, 499);
-            this.Load += new System.EventHandler(this.RemoveProgress_Load);
-            this.Controls.SetChildIndex(this.packageListLabel, 0);
-            this.Controls.SetChildIndex(this.detailedLog, 0);
-            this.Controls.SetChildIndex(this.detailsPanel, 0);
-            this.detailsPanel.ResumeLayout(false);
-            this.detailsPanel.PerformLayout();
-            this.ResumeLayout(false);
-            this.PerformLayout();
+          this.productList.GhostProductImages = false;
+          this.productList.Location = new System.Drawing.Point(3, 3);
+          this.productList.Name = "productList";
+          this.productList.OwnerDraw = true;
+          this.productList.ProgressBar = null;
+          this.productList.ProgressBarCol = 0;
+          this.productList.ProgressBarRow = 0;
+          this.productList.Size = new System.Drawing.Size(507, 339);
+          this.productList.TabIndex = 16;
+          this.productList.UseCompatibleStateImageBehavior = false;
+          this.productList.View = System.Windows.Forms.View.Details;
+          this.productList.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.productList_ColumnClick);
+          this.productList.ItemChecked += new System.Windows.Forms.ItemCheckedEventHandler(this.productList_ItemChecked);
+          // 
+          // blankHeader
+          // 
+          this.blankHeader.Text = "";
+          this.blankHeader.Width = 20;
+          // 
+          // productHeader
+          // 
+          this.productHeader.Text = "      Product";
+          this.productHeader.Width = 407;
+          // 
+          // progressHeader
+          // 
+          this.progressHeader.Text = "Progress";
+          this.progressHeader.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
+          this.progressHeader.Width = 69;
+          // 
+          // detailsPanel
+          // 
+          this.detailsPanel.Controls.Add(this.productList);
+          this.detailsPanel.Controls.Add(this.enableDetails);
+          this.detailsPanel.Location = new System.Drawing.Point(17, 107);
+          this.detailsPanel.Name = "detailsPanel";
+          this.detailsPanel.Size = new System.Drawing.Size(513, 383);
+          this.detailsPanel.TabIndex = 18;
+          // 
+          // RemoveProgress
+          // 
+          this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+          this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+          this.Caption = "Remove Products";
+          this.Controls.Add(this.detailsPanel);
+          this.Controls.Add(this.detailedLog);
+          this.Controls.Add(this.packageListLabel);
+          this.Name = "RemoveProgress";
+          this.Size = new System.Drawing.Size(560, 499);
+          this.Load += new System.EventHandler(this.RemoveProgress_Load);
+          this.Controls.SetChildIndex(this.packageListLabel, 0);
+          this.Controls.SetChildIndex(this.detailedLog, 0);
+          this.Controls.SetChildIndex(this.detailsPanel, 0);
+          this.detailsPanel.ResumeLayout(false);
+          this.detailsPanel.PerformLayout();
+          this.ResumeLayout(false);
+          this.PerformLayout();
 
         }
 

=== modified file 'WexInstaller/RemovePanels/RemoveProgress.cs'
--- a/WexInstaller/RemovePanels/RemoveProgress.cs	2011-02-08 14:34:41 +0000
+++ b/WexInstaller/RemovePanels/RemoveProgress.cs	2011-03-02 15:58:16 +0000
@@ -10,269 +10,273 @@
 
 namespace WexInstaller
 {
-    public partial class RemoveProgress : InstallerPanel
-    {
-        SolidBrush darkGrayBrush = new SolidBrush(Color.DarkGray);
-        Font titleFont = System.Drawing.SystemFonts.CaptionFont;
-
-        ListViewItem removingItem;
-        int start, stop, installStep, installPos;
-        int overall_progress;
-
-        private AutoResetEvent clearedToProceed;
-        private bool nextOk;
-        private bool backOk;
-        private bool executed;
-        private bool showingDetails;
-
-        public RemoveProgress()
-        {
-            InitializeComponent();
-
-            clearedToProceed = new AutoResetEvent(true);
+  public partial class RemoveProgress : InstallerPanel
+  {
+    SolidBrush darkGrayBrush = new SolidBrush(Color.DarkGray);
+    Font titleFont = System.Drawing.SystemFonts.CaptionFont;
+
+    ListViewItem removingItem;
+    int start, stop, installStep, installPos;
+    int overall_progress;
+
+    private AutoResetEvent clearedToProceed;
+    private bool nextOk;
+    private bool backOk;
+    private bool executed;
+    private bool showingDetails;
+
+    public RemoveProgress()
+    {
+      InitializeComponent();
+
+      clearedToProceed = new AutoResetEvent(true);
+      nextOk = true;
+      backOk = true;
+      executed = false;
+    }
+
+    private void ProductInstallationProgressChanged(object sender, ChainedInstallerEventArgs c)
+    {
+      switch (c.Action)
+      {
+          case ChainedInstallerAction.StartInstallation:
+              overall_progress = 0;
+              detailedLog.AppendText(String.Format("Beginning removal of {0}.{1}", (sender as Product).Title, Environment.NewLine));
+              detailedLog.AppendText(String.Format("{0}{1}", c.Message, Environment.NewLine));
+              break;
+          case ChainedInstallerAction.HandleActionData:
+              detailedLog.AppendText(String.Format("{0}{1}", c.Message, Environment.NewLine));
+              break;
+          case ChainedInstallerAction.HandleActionStart:
+              detailedLog.AppendText(String.Format("{0}{1}", c.Message, Environment.NewLine));
+              break;
+          case ChainedInstallerAction.ProgressSetRange:
+              detailedLog.AppendText(String.Format("Min: {0} Max: {1}{2}", c.ProgressMin.ToString(), c.ProgressMax.ToString(), Environment.NewLine));
+              start = c.ProgressMin;
+              stop = c.ProgressMax;
+              installStep = 1;
+              installPos = 0;
+              if (overall_progress > 0)
+                  overall_progress = 50;
+              break;
+          case ChainedInstallerAction.ProgressSetStep:
+              //detailedLog.AppendText(String.Format("Step: {0}{1}", c.ProgressStep.ToString(), Environment.NewLine));
+              installStep = c.ProgressStep;
+              break;
+          case ChainedInstallerAction.ProgressSetPosition:
+              //detailedLog.AppendText(String.Format("Position: {0}{1}", c.ProgressPosition.ToString(), Environment.NewLine));
+              if (c.ProgressPosition == stop && installPos == 0) return;
+              SetPosition(c.ProgressPosition);
+              break;
+          case ChainedInstallerAction.ProgressSingleStep:
+              SetPosition(installPos + installStep);
+              break;
+          case ChainedInstallerAction.EndInstallation:
+              detailedLog.AppendText(String.Format("{0}{1}", c.Message, Environment.NewLine));
+              break;
+          case ChainedInstallerAction.LogEvent:
+              detailedLog.AppendText(String.Format("{0}{1}", c.Message, Environment.NewLine));
+              break;
+          case ChainedInstallerAction.FinalAction:
+              Product p = (sender as Product);
+              if (c.ExitCode != 0)
+              {
+                  detailedLog.AppendText(String.Format("The product {0} failed to remove successfully.{1}", p.Title, Environment.NewLine));
+              }
+              else
+              {
+                  detailedLog.AppendText(String.Format("The product {0} was successfully removed.{1}", p.Title, Environment.NewLine));
+              }
+              detailedLog.AppendText(Environment.NewLine);
+
+              clearedToProceed.Set();
+
+              if (removingItem.Index == productList.Items.Count - 1)
+              {
+                  nextOk = true;
+                  backOk = false;
+                  SignalChange();
+              }
+              break;
+      }
+    }
+
+    private void SetPosition(int newPos)
+    {
+      installPos = newPos;
+      float stagePos = (float)installPos / (float)(stop - start);
+      int progress = (int)(50.0f * stagePos);
+      if (overall_progress >= 50)
+          progress += 50;
+      overall_progress = Math.Max(overall_progress, progress);
+      removingItem.SubItems[2].Text = String.Format(Resources.StatusPercentage, overall_progress);
+    }
+
+    public override void Activate()
+    {
+      productList.Items.Clear();
+      foreach (ProductCategory pc in ProductManager.ProductCategories)
+      {
+        foreach (Product p in pc.Products)
+        {
+          if (p.Installed)
+          {
+            ListViewItem item = new ListViewItem(String.Empty);
+            item.Name = p.Title;
+            item.Tag = p;
+            item.SubItems.Add(p.TitleWithVersion);
+            item.SubItems.Add(String.Empty);
+            item.Checked = true;
+            productList.Items.Add(item);
+          }
+        }
+      }
+
+      nextOk = productList.Items.Count > 0;
+
+      if (!executed)
+        NextButton.Text = Properties.Resources.NextButtonExecuteText;
+
+      base.Activate();
+    }
+
+    public override bool NextOk()
+    {
+      return nextOk;
+    }
+
+    public override bool BackOk()
+    {
+      return backOk;
+    }
+
+    public override bool Next()
+    {
+      if (!executed)
+      {
+        if (productList.CheckedItems.Count > 0)
+        {
+          DialogResult result = MessageBox.Show(Properties.Resources.ConfirmRemovalText,
+                                                Properties.Resources.ConfirmRemovalTitle, 
+                                                MessageBoxButtons.YesNo, 
+                                                MessageBoxIcon.Exclamation);
+          if (result == DialogResult.Yes)
+          {
+            enableDetails.Visible = true;
+            nextOk = false;
+            backOk = false;
+            SignalChange();
+
+            int totalRemaining = productList.CheckedItems.Count;
+            while (totalRemaining > 0)
+            {
+                if (clearedToProceed.WaitOne(1))
+                {
+                    totalRemaining = productList.CheckedItems.Count;
+                    foreach (ListViewItem item in productList.CheckedItems)
+                    {
+                        Product p = (item.Tag as Product);
+                        ProductState state = p.CurrentState;
+                        if (p.CurrentState == ProductState.RemoveStarted ||
+                            p.CurrentState == ProductState.RemoveInProgress)
+                        {
+                            continue;
+                        }
+
+                        if (p.CurrentState == ProductState.InstallSuccess ||
+                            p.CurrentState == ProductState.CurrentlyInstalled)
+                        {
+                            clearedToProceed.Reset();
+                            removingItem = item;
+                            p.ProductInstallationProgressChanged += new ProductInstationActionEventHandler(ProductInstallationProgressChanged);
+                            p.Remove();
+                            totalRemaining--;
+                            break;
+                        }
+                        else
+                        {
+                            totalRemaining--;
+                        }
+                    }
+                }
+                Application.DoEvents();
+                Thread.Sleep(0);
+            }
+
+            while (!clearedToProceed.WaitOne(1))
+            {
+                Application.DoEvents();
+            }
+
+            NextButton.Text = Properties.Resources.NextButtonDefaultText;
+            NextButton.Enabled = true;
+            NextButton.Refresh();
             nextOk = true;
             backOk = true;
-            executed = false;
-        }
-
-        private void ProductInstallationProgressChanged(object sender, ChainedInstallerEventArgs c)
-        {
-            switch (c.Action)
-            {
-                case ChainedInstallerAction.StartInstallation:
-                    overall_progress = 0;
-                    detailedLog.AppendText(String.Format("Beginning removal of {0}.{1}", (sender as Product).Title, Environment.NewLine));
-                    detailedLog.AppendText(String.Format("{0}{1}", c.Message, Environment.NewLine));
-                    break;
-                case ChainedInstallerAction.HandleActionData:
-                    detailedLog.AppendText(String.Format("{0}{1}", c.Message, Environment.NewLine));
-                    break;
-                case ChainedInstallerAction.HandleActionStart:
-                    detailedLog.AppendText(String.Format("{0}{1}", c.Message, Environment.NewLine));
-                    break;
-                case ChainedInstallerAction.ProgressSetRange:
-                    detailedLog.AppendText(String.Format("Min: {0} Max: {1}{2}", c.ProgressMin.ToString(), c.ProgressMax.ToString(), Environment.NewLine));
-                    start = c.ProgressMin;
-                    stop = c.ProgressMax;
-                    installStep = 1;
-                    installPos = 0;
-                    if (overall_progress > 0)
-                        overall_progress = 50;
-                    break;
-                case ChainedInstallerAction.ProgressSetStep:
-                    //detailedLog.AppendText(String.Format("Step: {0}{1}", c.ProgressStep.ToString(), Environment.NewLine));
-                    installStep = c.ProgressStep;
-                    break;
-                case ChainedInstallerAction.ProgressSetPosition:
-                    //detailedLog.AppendText(String.Format("Position: {0}{1}", c.ProgressPosition.ToString(), Environment.NewLine));
-                    if (c.ProgressPosition == stop && installPos == 0) return;
-                    SetPosition(c.ProgressPosition);
-                    break;
-                case ChainedInstallerAction.ProgressSingleStep:
-                    SetPosition(installPos + installStep);
-                    break;
-                case ChainedInstallerAction.EndInstallation:
-                    detailedLog.AppendText(String.Format("{0}{1}", c.Message, Environment.NewLine));
-                    break;
-                case ChainedInstallerAction.LogEvent:
-                    detailedLog.AppendText(String.Format("{0}{1}", c.Message, Environment.NewLine));
-                    break;
-                case ChainedInstallerAction.FinalAction:
-                    Product p = (sender as Product);
-                    if (c.ExitCode != 0)
-                    {
-                        detailedLog.AppendText(String.Format("The product {0} failed to remove successfully.{1}", p.Title, Environment.NewLine));
-                    }
-                    else
-                    {
-                        detailedLog.AppendText(String.Format("The product {0} was successfully removed.{1}", p.Title, Environment.NewLine));
-                    }
-                    detailedLog.AppendText(Environment.NewLine);
-
-                    clearedToProceed.Set();
-
-                    if (removingItem.Index == productList.Items.Count - 1)
-                    {
-                        nextOk = true;
-                        backOk = false;
-                        SignalChange();
-                    }
-                    break;
-            }
-        }
-
-        private void SetPosition(int newPos)
-        {
-            installPos = newPos;
-            float stagePos = (float)installPos / (float)(stop - start);
-            int progress = (int)(50.0f * stagePos);
-            if (overall_progress >= 50)
-                progress += 50;
-            overall_progress = Math.Max(overall_progress, progress);
-            removingItem.SubItems[2].Text = String.Format(Resources.StatusPercentage, overall_progress);
-        }
-
-        public override void Activate()
-        {
-            productList.Items.Clear();
-            foreach (ProductCategory pc in ProductManager.ProductCategories)
-            {
-                foreach (Product p in pc.Products)
-                {
-                    if (p.Installed)
-                    {
-                        ListViewItem item = new ListViewItem(String.Empty);
-                        item.Name = p.Title;
-                        item.Tag = p;
-                        item.SubItems.Add(p.TitleWithVersion);
-                        item.SubItems.Add(String.Empty);
-                        item.Checked = true;
-                        productList.Items.Add(item);
-                    }
-                }
-            }
-
-            if (!executed)
-            {
-                NextButton.Text = Properties.Resources.NextButtonExecuteText;
-                NextButton.Refresh();
-            }
-
-            base.Activate();
-        }
-
-        public override bool NextOk()
-        {
-            return nextOk;
-        }
-
-        public override bool BackOk()
-        {
-            return backOk;
-        }
-
-        public override bool Next()
-        {
-            if (!executed)
-            {
-                executed = true;
-
-                if (productList.CheckedItems.Count > 0)
-                {
-
-                    DialogResult result = MessageBox.Show(Properties.Resources.ConfirmRemovalText,
-                                                          Properties.Resources.ConfirmRemovalTitle, 
-                                                          MessageBoxButtons.YesNo, 
-                                                          MessageBoxIcon.Exclamation);
-                    if (result == DialogResult.Yes)
-                    {
-                        enableDetails.Visible = true;
-                        nextOk = false;
-                        backOk = false;
-                        SignalChange();
-
-                        int totalRemaining = productList.CheckedItems.Count;
-                        while (totalRemaining > 0)
-                        {
-                            if (clearedToProceed.WaitOne(1))
-                            {
-                                totalRemaining = productList.CheckedItems.Count;
-                                foreach (ListViewItem item in productList.CheckedItems)
-                                {
-                                    Product p = (item.Tag as Product);
-                                    ProductState state = p.CurrentState;
-                                    if (p.CurrentState == ProductState.RemoveStarted ||
-                                        p.CurrentState == ProductState.RemoveInProgress)
-                                    {
-                                        continue;
-                                    }
-
-                                    if (p.CurrentState == ProductState.InstallSuccess ||
-                                       p.CurrentState == ProductState.CurrentlyInstalled)
-                                    {
-                                        clearedToProceed.Reset();
-                                        removingItem = item;
-                                        p.ProductInstallationProgressChanged += new ProductInstationActionEventHandler(ProductInstallationProgressChanged);
-                                        p.Remove();
-                                        totalRemaining--;
-                                        break;
-                                    }
-                                    else
-                                    {
-                                        totalRemaining--;
-                                    }
-                                }
-                            }
-                            Application.DoEvents();
-                            Thread.Sleep(0);
-                        }
-
-                        while (!clearedToProceed.WaitOne(1))
-                        {
-                            Application.DoEvents();
-                        }
-
-                        NextButton.Text = Properties.Resources.NextButtonDefaultText;
-                        NextButton.Enabled = true;
-                        NextButton.Refresh();
-                        nextOk = true;
-                        backOk = true;
-                        SignalChange();
-                    }
-                }
-                else
-                {
-                    nextOk = false;
-                    backOk = true;
-                    NextButton.Enabled = false;
-                    BackButton.Enabled = true;
-                    MessageBox.Show(Properties.Resources.NoneSelectedForRemovalText,
-                                    Properties.Resources.NoneSelectedForRemovalTitle,
-                                    MessageBoxButtons.OK, 
-                                    MessageBoxIcon.Exclamation);
-                    SignalChange();
-                }
-
-                return false;
-            }
-            return base.Next();
-        }
-
-        private void productList_ColumnClick(object sender, ColumnClickEventArgs e)
-        {
-            switch (e.Column)
-            {
-                case 0:
-                    ListView l = sender as ListView;
-                    foreach (ListViewItem li in l.Items)
-                    {
-                        li.Checked = !li.Checked;
-                    }
-                    break;
-                default:
-                    break;
-            }
-        }
-
-        private void enableDetails_Click(object sender, EventArgs e)
-        {
-            showingDetails = !showingDetails;
-            enableDetails.Text = showingDetails ? "< &Hide Details" : "&Show Details >";
-            if (showingDetails)
-                detailsPanel.SetBounds(detailsPanel.Left, detailsPanel.Top,
-                    detailsPanel.Width, detailedLog.Top - detailsPanel.Top - 5);
-            else
-                detailsPanel.SetBounds(detailsPanel.Left, detailsPanel.Top,
-                    detailsPanel.Width, detailedLog.Bottom - detailsPanel.Top + 2);
-        }
-
-        private void RemoveProgress_Load(object sender, EventArgs e)
-        {
-            if (NextButton != null)
-            {
-                NextButton.Text = Properties.Resources.NextButtonExecuteText;
-                NextButton.Refresh();
-            }
-        }
-    }
+            SignalChange();
+
+            executed = true;
+          }
+        }
+        else
+        {
+          nextOk = false;
+          backOk = true;
+          NextButton.Enabled = false;
+          BackButton.Enabled = true;
+          MessageBox.Show(Properties.Resources.NoneSelectedForRemovalText,
+                          Properties.Resources.NoneSelectedForRemovalTitle,
+                          MessageBoxButtons.OK, 
+                          MessageBoxIcon.Exclamation);
+          SignalChange();
+        }
+
+        return false;
+      }
+      return base.Next();
+    }
+
+    private void productList_ColumnClick(object sender, ColumnClickEventArgs e)
+    {
+      switch (e.Column)
+      {
+        case 0:
+          ListView l = sender as ListView;
+          foreach (ListViewItem li in l.Items)
+          {
+              li.Checked = !li.Checked;
+          }
+          break;
+        default:
+            break;
+      }
+    }
+
+    private void enableDetails_Click(object sender, EventArgs e)
+    {
+        showingDetails = !showingDetails;
+        enableDetails.Text = showingDetails ? "< &Hide Details" : "&Show Details >";
+        if (showingDetails)
+            detailsPanel.SetBounds(detailsPanel.Left, detailsPanel.Top,
+                detailsPanel.Width, detailedLog.Top - detailsPanel.Top - 5);
+        else
+            detailsPanel.SetBounds(detailsPanel.Left, detailsPanel.Top,
+                detailsPanel.Width, detailedLog.Bottom - detailsPanel.Top + 2);
+    }
+
+    private void RemoveProgress_Load(object sender, EventArgs e)
+    {
+        if (NextButton != null)
+        {
+            NextButton.Text = Properties.Resources.NextButtonExecuteText;
+            NextButton.Refresh();
+        }
+    }
+
+    private void productList_ItemChecked(object sender, ItemCheckedEventArgs e)
+    {
+      nextOk = productList.CheckedItems.Count > 0;
+      NextButton.Enabled = nextOk;
+    }
+  }
 }

=== modified file 'installer-vs2010.sln'
--- a/installer-vs2010.sln	2011-02-25 21:45:33 +0000
+++ b/installer-vs2010.sln	2011-03-02 15:58:16 +0000
@@ -25,6 +25,7 @@
 		{64664A39-D6C5-4842-A879-BDD915DDDCCF} = {64664A39-D6C5-4842-A879-BDD915DDDCCF}
 		{07B31F5A-9F17-4ACF-B3B6-2AF9000B1414} = {07B31F5A-9F17-4ACF-B3B6-2AF9000B1414}
 		{13190F97-5D72-4949-A77A-500A57DA44BB} = {13190F97-5D72-4949-A77A-500A57DA44BB}
+		{57913AF9-D535-4B13-A088-8DF74FD47A53} = {57913AF9-D535-4B13-A088-8DF74FD47A53}
 	EndProjectSection
 EndProject
 Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "Setup_Web", "Setup\Setup_Web.wixproj", "{101DB4CD-090C-44CE-9D19-C6EA941D2DCF}"

Attachment: [text/bzr-bundle] bzr/mike.lischke@oracle.com-20110302155816-uw74hak2eh37u4lr.bundle
Thread
bzr commit into wex-installer-1.0 branch (mike.lischke:350) Mike Lischke2 Mar