/// <summary>
		/// Raises the BeforeSwitchToLatestVersion event
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected virtual void OnBeforeSwitchToLatestVersion(object sender, AutoUpdateManagerWithDownloadDescriptorCancelEventArgs e)
		{
			try
			{				
				if (this.BeforeSwitchToLatestVersion != null)
					this.BeforeSwitchToLatestVersion(sender, e);

				// cancel if it's not supposed to switch to the latest version automatically
				if (!e.OverrideOptions && !e.Cancel)
					if (!_options.AutomaticallySwitchToNewVersion)
						e.Cancel = true;
			}
			catch(ThreadAbortException)
			{

			}
			catch(Exception ex)
			{
				Trace.WriteLine(ex);
			}
		}
		/// <summary>
		/// Occurs when the engine's background thread runs
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="ea"></param>
		protected virtual void OnThreadRun(object sender, BackgroundThreadStartEventArgs threadStartEventArgs)
		{
			bool downloaded = false;
			bool installed = false;
			bool finalized = false;
			AutoUpdateDownloadDescriptor recommendedUpdateDescriptor = null;

			try
			{
				/*
				 * Raise the AutoUpdateProcessStarted event
				 * */
				AutoUpdateManagerEventArgs startedArgs = new AutoUpdateManagerEventArgs(this, null);
				this.OnAutoUpdateProcessStarted(this, startedArgs);

				#region Step 1. QueryForLatestVersion

				/*
				 * Raise the BeforeQueryForLatestVersion event
				 * */
				AutoUpdateManagerCancelEventArgs beforeQueryArgs = new AutoUpdateManagerCancelEventArgs(this, startedArgs.ProgressViewer, false);
				this.OnBeforeQueryForLatestVersion(this, beforeQueryArgs);

				// create an array list to hold all of the available updates
				ArrayList listOfAvailableDownloads = new ArrayList();				
				
				// use the downloaders to check for downloads
				foreach(AutoUpdateDownloader downloader in _downloaders)
				{
					try
					{
						// query the latest update available for the specified product
						AutoUpdateDownloadDescriptor updateAvailable;						
						
						// if the downloader finds an update is available
						if (downloader.QueryLatestVersion(beforeQueryArgs.ProgressViewer, _options, _productToUpdate, out updateAvailable))														
							// add it to the list of downloads available
							listOfAvailableDownloads.Add(updateAvailable);						
					}
					catch(ThreadAbortException ex)
					{
						throw new Exception(ex.Message, ex);
					}
					catch(Exception ex)
					{
						Trace.WriteLine(ex);
					}
				}

				// create a simple array of the updates that are available for download
				AutoUpdateDownloadDescriptor[] availableDownloads = listOfAvailableDownloads.ToArray(typeof(AutoUpdateDownloadDescriptor)) as AutoUpdateDownloadDescriptor[];

				// sort and select the download that contains the newest version
				recommendedUpdateDescriptor = this.SelectTheDownloadWithTheNewestUpdate(availableDownloads);

				/*
				 * Raise the AfterQueryForLatestVersion event
				 * */
				AutoUpdateManagerWithDownloadDescriptorEventArgs afterQueryArgs = new AutoUpdateManagerWithDownloadDescriptorEventArgs(this, beforeQueryArgs.ProgressViewer, recommendedUpdateDescriptor);
				this.OnAfterQueryForLatestVersion(this, afterQueryArgs);

				// if the manager could not find a suitable recomendation for downloading, we're done 
				if (recommendedUpdateDescriptor == null)
				{
					/*
					 * Raise the NoLaterVersionAvailable event
					 * */
					this.OnNoLaterVersionAvailable(this, new AutoUpdateManagerEventArgs(this, afterQueryArgs.ProgressViewer));
					return;
				}
				
				// or if the product to update is newer or equal to the version of the recommended update picked for downloading
				if (_productToUpdate.Version >= recommendedUpdateDescriptor.Manifest.Product.Version)
				{
					/*
					 * Raise the NoLaterVersionAvailable event
					 * */
					this.OnNoLaterVersionAvailable(this, new AutoUpdateManagerEventArgs(this, afterQueryArgs.ProgressViewer));
					return;
				}
                
				#endregion

				#region Step 2. Download

				/*
				 * Create the path including the filename where the .Update file will be downloaded to locally
				 * (ex: "C:\Program Files\Razor\1.0.0.0.update")				  
				 * */
				recommendedUpdateDescriptor.DownloadedPath = Path.Combine(_options.DownloadPath, Path.GetFileName(recommendedUpdateDescriptor.Manifest.UrlOfUpdate));

				/*
				 * Raise the BeforeDownload event
				 * */
				AutoUpdateManagerWithDownloadDescriptorCancelEventArgs beforeDownloadArgs = new AutoUpdateManagerWithDownloadDescriptorCancelEventArgs(this, afterQueryArgs.ProgressViewer, recommendedUpdateDescriptor, false);
				this.OnBeforeDownload(this, beforeDownloadArgs);
				
				// bail if the download was cancelled
				if (beforeDownloadArgs.Cancel)
					return;				
				
				// use the downloader that found the update to download it
				// the update may be available via some proprietary communications channel (http, ftp, or Unc paths)
				downloaded = recommendedUpdateDescriptor.Downloader.Download(beforeDownloadArgs.ProgressViewer, recommendedUpdateDescriptor);

				/*
				 * Raise the AfterDownload event
				 * */
				AutoUpdateManagerWithDownloadDescriptorEventArgs afterDownloadArgs = new AutoUpdateManagerWithDownloadDescriptorEventArgs(this, beforeDownloadArgs.ProgressViewer, recommendedUpdateDescriptor);
				afterDownloadArgs.OperationStatus = downloaded;
				this.OnAfterDownload(this, afterDownloadArgs);
				
				// if the download failed bail out
				if (!downloaded)
					return;

				#endregion

				#region Step 3. Install

				/*
				 * Raise the BeforeInstall event
				 * */
				AutoUpdateManagerWithDownloadDescriptorCancelEventArgs beforeInstallArgs = new AutoUpdateManagerWithDownloadDescriptorCancelEventArgs(this, afterDownloadArgs.ProgressViewer, recommendedUpdateDescriptor, false);
				this.OnBeforeInstall(this, beforeInstallArgs);

				// if the installation was not cancelled
				if (!beforeInstallArgs.Cancel)
				{				
					// install the update
					installed = this.InstallUpdate(beforeInstallArgs.ProgressViewer, recommendedUpdateDescriptor);

					// if the update was installed, now is the time to finalize the installation
					if (installed)
					{
						// all the downloader to finalize the install, there may be things to do after the installation is complete
						// depending upon the source of the download, and again since it's plugable only the downloader will know how to deal with it
						// and by default it will just delete the downloaded .update file
						finalized = recommendedUpdateDescriptor.Downloader.FinalizeInstallation(beforeInstallArgs.ProgressViewer, recommendedUpdateDescriptor);
                    }    															
				}

				/*
				 * Raise the AfterInstall event
				 * */				
				AutoUpdateManagerWithDownloadDescriptorEventArgs afterInstallArgs = new AutoUpdateManagerWithDownloadDescriptorEventArgs(this, beforeInstallArgs.ProgressViewer, recommendedUpdateDescriptor);				
				afterInstallArgs.OperationStatus = installed && finalized;																	
				this.OnAfterInstall(this, afterInstallArgs);
				
				#endregion

				#region Step 4. Update Alternate Path

				/*
				 * Raise the X event
				 * */
				AutoUpdateManagerWithDownloadDescriptorCancelEventArgs beforeUpdateAltPathArgs = new AutoUpdateManagerWithDownloadDescriptorCancelEventArgs(this, afterInstallArgs.ProgressViewer, recommendedUpdateDescriptor, false);
				this.OnBeforeUpdateAlternatePath(this, beforeUpdateAltPathArgs);

				if (!beforeUpdateAltPathArgs.Cancel)
				{
					// copy the manifest & the update there 
					this.AdjustUrlOfUpdateInManifest(_options, recommendedUpdateDescriptor);
					this.CreateCopyOfManifestInAlternatePath(beforeUpdateAltPathArgs.ProgressViewer, recommendedUpdateDescriptor);
					this.CreateCopyOfUpdateInAlternatePath(beforeUpdateAltPathArgs.ProgressViewer, recommendedUpdateDescriptor);
				}
				
				// delete the downloaded .update file, don't leave it laying around
				File.Delete(recommendedUpdateDescriptor.DownloadedPath);

				AutoUpdateManagerWithDownloadDescriptorEventArgs afterUpdateAltPathArgs = new AutoUpdateManagerWithDownloadDescriptorEventArgs(this, beforeUpdateAltPathArgs.ProgressViewer, recommendedUpdateDescriptor);
				this.OnAfterUpdateAlternatePath(this, afterUpdateAltPathArgs);

				#endregion

				#region Step 5. Switch to Latest Version

				if (installed)
				{
					/*
					 * Raise the BeforeSwitchToLatestVersion event
					 * */
					AutoUpdateManagerWithDownloadDescriptorCancelEventArgs beforeSwitchedArgs = new AutoUpdateManagerWithDownloadDescriptorCancelEventArgs(this, afterUpdateAltPathArgs.ProgressViewer, recommendedUpdateDescriptor, false);
					this.OnBeforeSwitchToLatestVersion(this, beforeSwitchedArgs);

					// if switching to the latest version was not cancelled
					if (!beforeSwitchedArgs.Cancel)
					{
						/*
						* Raise the SwitchToLatestVersion event
						* */
						AutoUpdateManagerWithDownloadDescriptorEventArgs switchToArgs = new AutoUpdateManagerWithDownloadDescriptorEventArgs(this, beforeSwitchedArgs.ProgressViewer, recommendedUpdateDescriptor);
						this.OnSwitchToLatestVersion(this, switchToArgs);

						// the rest should be history because the AutoUpdateSnapIn should catch that event and switch to the latest version using the bootstrap
					}
				}

				#endregion
			}
			catch(ThreadAbortException)
			{
				Debug.WriteLine("The AutoUpdateManager has encountered a ThreadAbortException.\n\tThe auto-update thread has been aborted.", MY_TRACE_CATEGORY);

				try
				{
					// delete the downloaded .update file, don't leave it laying around
					File.Delete(recommendedUpdateDescriptor.DownloadedPath);
				}
				catch(Exception)
				{

				}
			}
			catch(Exception ex)
			{
				Debug.WriteLine(ex);
				this.OnException(this, new AutoUpdateExceptionEventArgs(ex));				
			}
		}
		/// <summary>
		/// Raises the BeforeUpdateAlternatePath event
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected virtual void OnBeforeUpdateAlternatePath(object sender, AutoUpdateManagerWithDownloadDescriptorCancelEventArgs e)
		{
			try
			{				
				if (this.BeforeUpdateAlternatePath != null)
					this.BeforeUpdateAlternatePath(sender, e);

				// cancel if it's not supposed to update the alternate path automatically
				if (!e.OverrideOptions && !e.Cancel)
					if (!_options.AutomaticallyUpdateAlternatePath)
						e.Cancel = true;
			}
			catch(ThreadAbortException)
			{

			}
			catch(Exception ex)
			{
				Trace.WriteLine(ex);
			}
		}