//
// Copyright (c) 2002
// Ronald Kevin Burton
//
// Z poniższym kodem nie jest związana żadna gwarancja poprawności działania.
// Program został dołączony do książki ".NET CLR. Księga eksperta" w celu
// ilustracji koncepcji i zasad przedstawionych w tej książce. Program może być 
// używany na własne ryzyko.
//
// Przyznaje się prawo do użycia lub kopiowania tego oprogramowania do dowolnego celu
// bez konieczności ponoszenia żadnych opłat pod warunkiem, że powyższe uwagi zostaną 
// zachowane we wszystkich kopiach. Przyznaje się także prawo do modyfikacji kodu
// i dystrybucji zmodyfikowanego kodu pod warunkiem zachowania powyższych uwag
// oraz dołączenia informacji mówiącej o modyfikacji kodu.
//
// 
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Threading;

namespace ThreadAbort
{
	/// <summary>
	/// Podsumowanie dla Form1.
	/// </summary>
	public class ThreadAbortForm : System.Windows.Forms.Form
	{
		enum MessageType
		{
			Text,
			Error
		}

		private System.Windows.Forms.Button startThreadButton;
		private System.Windows.Forms.Button abortThreadButton;
		private System.Windows.Forms.Button finishThreadButton;
		private ManualResetEvent StopEvent;
		private Thread thread;
		private delegate void MessageDelegate(string message, MessageType type);
		private MessageDelegate messageDelegate;
		private System.Windows.Forms.RichTextBox messages;
		private System.Windows.Forms.Button interruptThreadButton;
		private System.Windows.Forms.Button resumeThreadButton;
		private System.Windows.Forms.Button suspendThreadButton;
		/// <summary>
		/// Wymagana zmienna.
		/// </summary>
		private System.ComponentModel.Container components = null;

		public ThreadAbortForm()
		{
			//
			// Wymagane do obsługi Windows Form Designer
			//
			InitializeComponent();

			startThreadButton.Enabled = true;
			abortThreadButton.Enabled = false;
			finishThreadButton.Enabled = false;
			interruptThreadButton.Enabled = false;
			suspendThreadButton.Enabled = false;
			resumeThreadButton.Enabled = false;
			StopEvent = new ManualResetEvent(false);
			thread = new Thread(new ThreadStart(ThreadEntry));
			messageDelegate = new MessageDelegate(DisplayMessage);
		}

		/// <summary>
		/// Oczyszczenie używanych zasobów.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if (components != null) 
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		#region Windows Form Designer generated code
		/// <summary>
		/// Metoda wymagana do obsługi Designera - nie należy modyfikować
		/// zawartości tej metody przy użyciu edytora kodu.
		/// </summary>
		private void InitializeComponent()
		{
			this.suspendThreadButton = new System.Windows.Forms.Button();
			this.finishThreadButton = new System.Windows.Forms.Button();
			this.interruptThreadButton = new System.Windows.Forms.Button();
			this.abortThreadButton = new System.Windows.Forms.Button();
			this.messages = new System.Windows.Forms.RichTextBox();
			this.resumeThreadButton = new System.Windows.Forms.Button();
			this.startThreadButton = new System.Windows.Forms.Button();
			this.SuspendLayout();
			// 
			// suspendThreadButton
			// 
			this.suspendThreadButton.Enabled = false;
			this.suspendThreadButton.Location = new System.Drawing.Point(224, 16);
			this.suspendThreadButton.Name = "suspendThreadButton";
			this.suspendThreadButton.Size = new System.Drawing.Size(88, 23);
			this.suspendThreadButton.TabIndex = 1;
			this.suspendThreadButton.Text = "Zawieś";
			this.suspendThreadButton.Click += new System.EventHandler(this.OnSuspend);
			// 
			// finishThreadButton
			// 
			this.finishThreadButton.Enabled = false;
			this.finishThreadButton.Location = new System.Drawing.Point(528, 16);
			this.finishThreadButton.Name = "finishThreadButton";
			this.finishThreadButton.TabIndex = 4;
			this.finishThreadButton.Text = "Zakończ";
			this.finishThreadButton.Click += new System.EventHandler(this.OnFinish);
			// 
			// interruptThreadButton
			// 
			this.interruptThreadButton.Enabled = false;
			this.interruptThreadButton.Location = new System.Drawing.Point(120, 16);
			this.interruptThreadButton.Name = "interruptThreadButton";
			this.interruptThreadButton.Size = new System.Drawing.Size(88, 23);
			this.interruptThreadButton.TabIndex = 1;
			this.interruptThreadButton.Text = "Przerwij";
			this.interruptThreadButton.Click += new System.EventHandler(this.OnInterrupt);
			// 
			// abortThreadButton
			// 
			this.abortThreadButton.Enabled = false;
			this.abortThreadButton.Location = new System.Drawing.Point(432, 16);
			this.abortThreadButton.Name = "abortThreadButton";
			this.abortThreadButton.Size = new System.Drawing.Size(88, 23);
			this.abortThreadButton.TabIndex = 1;
			this.abortThreadButton.Text = "Zakończ";
			this.abortThreadButton.Click += new System.EventHandler(this.OnAbort);
			// 
			// messages
			// 
			this.messages.Location = new System.Drawing.Point(16, 56);
			this.messages.Name = "messages";
			this.messages.ReadOnly = true;
			this.messages.Size = new System.Drawing.Size(600, 312);
			this.messages.TabIndex = 3;
			this.messages.Text = "";
			this.messages.WordWrap = false;
			// 
			// resumeThreadButton
			// 
			this.resumeThreadButton.Enabled = false;
			this.resumeThreadButton.Location = new System.Drawing.Point(328, 16);
			this.resumeThreadButton.Name = "resumeThreadButton";
			this.resumeThreadButton.Size = new System.Drawing.Size(88, 23);
			this.resumeThreadButton.TabIndex = 1;
			this.resumeThreadButton.Text = "Wznów";
			this.resumeThreadButton.Click += new System.EventHandler(this.OnResume);
			// 
			// startThreadButton
			// 
			this.startThreadButton.Location = new System.Drawing.Point(16, 16);
			this.startThreadButton.Name = "startThreadButton";
			this.startThreadButton.Size = new System.Drawing.Size(88, 23);
			this.startThreadButton.TabIndex = 0;
			this.startThreadButton.Text = "Start";
			this.startThreadButton.Click += new System.EventHandler(this.OnStart);
			// 
			// ThreadAbortForm
			// 
			this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
			this.ClientSize = new System.Drawing.Size(648, 381);
			this.Controls.AddRange(new System.Windows.Forms.Control[] {
																		  this.suspendThreadButton,
																		  this.resumeThreadButton,
																		  this.interruptThreadButton,
																		  this.messages,
																		  this.finishThreadButton,
																		  this.abortThreadButton,
																		  this.startThreadButton});
			this.Name = "ThreadAbortForm";
			this.Text = "Test przerywania wątków";
			this.Closing += new System.ComponentModel.CancelEventHandler(this.OnClose);
			this.ResumeLayout(false);

		}
		#endregion

		/// <summary>
		/// Główny punkt wejścia dla aplikacji.
		/// </summary>
		[STAThread]
		static void Main() 
		{
			Application.Run(new ThreadAbortForm());
		}

		private void ThreadEntry()
		{
			bool finish = false;
			start:
			try
			{
				string msg = "Uruchomienie wątku";
				BeginInvoke(messageDelegate, new object[] {msg, MessageType.Text});
				finish = StopEvent.WaitOne();
			}
			catch(ThreadAbortException e)
			{
				string msg = "Wystąpił wyjątek ThreadAbortException\r\n" + e.ToString();
				BeginInvoke(messageDelegate, new object[] {msg, MessageType.Error});
				// Thread.ResetAbort();
				// Proszę zauważyć, że zostało to zignorowane.
				goto start;
			}
			catch(ThreadInterruptedException e)
			{
				string msg = "Wystąpił wyjątek ThreadInterruptedException\r\n" + e.ToString();
				BeginInvoke(messageDelegate, new object[] {msg, MessageType.Error});
				// Oczywiście polecenie GOTO jest uważane za niebezpieczne (zobacz na przykład
				// http://www.net.org/html/history/detail/1968-goto.html), ale nie było
				// tu innego wyjścia.
				goto start;
			}
			finally
			{
				if(!finish)
				{
					string msg = "Usuwanie wątku w wyniku przerwania pracy.";
					BeginInvoke(messageDelegate, new object[] {msg, MessageType.Text});
				}
			}
		}

		private void DisplayMessage(string message, MessageType type)
		{
			if(type == MessageType.Error)
			{
				int startPosition = messages.SelectionStart;
				messages.AppendText(message);
				int endPosition = messages.SelectionStart;
				messages.AppendText("\r\n");
				messages.SelectionStart = startPosition;
				messages.SelectionLength = endPosition - startPosition + 1;
				messages.SelectionColor = Color.Red;
				messages.SelectionLength = 0;
			}
			else
			{
				messages.AppendText(message + "\r\n");
			}
		}

		private void OnStart(object sender, System.EventArgs e)
		{
			try
			{
				DisplayMessage("Rozpoczęcie od stanu wątku " + thread.ThreadState.ToString(), MessageType.Text);
				StopEvent.Reset();
				thread.Start();

				// startThreadButton.Enabled = false;
				abortThreadButton.Enabled = true;
				finishThreadButton.Enabled = true;
				interruptThreadButton.Enabled = true;
				suspendThreadButton.Enabled = true;
				resumeThreadButton.Enabled = true;
				startThreadButton.Refresh();
				abortThreadButton.Refresh();
				finishThreadButton.Refresh();
				interruptThreadButton.Refresh();
				suspendThreadButton.Refresh();
				resumeThreadButton.Refresh();
			}
			catch (Exception exception)
			{
				DisplayMessage(exception.ToString(), MessageType.Error);
			}
		}

		private void OnAbort(object sender, System.EventArgs e)
		{
			startThreadButton.Enabled = true;
			abortThreadButton.Enabled = false;
			finishThreadButton.Enabled = false;
			interruptThreadButton.Enabled = false;
			suspendThreadButton.Enabled = false;
			resumeThreadButton.Enabled = false;
			startThreadButton.Refresh();
			abortThreadButton.Refresh();
			finishThreadButton.Refresh();
			interruptThreadButton.Refresh();
			suspendThreadButton.Refresh();
			resumeThreadButton.Refresh();

			DisplayMessage("Przerwanie pracy przy stanie wątku " + thread.ThreadState.ToString(), MessageType.Text);
			thread.Abort();
			thread.Join(2000);
			DisplayMessage("Koniec", MessageType.Text);
			thread = new Thread(new ThreadStart(ThreadEntry));
		}

		private void OnFinish(object sender, System.EventArgs e)
		{
			startThreadButton.Enabled = true;
			abortThreadButton.Enabled = false;
			finishThreadButton.Enabled = false;
			interruptThreadButton.Enabled = false;
			suspendThreadButton.Enabled = false;
			resumeThreadButton.Enabled = false;
			startThreadButton.Refresh();
			abortThreadButton.Refresh();
			finishThreadButton.Refresh();
			interruptThreadButton.Refresh();
			suspendThreadButton.Refresh();
			resumeThreadButton.Refresh();

			DisplayMessage("Kończenie pracy przy stanie wątku " + thread.ThreadState.ToString(), MessageType.Text);
			StopEvent.Set();
			thread.Join();
			DisplayMessage("Koniec", MessageType.Text);
			thread = new Thread(new ThreadStart(ThreadEntry));
		}

		private void OnInterrupt(object sender, System.EventArgs e)
		{
			DisplayMessage("Wystąpienie przerwania przy stanie wątku " + thread.ThreadState.ToString(), MessageType.Text);
			thread.Interrupt();
		}

		private void OnSuspend(object sender, System.EventArgs e)
		{
			DisplayMessage("Zawieszenie przy stanie wątku " + thread.ThreadState.ToString(), MessageType.Text);
			thread.Suspend();
			resumeThreadButton.Enabled = true;
			abortThreadButton.Enabled = false;
			finishThreadButton.Enabled = false;
			resumeThreadButton.Refresh();
			abortThreadButton.Refresh();
			finishThreadButton.Refresh();
		}

		private void OnResume(object sender, System.EventArgs e)
		{
			DisplayMessage("Wznowienie przy stanie wątku " + thread.ThreadState.ToString(), MessageType.Text);
			try
			{
				thread.Resume();

				abortThreadButton.Enabled = true;
				finishThreadButton.Enabled = true;
				abortThreadButton.Refresh();
				finishThreadButton.Refresh();
			}
			catch(Exception exception)
			{
				DisplayMessage(exception.ToString(), MessageType.Error);
			}
		}

		private void OnClose(object sender, System.ComponentModel.CancelEventArgs e)
		{
			try
			{
				StopEvent.Set();
				thread.Join();
			}
			catch(Exception exception)
			{
				DisplayMessage(exception.ToString(), MessageType.Error);
			}
		}
	}
}
