示例#1
0
        /// <summary>
        /// Override the OnRun method to capture the date and time that the job was started and to change it's state
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected override void OnRun(object sender, BackgroundThreadStartEventArgs e)
        {
            _job._dateTimeStarted = DateTime.Now;
            _job._state           = BackgroundThreadPoolJobStates.Running;

            base.OnRun(sender, e);
        }
		/// <summary>
		/// Override the OnRun method to capture the date and time that the job was started and to change it's state
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected override void OnRun(object sender, BackgroundThreadStartEventArgs e)
		{
			_job._dateTimeStarted = DateTime.Now;
			_job._state = BackgroundThreadPoolJobStates.Running;
 
			base.OnRun (sender, e);
		}
        /// <summary>
        /// Monitors the jobs in the job queue and delegates threads to service the waiting jobs
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnProcessJobs(object sender, BackgroundThreadStartEventArgs e)
        {
            try
            {
                // continuously monitor the job queue and thread count
                while (true)
                {
                    // lock the thread list
                    lock (_threads.SyncRoot)
                    {
                        // if we haven't reached the max thread limit
                        if (_threads.Count < _maxThreads)
                        {
                            // lock the job queue
                            lock (this.JobQueue.SyncRoot)
                            {
                                // if there are jobs waiting
                                if (this.JobQueue.Count > 0)
                                {
                                    // dequeue the next waiting job
                                    BackgroundThreadPoolJob job = this.JobQueue.Dequeue();

                                    // create a new background thread pool thread to process the job
                                    BackgroundThreadPoolThread thread = new BackgroundThreadPoolThread(job);

                                    // and finally add the thread to our list of threads
                                    _threads.Add(thread);
                                }
                            }
                        }

                        this.DestroyThreads(true /* only the finished ones */);
                    }

                    Thread.Sleep(100);
                }
            }
            catch (ThreadAbortException)
            {
                // the processing thread is aborting
            }
            catch (Exception ex)
            {
                Log.WriteLine(ex);
            }
        }
示例#4
0
 /// <summary>
 /// Provides a virtual method to override and perform custom processing on a background thread
 /// </summary>
 /// <param name="args"></param>
 protected virtual void OnRun(object sender, BackgroundThreadStartEventArgs e)
 {
     try
     {
         if (this.Run != null)
         {
             this.Run(sender, e);
         }
     }
     catch (ThreadAbortException)
     {
         // eat this exception
         // there's a high probability that it will get thrown
         // when the thread aborts, which is normal
     }
     catch (Exception ex)
     {
         Log.WriteLine(ex);
     }
 }
		/// <summary>
		/// Runs the background thread that listens for incoming connections
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void OnThreadRun(object sender, BackgroundThreadStartEventArgs e)
		{
			try
			{
				IPEndPoint ep = (IPEndPoint)e.Args[0];
            
				Debug.Assert(ep != null);
				Trace.WriteLineIf(_verbose, string.Format("Binding to the end point '{0}'.", ep.ToString()), TraceCategory);
                  
				_listeningSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
				_listeningSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1);
				_listeningSocket.Bind(ep);
            
				Trace.WriteLineIf(_verbose, string.Format("Listening for inbound connections on '{0}'.", ep.ToString()), TraceCategory);

				while(true)
				{           
					_listeningSocket.Listen(100); // pending connections queue length

					// accept the connection
					Socket socket = _listeningSocket.Accept();
      
					Trace.WriteLineIf(_verbose, string.Format("Accepted an inbound connection from '{0}'.", socket.RemoteEndPoint.ToString()), TraceCategory);

					// create a new connection for the connection
					HttpConnection connection = new HttpConnection(socket, _verbose);
					connection.RequestDispatcher = _dispatcher;
					connection.Exception += new EventHandler<ExceptionEventArgs>(this.OnConnectionException);
					connection.Opened += new EventHandler<HttpConnectionEventArgs>(this.OnConnectionOpened);
                    connection.Closed += new EventHandler<HttpConnectionEventArgs>(this.OnConnectionClosed); 
					connection.IsServerSideConnection = true;
					connection.BeginSession(true);                                       
				}  
			}
			catch(ThreadAbortException)
			{
			}
			catch(Exception ex)
			{
				this.OnException(this, new ExceptionEventArgs(ex));
			}
		}
		/// <summary>
		/// Tries to receive data from the network on a background thread
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected virtual void OnReadFromSocket(object sender, BackgroundThreadStartEventArgs threadStartArgs) 
		{
			try 
			{	               
				// create a new message reader
				_messageReader = new HttpMessageReader();
				
				while(true) 
				{
					try 
					{			
						// read a single message
						HttpMessage message = null;
							
						// lock the reader
						lock(_messageReader)
						{
							// read a message
							message = _messageReader.Read(_socket, _stopEvent);
						}

						// what type of message is this ?						
						switch(message.Type)
						{
							/*
							 * process the request
							 * */
							case HttpMessageTypes.HttpRequest:
							{	
								// process the request by dispatching it and then responding if need be
                                this.OnRequestReceived(this, new HttpRequestCancelEventArgs(new HttpRequest(message), false));								
								break;
							}
							/*
							 * process the response
							 * */
							case HttpMessageTypes.HttpResponse:
							{
								this.OnResponseReceived(this, new HttpResponseEventArgs(new HttpResponse(message)));
								break;
							}
								/*
								 * an unknown message type
								 * */
							default:
							{	
								// hopefully this will never happen!
								Debug.WriteLine(string.Format("A message of unknown type was received from '{0}'...\n{1}", message));	
								break;
							}							
						};
					}
					catch(HttpMessageReaderAbortedException)
					{
						// the reader was aborted
					}
					catch(HttpConnectionClosedByPeerException) 
					{					
						/*
						* the remote host has closed the connection
						* */
						this.Close();
						return;
					}
					
					// see if we should stop receiving
					if (_stopEvent != null)
						if (_stopEvent.WaitOne(1, false)) 
						{
							/*
							* we have recieved a signal that we should stop receiving
							* and disconnect the current connection
							* */
							if (_disconnectOnStop)
								this.Close();
							return;
						}							
				}
												
			}
			catch(ThreadAbortException) 
			{
				/*
				 * the thread is aborting
				 * */
				if (_disconnectOnStop)
					this.Close();
			}
			catch(ObjectDisposedException) 
			{
				/*
				 * the connection has closed the socket
				 * */				
				this.Close();
			}
			catch(SocketException ex) 
			{
				// if the connection is reset, or a blocking call was cancelled with a call to cancelblockingcall
				if (ex.ErrorCode == (int)SocketErrors.WSAECONNRESET ||
					ex.ErrorCode == (int)SocketErrors.WSAEINTR) 
				{
					this.Close();
					return;
				}

				// notify that this connection has encountered an exception
				this.OnException(this, new ExceptionEventArgs(ex));
			}
			catch(Exception ex) 
			{
				// notify that this connection has encountered an exception
				this.OnException(this, new ExceptionEventArgs(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)
                    {
                        Debug.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 ex)
                {
					Debug.WriteLine(ex);
                }
            }
            catch(Exception ex)
            {
                //Debug.WriteLine(ex);
                this.OnException(this, new AutoUpdateExceptionEventArgs(ex));				
            }
        }
示例#8
0
		private void OnThreadRun(object sender, BackgroundThreadStartEventArgs e)
		{
//			const int SOCKET_ERROR = -1;
			
			try
			{
				string address = (string)e.Args[0];
				int timesToPing = (int)e.Args[1];

				#region Address Resolution

				bool isHostName = false;
				IPAddress ipAddress = null;
				try
				{
					ipAddress = IPAddress.Parse(address);
				}
				catch(Exception)
				{
					try
					{
						ipAddress = Dns.GetHostEntry(address).AddressList[0];
						isHostName = true;
					}
					catch(Exception ex)
					{
						throw ex;
					}
				}

				#endregion
				
				// create the source and destination end points
				IPEndPoint sourceIPEP = new IPEndPoint(IPAddress.Any, 0);
				IPEndPoint destinationIPEP = new IPEndPoint(ipAddress, 0);

				EndPoint source = sourceIPEP;
				EndPoint destination = destinationIPEP;

				// create an icmp socket
				Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp);
				
				// create an icmp echo packet
				IcmpEchoPacket packet = new IcmpEchoPacket();

				// serialize the packet to a byte array
				byte[] bytes = IcmpPacket.GetBytes(packet);

				// create the checksum for the packet based on it's data
				packet.Checksum = IcmpPacket.CreateChecksum(bytes);
				
				// create a packet reader and writer
				IcmpPacketReader reader = new IcmpPacketReader();
				IcmpPacketWriter writer = new IcmpPacketWriter();				

				// raise the ping started event
				this.OnPingStarted(this, new PingerEventArgs(address, isHostName, ipAddress, timesToPing));

				// ping statistics
				int packetsSent = 0;
				int packetsReceived = 0;				
				int[] elapsedTimes = new int[timesToPing];

				// now ping the destination X number of times as instructed
				for(int i = 0; i < timesToPing; i++)
				{
					int start = System.Environment.TickCount;
					int end = 0;
					int elapsed = 0;

					try
					{
						// send the icmp echo request					
						int bytesSent = writer.Write(socket, packet, destination);	
						
						packetsSent++;

						// wait for a response
						IcmpPacket response;
						int bytesReceived;
						bool receivedResponse = reader.Read(socket, source, 1000 /* 1 second timeout */, out response, out bytesReceived);
						
						// calculate the end and elapsed time in milliseconds
						end = System.Environment.TickCount;
						elapsed = end - start;
						
						elapsedTimes[i] = elapsed;

						if (receivedResponse)
							packetsReceived++;

						// raise the ping result event	
						this.OnPingResult(this, new PingerResultEventArgs(address, isHostName, ipAddress, timesToPing, !receivedResponse, bytesReceived, elapsed));
					}
					catch(Exception ex)
					{
						/*
						 * this should never hit
						 * 
						 * raw sockets shouldn't pose a problem when targeting icmp 
						 * */
						this.OnException(this, new ExceptionEventArgs(ex));
					}									
				}
				
				// calculate the percentage lost
				int percentLost = (int)(((double)(packetsSent - packetsReceived) / (double)packetsSent) * 100d);
				int min = int.MaxValue;
				int max = int.MinValue;
				int average = 0;
				int total = 0;
				for(int i = 0; i < timesToPing; i++)
				{
					if (elapsedTimes[i] < min)
						min = elapsedTimes[i];

					if (elapsedTimes[i] > max)
						max = elapsedTimes[i];

					total += elapsedTimes[i];
				}

				average = (int)((double)total / (double)timesToPing);

				PingerStatisticsEventArgs ea = new PingerStatisticsEventArgs(
					address, isHostName, ipAddress, timesToPing, 
					packetsSent, 
					packetsReceived, 
					packetsSent - packetsReceived,
					percentLost, 
					min, 
					max, 
					average);

				this.OnPingStatistics(this, ea);
			}
			catch(ThreadAbortException)
			{

			}
			catch(Exception ex)
			{
				Debug.WriteLine(ex);
				this.OnException(this, new ExceptionEventArgs(ex));
			}
			finally
			{

			}
		}
		/// <summary>
		/// Monitors the jobs in the job queue and delegates threads to service the waiting jobs
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void OnProcessJobs(object sender, BackgroundThreadStartEventArgs e)
		{
			try
			{
				// continuously monitor the job queue and thread count
				while (true)
				{		
					// lock the thread list
					lock (_threads.SyncRoot)
					{
						// if we haven't reached the max thread limit
						if (_threads.Count < _maxThreads)
						{				
							// lock the job queue
							lock (this.JobQueue.SyncRoot)
							{
								// if there are jobs waiting
								if (this.JobQueue.Count > 0)
								{
									// dequeue the next waiting job
									BackgroundThreadPoolJob job = this.JobQueue.Dequeue();

									// create a new background thread pool thread to process the job
									BackgroundThreadPoolThread thread = new BackgroundThreadPoolThread(job);															

									// and finally add the thread to our list of threads
									_threads.Add(thread);
								}
							}
						}	
   					
						this.DestroyThreads(true /* only the finished ones */);
					}

					Thread.Sleep(100);
				}
			}
			catch(ThreadAbortException)
			{
				// the processing thread is aborting
			}
			catch(Exception ex)
			{
				Log.WriteLine(ex);
			}
		}
		/// <summary>
		/// The background thread's entry method
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void HandleThreadRun(object sender, BackgroundThreadStartEventArgs e)
		{
			try
			{
				// first try the Type var, if we can create one using Reflection we don't need the provider
				if (_type != null)
				{
					// make sure that the type specified inherits from Form or is directly a Form class
					TypeUtilities.AssertTypeIsSubclassOfBaseType(_type, typeof(Form));

					// create a new instance of the window using the Type specified
					_window = TypeUtilities.CreateInstanceOfType(_type, Type.EmptyTypes, new object[] {}) as Form;
				}
				else
				{
					// use the provider to create an instance of the window
					_window = _provider.CreateWindow(e.Args);
				}

				// make sure we have a window instance
				if (_window == null)
					throw new ArgumentNullException("InnerWindow", "No window instance was created.");

				// raise an event so that we can modify the window as needed before it is shown
				this.OnWindowCreated(this, new AsyncWindowManagerEventArgs(this));

				// release the start method by signalling we have created the window
				_createdEvent.Set();

				// see if an owner was specified
				IWin32Window owner = null;
				if (e.Args != null)
					if (e.Args.Length > 0)
						owner = (IWin32Window)e.Args[0];

				// show the window modally on this thread
				if (owner != null)
				{
					_window.Owner = owner as Form;
					_window.ShowDialog(owner);
				}
				else
				{
					_window.ShowDialog();
				}
			}
			catch(Exception ex)
			{
				_failed = true;
				Log.WriteLine(ex);
				this.OnException(this, new AsyncWindowManagerExceptionEventArgs(this, ex));
			}
			finally
			{
				_window = null;
				_createdEvent.Set();
			}
		}
		/// <summary>
		/// The background thread procedure that will handle scrolling the Image
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void HandleThreadRun(object sender, BackgroundThreadStartEventArgs e)
		{
			try
			{
				while (true)
				{
					if (this.DesignMode)
					{
						Thread.Sleep(500);
					}
					else
					{	
						// step forward on the offset
						_offset += _step;
						
						// reset the offset if we hit the edge
						if (_offset >= this.Width)
							_offset = 0;					
	
						// repaint
						this.Invalidate();

						// snooze a bit
						Thread.Sleep(_frameRate);																						
					}					
				}
			}
			catch(System.Threading.ThreadAbortException)
			{
				// watch out for this little guy. :P
				// some days i still feel like this is not right that an exception is thrown
				// but i suppose there's not really any better way for the framework to stop the thread
				// and give you control back
			}
			catch(Exception ex)
			{
				Log.WriteLine(ex);
			}
		}
		/// <summary>
		/// Provides a virtual method to override and perform custom processing on a background thread
		/// </summary>
		/// <param name="args"></param>
		protected virtual void OnRun(object sender, BackgroundThreadStartEventArgs e)
		{
			try
			{
				if (this.Run != null)
					this.Run(sender, e);
			}
			catch (ThreadAbortException)
			{
				// eat this exception
				// there's a high probability that it will get thrown
				// when the thread aborts, which is normal
			}
			catch (Exception ex)
			{
				Log.WriteLine(ex);
			}			
		}