Click here to Skip to main content
15,892,927 members
Articles / Desktop Programming / Windows Forms

Windows Services Made Simple

Rate me:
Please Sign up or sign in to vote.
4.62/5 (10 votes)
27 Jun 2007CPOL10 min read 94.5K   6.9K   69  
Describes how to build a Windows Service using the Pegasus Library.
using System;
using System.Collections.Generic;
using System.Threading;

using Pegasus.Diagnostics;
using Pegasus.Threading;

namespace Pegasus.Workflow.Service.DefaultServices
{
	/// <summary>
	/// Implements the message service using the Pegasus AppThreadPool class.  
	/// Each message is dispatched to a worker thread.
	/// </summary>
	public class ThreadedMessageService : WorkflowMessageService, IDispatchTimerTask
	{
		// Local Instance Values
		private Dictionary<int, TimerTask> m_taskTable = new Dictionary<int, TimerTask>();
		private AppThreadPool m_threadPool;

		/// <summary>
		/// Initializes a new instance of the <see cref="T:ThreadedMessageService"/> class.
		/// </summary>
		public ThreadedMessageService()
		{
			m_threadPool = new AppThreadPool();
			m_threadPool.UnhandledException += new AppThreadPool.UnhandledExceptionHandler( ThreadPool_UnhandledException );
		}

		/// <summary>
		/// Gets the underlying thread pool.
		/// </summary>
		/// <value>The thread pool.</value>
		public AppThreadPool ThreadPool
		{
			get
			{
				return m_threadPool;
			}
		}

		/// <summary>
		/// Starts this instance of the service.
		/// </summary>
		/// <param name="workflowService"></param>
		public override void Start( WorkflowService workflowService )
		{
			base.Start( workflowService );
		}

		/// <summary>
		/// Stops this instance of the service.
		/// </summary>
		public override void Stop()
		{
			base.Stop();

			// Stop all pending tasks
			foreach( TimerTask task in m_taskTable.Values )
			{
				AppScheduler2.AppScheduler.UnscheduleTask( task );
			}
			
			m_taskTable.Clear();
			m_threadPool.JoinWorkers();
		}

		/// <summary>
		/// Dispatches the timer event that was fired from the timer task.
		/// </summary>
		/// <param name="task">The task.</param>
		/// <param name="parameters">The parameters.</param>
		void IDispatchTimerTask.DispatchTimerTask( TimerTask task, WorkflowEventParameters parameters )
		{
			// Check Parameters
			ParamCode.AssertNotNull( task, "task" );
			ParamCode.AssertNotNull( parameters, "parameters" );

			// Remove the task from our table.
			lock( m_taskTable )
			{
				m_taskTable.Remove( task.GetHashCode() );
			}

			// Queue the message to be executed
			m_threadPool.QueueUserWorkItem( new WaitCallback( ThreadWorkflowEventMessage ), parameters );
		}

		/// <summary>
		/// Processes the workflow start message.
		/// </summary>
		/// <param name="workflowId">The workflow id.</param>
		/// <returns>
		/// True if the message was process, False if the message should be recycled and processed later.
		/// </returns>
		protected override bool ProcessWorkflowStartMessage( int workflowId )
		{
			// Check Parameters
			ParamCode.AssertRange( workflowId, 1, int.MaxValue, "workflowId" );

			m_threadPool.QueueUserWorkItem( new WaitCallback( ThreadStartMessage ), workflowId );
			return true;
		}

		/// <summary>
		/// Processes the workflow start message.
		/// </summary>
		/// <param name="workflowId">The workflow id.</param>
		/// <param name="startState">The start state.</param>
		/// <returns>
		/// True if the message was process, False if the message should be recycled and processed later.
		/// </returns>
		protected override bool ProcessWorkflowRestartMessage( int workflowId, string startState )
		{
			// Check Parameters
			ParamCode.AssertRange( workflowId, 1, int.MaxValue, "workflowId" );
			ParamCode.AssertNotEmpty( startState, "startState" );

			RestartParameters parameters = new RestartParameters();
			parameters.WorkflowId = workflowId;
			parameters.StartState = startState;

			m_threadPool.QueueUserWorkItem( new WaitCallback( ThreadRestartMessage ), parameters );
			return true;
		}

		/// <summary>
		/// Processes the data exchange message.
		/// </summary>
		/// <param name="message">The message.</param>
		/// <returns>
		/// True if the message was process, False if the message should be recycled and processed later.
		/// </returns>
		protected override bool ProcessDataExchangeMessage( DataExchangeMessage message )
		{
			// Check Parameters
			ParamCode.AssertNotNull( message, "message" );

			m_threadPool.QueueUserWorkItem( new WaitCallback( ThreadDataExchangeMessage ), message );
			return true;
		}

		/// <summary>
		/// Processes the workflow event message.
		/// </summary>
		/// <param name="message">The message.</param>
		/// <param name="sender">The sender.</param>
		/// <param name="args">The <see cref="T:Pegasus.Workflow.Service2.WorkflowEventArgs"/> instance containing the event data.</param>
		/// <returns>
		/// True if the message was process, False if the message should be recycled and processed later.
		/// </returns>
		protected override bool ProcessWorkflowEventMessage( WorkflowMessageService.WorkflowEventMessage message, object sender, WorkflowEventArgs args )
		{
			// Check Parameters
			ParamCode.AssertNotNull( message, "message" );
			// sender can be null
			ParamCode.AssertNotNull( args, "args" );

			WorkflowEventParameters parameters = new WorkflowEventParameters();
			parameters.Message = message;
			parameters.Sender = sender;
			parameters.Args = args;

			m_threadPool.QueueUserWorkItem( new WaitCallback( ThreadWorkflowEventMessage ), parameters );
			return true;
		}

		/// <summary>
		/// Processes the register workflow event message.
		/// </summary>
		/// <param name="time">The time.</param>
		/// <param name="message">The message.</param>
		/// <param name="sender">The sender.</param>
		/// <param name="args">The <see cref="T:Pegasus.Workflow.Service2.WorkflowEventArgs"/> instance containing the event data.</param>
		/// <returns></returns>
		protected override int ProcessRegisterWorkflowEventMessage( DateTime time, WorkflowEventMessage message, object sender, WorkflowEventArgs args )
		{
			// Check Parameters 
			// time can be any valid date time
			ParamCode.AssertNotNull( message, "message" );
			// sender can be null
			ParamCode.AssertNotNull( args, "args" );
			
			WorkflowEventParameters parameters = new WorkflowEventParameters();
			parameters.Message = message;
			parameters.Sender = sender;
			parameters.Args = args;

			TimerTask task = new TimerTask( this, time, parameters );
			int messageId = task.GetHashCode();

			m_taskTable.Add( messageId, task );
			AppScheduler2.AppScheduler.ScheduleTask( task );

			return messageId;
		}

		/// <summary>
		/// Processes the unregister workflow event message.
		/// </summary>
		/// <param name="messageId">The message id.</param>
		protected override void ProcessReleaseWorkflowEventMessage( int messageId )
		{
			// Check Parameters
			// messageId is any value int

			if( m_taskTable.ContainsKey( messageId ) )
			{
				TimerTask task = m_taskTable[ messageId ];
				m_taskTable.Remove( messageId );

				AppScheduler2.AppScheduler.UnscheduleTask( task );
			}
		}

		/// <summary>
		/// Process unhandled exception from the thread pool.
		/// </summary>
		/// <param name="callback">The callback.</param>
		/// <param name="state">The state.</param>
		/// <param name="e">The e.</param>
		private void ThreadPool_UnhandledException( WaitCallback callback, object state, Exception e )
		{
			// Check Parameters
			// callback could be null
			// state could be null
			ParamCode.AssertNotNull( e, "e" );

			WorkflowService.FireUnhandledException( this, e );
		}

		/// <summary>
		/// ThreadProc to process the workflow start message.
		/// </summary>
		/// <param name="parameter">The parameter.</param>
		private void ThreadStartMessage( object parameter )
		{
			// Check Parameters
			ParamCode.AssertNotNull( parameter, "parameter" );
			ParamCode.Assert( parameter is int, "parameter", "Parameter is wrong type, expected WorkflowId (int), received {0}", parameter.GetType() );

			DispatchWorkflowStartMessage( (int) parameter );
		}

		/// <summary>
		/// ThreadProc to process the workflow restart message.
		/// </summary>
		/// <param name="parameter">The parameter.</param>
		private void ThreadRestartMessage( object parameter )
		{
			// Check Parameters
			ParamCode.AssertNotNull( parameter, "parameter" );
			ParamCode.Assert( parameter is RestartParameters, "parameter", "Parameter is wrong type, expected RestartParameters, received {0}", parameter.GetType() );

			RestartParameters restartParams = (RestartParameters) parameter;

			DispatchWorkflowRestartMessage( restartParams.WorkflowId, restartParams.StartState );
		}

		/// <summary>
		/// Threads the data exchange message.
		/// </summary>
		/// <param name="parameter">The parameter.</param>
		private void ThreadDataExchangeMessage( object parameter )
		{
			// Check Parameters
			ParamCode.AssertNotNull( parameter, "parameter" );
			ParamCode.Assert( parameter is WorkflowMessageService.DataExchangeMessage, "parameter", "Parameter is wrong type, expected WorkflowMessageService.DataExchangeMessage, received {0}", parameter.GetType() );

			DispatchDataExchangeMessage( (WorkflowMessageService.DataExchangeMessage) parameter );
		}

		/// <summary>
		/// Threads the workflow event message.
		/// </summary>
		/// <param name="parameter">The parameter.</param>
		private void ThreadWorkflowEventMessage( object parameter )
		{
			// Check Parameters
			ParamCode.AssertNotNull( parameter, "parameter" );
			ParamCode.Assert( parameter is WorkflowEventParameters, "parameter", "Parameter is wrong type, expected WorkflowEventParameters, received {0}", parameter.GetType() );

			WorkflowEventParameters parameters = (WorkflowEventParameters) parameter;

			DispatchWorkflowEventMessage( parameters.Message, parameters.Sender, parameters.Args );
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions