Introduction
Over the past years, I've learned many things from CodeProject and now I want to give something to CodeProject. In many Winform applications, we might have to wait for a user to click a button or press any key, then we do some background functionality based on this event. But for server software, we might have to wait for some incoming request and respond according with the request which usually means event. That is, there are two things that are involved; one is sending the event and another one is receiving the event. This means event sender activates the events whereas the receiver can handle it. But to handle the events means to execute some sort of methods which is usually done by the event handler. As the event sender does not know which method will handle the event, you need the third party who could point the event handler. This is called delegate which acts as a reference of the handler. I am using here both the event and delegate.
However, in order to see the sample code for the article, you have to:
- Download the sample ZIP file
- Browse to it and unzip the contents somewhere
- Go to Visual Studio and open the project
Background
When I was assigned to develop server software for the first time, I was surprised and spent my enough valuable time to make a decision which types of application should I start, should it be a console application or Winforms application. That time, I didn’t have much idea about multi-threading and other related issues. So I decided to start it in Winform application and finished it in due time perfectly. When it is deployed to the testing server, I am afraid to say that just after 30 min of its running, it gets freezed up. I am shocked and then tried to find out the problems and, solutions according that. This is what makes me show you how to develop a simple server application. This time I have developed it only for copying file from one directory to other directory. But by using this idea, you can develop any server software. This is only for beginners who wants to start developing this type of software. Yes of course by using the latest technology, we could develop a high level of server software but I am not going in this side so far but will be in the near future.
How It Works
We, the developers, always try to look into the performance when we develop an application. Is there any developer who wants his application to freeze or smash? But what are the steps a developer should yield to keep it all smooth going. When I used to develop this application, I saw whenever I attempt to copy files from one directory to other it includes CPU as well as hard drive. The major fallback of it is to limit a user to execute one task at a time. So how do we deal with application freezing and smashing?
By using threads through which we can handle all the jobs simultaneously under one main application thread. You can perform smaller operations with the help of threading class; instead I prefer using Background Worker class to implement to perform all my heavy duty operations. Background Worker is used to execute long running operations on a separate thread, support cancellation, progress reporting and marshalling results back to the UI thread. As I have offered the smooth UI which shows the operation details and at the same time the operation will continue in the background, I choose this one to use. If you want to know in details about the Background Worker, you can read this article.
The main mechanism is that when the user clicks the start button, it issues the event OnCopyStarted
and the event handler is responsible for Copying data while at the same time OnUIChanged
was getting issued and do some enable/disable UI Controls, when we click stop button, it issued the OnCopyStoppedEvent
and at the same time it checks the Background worker’s working status does Background worker finish/Cancel its task or not. If not, it pops up the dialog window for a moment and waits for Background worker to be free, once the Backgroundworker
gets free, the Dialogue will automatically dispose. Here I have used one way binding. So any changes in property value will reflect to the UI but changing the UI will not reflect on the properties.
Using the Code
First, please have a look at the Entities that I have used here:
namespace FileSystem.Replication.Server.Entities
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
public class MyFileSystemEntities
{
public string CopyTo{get;set;}
public string CopyFrom { get; set; }
public string Messages { get; set; }
}
}
In this above code, the CopyTo
holds the full path of directory to where we want to copy while CopyFrom
holds the full path of directory from where we want to copy. The message
holds the custom message or any error message to display in the window.
namespace FileSystem.Replication.Server.Entities
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
public class UIEntities
{
public UIEntities()
{
}
public bool EnableTxtCopyFrom { get; set; }
public bool EnableTxtCopyTo { get; set; }
public bool EnableBtnStart { get; set; }
public bool EnableBtnStop { get; set; }
public bool EnableBtnClose { get; set; }
public bool EnableBtnCopyFrom { get; set; }
public bool EnableBtnCopyTo { get; set; }
}
}
The properties of the above class directly bind to the fields of the FrmMain.cs. As an example EnableTxtCopyFrom is bind to the TxtCopyform text box. This is something like this if the value of EnableTxtCopyFrom is true then the text box TxtCopyForm will be enabled and also will be disabled if it false. That is, it shows one way binding. And the property value has been changed when an event getting executed which needs to reflect to the UI.
Second, you also need to have a look on the custom event arguments that i have used here
namespace FileSystem.Replication.Server.MyEventArguments
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FileSystem.Replication.Server.Entities;
public class MyFileSystemEventArgs : EventArgs
{
public MyFileSystemEventArgs(MyFileSystemEntities submittedEntities)
: base()
{
this.MyCurrentFileSystemEntity = submittedEntities;
}
public MyFileSystemEntities MyCurrentFileSystemEntity { get; set; }
}
}
The MyFileSystemEventArgs
contains properties which is MyCurrentFileSystemEntity
. MyCurrentFileSystemEntity
is of MyFileSystemEntities
that is; it contains all the properties of MyFileSystemEntities
. I will show you where I have used this custom event args.
namespace FileSystem.Replication.Server.MyEventArguments
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FileSystem.Replication.Server.Entities;
public class MyUIEventArgs : EventArgs
{
public MyUIEventArgs(UIEntities submittedEntities)
: base()
{
this.CurrentUIEntities = submittedEntities;
}
public UIEntities CurrentUIEntities { get; set; }
}
}
Like MyFileSystemEventArgs
, MyUIEventArgs
also has an extra property CurrentUIEntities
of UIEntities
. I have already discussed about the MyFileSystemEventArgs
and UIEntities
.
Now come to the Delegate portion:
namespace FileSystem.Replication.Server.Delegates
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FileSystem.Replication.Server.MyEventArguments;
public class MyDelegates
{
public delegate void CopyStartedEventHandler
(object sender, MyFileSystemEventArgs e);
public delegate void BackgroundWorkerCheckedEventHandler
(object sender, EventArgs e);
public delegate void UIChangedEventHandler(object sender, MyUIEventArgs e);
public delegate void CopyStoppedEventHandler(object sender, EventArgs e);
}
}
Okay, so the first thing I have done in this class is to declare delegates. There are four delegates which are used for four specific tasks. For example:
public delegate void CopyStartedEventHandler(object sender, MyFileSystemEventArgs e);
The delegate defines the parameters sent to the event handlers. Thus any class that wants to handle this event must have a handler method which has the same return type and argument list as this delegate. Here as you can see, the first parameter is an object and the second parameter is my custom event arguments MyFileSystemEventArgs
which has been discussed earlier. As its name is CopyStartedEventHandler
, we could say that this delegate will use to make the reference of the method whose task is to perform Copy Operation.
Next come to the interface section:
namespace FileSystem.Replication.Server.Interfaces
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using FileSystem.Replication.Server.Entities;
public interface IController
{
BackgroundWorker BackgroundWorker { get; set; }
void DoCopy(MyFileSystemEntities arguments, DoWorkEventArgs e);
}
}
This IController
interface defines the methods and properties of Controller
Class. What the controller
class does will be explained later on, especially when I will discuss about the controller
class. Here, Background Worker is used to do the tasks simultaneously whereas DoCopy
is used for Copy Operation.
namespace FileSystem.Replication.Server.Interfaces
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FileSystem.Replication.Server.MyEventArguments;
public interface IEvent
{
event FileSystem.Replication.Server.Delegates.
MyDelegates.CopyStartedEventHandler CopyStarted;
event FileSystem.Replication.Server.Delegates.
MyDelegates.CopyStoppedEventHandler CopyStopped;
event FileSystem.Replication.Server.Delegates.
MyDelegates.UIChangedEventHandler UIChanged;
event FileSystem.Replication.Server.Delegates.
MyDelegates.BackgroundWorkerCheckedEventHandler BackgroundWorkerChecked;
void OnBackgroundWorkerChecked(EventArgs e);
void OnUIChanged(MyUIEventArgs e);
void OnFileCopyStopped(EventArgs e);
void OnCopyStarted(MyFileSystemEventArgs e);
}
}
This IEvent
interface contains the signature of all custom events that are used in the project. You might split up it in two more interfaces depending on the modules. It also contains the signature of the methods that will be used to invoke the events. Here CopyStarted
and CopyStopped
is used to start and stop the Copy Operation while UIChanged
is used to change the UI. OnCopyStarted
is responsible for notifying receivers that the event CopyStarted
occurred. In the same way, OnFileCopyStoped
and OnUIChanged
is responsible for notifying receivers that the event CopyStoped
and UIChanged
occurred.
namespace FileSystem.Replication.Server.Interfaces
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
public interface ILogger
{
bool IsBackgroundWorkerFree { get; set; }
WaitingDialogue WaitingDialog { get; set; }
void Log(string submittedMsg);
void LogM(string submttedMethodName,string submittedMsg);
}
}
This ILogger
interface defines the methods and properties which are used for logging purposes. Here Log
and LogM
has been used to log the current progress, IsBackgroundWorkerFree
has been used to check the current Background Worker status, is it free or not. This is especially needed if we are on the stage of copying a large file. Say, the BackgroundWorker
is busy now to copy the file and have also been done half of its work, at this time if we want to stop the copying file the BackgroundWorker
needs few times to cancel its operation. In this time period, I just display a busy dialog which usually guides you to wait for some moment. For doing this, I have used here a modal dialog which is WaitingDialog
.
namespace FileSystem.Replication.Server.Interfaces
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using FileSystem.Replication.Server.Entities;
public interface IMasterServices
{
bool CopyFile(MyFileSystemEntities submittedEntities);
bool CopyDirectory(MyFileSystemEntities submittedEntities, DoWorkEventArgs e);
}
}
The IMasterServices
which has defined the signature of MasterServices.CopyFile
is used to copy file file whereas CopyDirectory
is used to Copy directory from one place to another.
Now Come to the Implementation Stage
namespace FileSystem.Replication.Server.Implementation
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FileSystem.Replication.Server.Entities;
using FileSystem.Replication.Server.Interfaces;
using FileSystem.Replication.Server.MyEventArguments;
using MyServer = FileSystem.Replication.Server.Delegates.MyDelegates;
public class MyEventHandler : IEvent
{
#region Variables
private static object syncLockCopyStarted = new object();
private static object syncLockUIChanged = new object();
private static object syncLockCopyStopped = new object();
private static object syncLockBackgroundWorkerChecked = new object();
private MyServer.CopyStartedEventHandler copyStarted;
private MyServer.CopyStoppedEventHandler copyStopped;
private MyServer.BackgroundWorkerCheckedEventHandler backgroundWorkerChecked;
private MyServer.UIChangedEventHandler uiChanged;
#endregion
#region COnstructor
public MyEventHandler()
{
}
#endregion
#region Events
public event MyServer.CopyStartedEventHandler CopyStarted
{
add
{
lock (syncLockCopyStarted)
{
this.copyStarted += value;
}
}
remove
{
lock (syncLockCopyStarted)
{
this.copyStarted -= value;
}
}
}
public event MyServer.CopyStoppedEventHandler CopyStopped
{
add
{
lock (syncLockCopyStopped)
{
this.copyStopped += value;
}
}
remove
{
lock (syncLockCopyStopped)
{
this.copyStopped -= value;
}
}
}
public event MyServer.UIChangedEventHandler UIChanged
{
add
{
lock (syncLockUIChanged)
{
this.uiChanged += value;
}
}
remove
{
lock (syncLockUIChanged)
{
this.uiChanged -= value;
}
}
}
public event MyServer.BackgroundWorkerCheckedEventHandler BackgroundWorkerChecked
{
add
{
lock (syncLockBackgroundWorkerChecked)
{
this.backgroundWorkerChecked += value;
}
}
remove
{
lock (syncLockBackgroundWorkerChecked)
{
this.backgroundWorkerChecked -= value;
}
}
}
#endregion
#region OnCopyStarted
public void OnCopyStarted(MyFileSystemEventArgs e)
{
MyServer.CopyStartedEventHandler handler;
lock (syncLockCopyStarted)
{
handler = this.copyStarted;
}
if (handler != null)
{
handler(this, e);
}
}
#endregion
#region OnFileCopyStopped
public void OnFileCopyStopped(EventArgs e)
{
MyServer.CopyStoppedEventHandler handler;
lock (syncLockCopyStopped)
{
handler = this.copyStopped;
}
if (handler != null)
{
handler(this, e);
}
}
#endregion
#region OnUIChanged
public void OnUIChanged(MyUIEventArgs e)
{
MyServer.UIChangedEventHandler handler;
lock (syncLockUIChanged)
{
handler = this.uiChanged;
}
if (handler != null)
{
handler(this, e);
}
}
#endregion
#region OnBackgroundWorkerChecked
public void OnBackgroundWorkerChecked(EventArgs e)
{
MyServer.BackgroundWorkerCheckedEventHandler handler;
lock (syncLockBackgroundWorkerChecked)
{
handler = this.backgroundWorkerChecked;
}
if (handler != null)
{
handler(this, e);
}
}
#endregion
}
}
The MyEventHandler
implements IEvent
that contain the Events
implementation and the method implementation to invoke those events. I need to explain something here:
public event MyServer.CopyStartedEventHandler CopyStarted
{
add
{
lock (syncLockCopyStarted)
{
this.copyStarted += value;
}
}
remove
{
lock (syncLockCopyStarted)
{
this.copyStarted -= value;
}
}
}
This above code shows that when the connector is about to execute a job, you can catch this event to perform monitoring work.
public void OnCopyStarted(MyFileSystemEventArgs e)
{
MyServer.CopyStartedEventHandler handler;
lock (syncLockCopyStarted)
{
handler = this.copyStarted;
}
if (handler != null)
{
handler(this, e);
}
}
The above code shows the method that is responsible for notifying receivers that the event occurred.
namespace FileSystem.Replication.Server.Implementation
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using FileSystem.Replication.Server.Entities;
using FileSystem.Replication.Server.Implementation;
using FileSystem.Replication.Server.Interfaces;
using FileSystem.Replication.Server.MyEventArguments;
public class Controller : IController
{
#region Variables
private ILogger log = null;
private IMasterServices services = null;
private IEvent customEvent = null;
#endregion
#region Constructor
public Controller(ILogger submittedLog, IEvent submittedCustomEvent)
{
try
{
this.log = submittedLog;
this.customEvent = submittedCustomEvent;
this.customEvent.CopyStarted += new FileSystem.Replication.Server.
Delegates.MyDelegates.CopyStartedEventHandler
(this.Controller_FileCopyStarted);
this.customEvent.CopyStopped += new FileSystem.Replication.Server.
Delegates.MyDelegates.CopyStoppedEventHandler
(this.Controller_FileCopyStopped);
this.customEvent.BackgroundWorkerChecked +=
new FileSystem.Replication.Server.Delegates.MyDelegates.
BackgroundWorkerCheckedEventHandler
(this.Controller_BackgroundWorkerChecked);
this.BackgroundWorker = new System.ComponentModel.BackgroundWorker();
this.BackgroundWorker.WorkerSupportsCancellation = true;
this.BackgroundWorker.DoWork +=
new System.ComponentModel.DoWorkEventHandler
(this.BackgroundWorker_DoWork);
this.BackgroundWorker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler
(this.BackgroundWorker_RunWorkerCompleted);
this.services = new MasterServices(this.log, this.BackgroundWorker);
}
catch (Exception oEx)
{
this.log.LogM("Controller", oEx.ToString());
}
}
#endregion
#region properties
public BackgroundWorker BackgroundWorker { get; set; }
#endregion
#region Controller_BackgroundWorkerChecked
private void Controller_BackgroundWorkerChecked(object sender, EventArgs e)
{
if (this.log.IsBackgroundWorkerFree == true)
{
this.log.WaitingDialog.Dispose();
}
}
#endregion
#region BackgroundWorker_DoWork
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
try
{
System.ComponentModel.BackgroundWorker bw =
sender as System.ComponentModel.BackgroundWorker;
UIEntities ui = new UIEntities()
{
EnableBtnClose = true,
EnableBtnCopyFrom = false,
EnableBtnCopyTo = false,
EnableBtnStop = true,
EnableBtnStart = false,
EnableTxtCopyFrom = false,
EnableTxtCopyTo = false
};
MyUIEventArgs uiArgs = new MyUIEventArgs(ui);
this.customEvent.OnUIChanged(uiArgs);
MyFileSystemEntities arguments = e.Argument as MyFileSystemEntities;
this.log.Log("Start Copying \n");
this.DoCopy(arguments, e);
}
catch (Exception oEx)
{
this.log.LogM("BackgroundWorker_DoWork", oEx.ToString());
}
}
#endregion
#region DoCopy
public void DoCopy(MyFileSystemEntities arguments, DoWorkEventArgs e)
{
if (arguments == null)
{
throw new ArgumentNullException();
}
else
{
if (!string.IsNullOrEmpty(arguments.CopyFrom))
{
if (Directory.Exists(arguments.CopyFrom))
{
this.services.CopyDirectory(arguments, e);
}
else if (File.Exists(arguments.CopyFrom))
{
this.services.CopyFile(arguments);
}
}
}
}
#endregion
#region BackgroundWorker_RunWorkerCompleted
private void BackgroundWorker_RunWorkerCompleted
(object sender, RunWorkerCompletedEventArgs e)
{
try
{
if (e.Cancelled)
{
UIEntities ui = new UIEntities()
{
EnableBtnClose = true,
EnableBtnCopyFrom = true,
EnableBtnCopyTo = true,
EnableBtnStop = false,
EnableBtnStart = true,
EnableTxtCopyFrom = true,
EnableTxtCopyTo = true
};
MyUIEventArgs uiArgs = new MyUIEventArgs(ui);
this.customEvent.OnUIChanged(uiArgs);
this.log.Log("User cancels the Operation \n");
this.log.IsBackgroundWorkerFree = true;
}
else
{
UIEntities ui = new UIEntities()
{
EnableBtnClose = true,
EnableBtnCopyFrom = true,
EnableBtnCopyTo = true,
EnableBtnStop = false,
EnableBtnStart = true,
EnableTxtCopyFrom = true,
EnableTxtCopyTo = true
};
MyUIEventArgs uiArgs = new MyUIEventArgs(ui);
this.customEvent.OnUIChanged(uiArgs);
this.log.Log("Opearation completed \n");
this.log.IsBackgroundWorkerFree = true;
}
}
catch (Exception oEx)
{
this.log.LogM("backgroundWorker_RunWorkerCompleted", oEx.ToString());
}
}
#endregion
#region Controller_FileCopyStopped
private void Controller_FileCopyStopped(object sender, EventArgs e)
{
try
{
this.BackgroundWorker.CancelAsync();
}
catch (Exception oEx)
{
this.log.LogM("Controller_FileCopyStopped", oEx.ToString());
}
}
#endregion
#region Controller_FileCopyStarted
private void Controller_FileCopyStarted(object sender, MyFileSystemEventArgs e)
{
try
{
MyFileSystemEntities entities = e.MyCurrentFileSystemEntity;
if (entities != null && !string.IsNullOrEmpty(entities.CopyFrom) &&
!string.IsNullOrEmpty(entities.CopyTo))
{
this.BackgroundWorker.RunWorkerAsync(entities);
}
}
catch (Exception oEx)
{
this.log.LogM("Controller_FileCopyStarted", oEx.ToString());
}
}
#endregion
}
}
The Controller
class implements the IController
interface and when any request comes from the front end, it will decide back ground working process. As an example, when file copy event is fired the Controller
class handles it, call the Background Worker RunWorkerAsync()
which usually means that to start execution of background worker copy operation. If any request comes to stop copy operation, the controller call Background
Worker CancelAsync()
which usually means submitting a request to terminate the pending background operation. If it successfully completes its task, then it fires the BackgroundWorker RunWorkerCompleted
and RunWorkerCompletedEventArgs.Cancelled
will be false and then the background worker will be free. If it cancels its task like as if we Click the stop button to stop the copy operation, it also fires the Background Worker RunWorkerCompleted
but in this case RunWorkerCompletedEventArgs.Cancelled
should be true
and the background worker getting free. When it succeeds or cancels its operation that is, when it reaches the Completed
state, it executes the UIChanged
Event to change the UI according to this.
namespace FileSystem.Replication.Server.Implementation
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using FileSystem.Replication.Server.Entities;
using FileSystem.Replication.Server.Interfaces;
public class MasterServices : IMasterServices
{
#region Variables
private ILogger log = null;
private BackgroundWorker backgroundWorker = null;
#endregion
#region Constructor
public MasterServices(ILogger submittedLog,
BackgroundWorker submittedBackgroundWorker)
{
this.log = submittedLog;
this.backgroundWorker = submittedBackgroundWorker;
}
#endregion
#region IMasterServices Members
public bool CopyFile(MyFileSystemEntities submittedEntities)
{
if (submittedEntities == null)
{
throw new ArgumentNullException();
}
if (string.IsNullOrEmpty(submittedEntities.CopyFrom))
{
throw new ArgumentNullException();
}
else if (string.IsNullOrEmpty(submittedEntities.CopyTo))
{
throw new ArgumentNullException();
}
else
{
try
{
if (File.Exists(submittedEntities.CopyFrom))
{
this.log.Log("Coping file : \n\n Source :" +
submittedEntities.CopyFrom + "\n Destination :" +
submittedEntities.CopyTo + "\n");
File.Copy(submittedEntities.CopyFrom,
submittedEntities.CopyTo, true);
return true;
}
return false;
}
catch (Exception oEx)
{
this.log.LogM("CopyFile", oEx.ToString());
return false;
}
}
}
#endregion
#region CopyDirectory
public bool CopyDirectory
(MyFileSystemEntities submittedEntities, DoWorkEventArgs e)
{
if (submittedEntities == null)
{
throw new ArgumentNullException();
}
else if (string.IsNullOrEmpty(submittedEntities.CopyFrom))
{
throw new ArgumentNullException();
}
else if (string.IsNullOrEmpty(submittedEntities.CopyTo))
{
throw new ArgumentNullException();
}
else
{
try
{
DirectoryInfo sourceDir =
new DirectoryInfo(submittedEntities.CopyFrom);
DirectoryInfo destDir = new DirectoryInfo(submittedEntities.CopyTo);
if (!destDir.Exists)
{
destDir.Create();
}
foreach (FileInfo childFile in sourceDir.GetFiles())
{
if (this.backgroundWorker != null)
{
if (this.backgroundWorker.CancellationPending == true)
{
e.Cancel = true;
break;
}
else
{
this.log.Log("Coping file : \n\n Source :" +
childFile.FullName + "\n Destination :" +
Path.Combine(destDir.FullName,
childFile.Name) + "\n");
childFile.CopyTo(Path.Combine
(destDir.FullName, childFile.Name), true);
}
}
}
foreach (DirectoryInfo subDir in sourceDir.GetDirectories())
{
if (this.backgroundWorker != null)
{
if (this.backgroundWorker.CancellationPending == true)
{
e.Cancel = true;
break;
}
else
{
if (!subDir.Exists)
{
this.log.Log("Directory Created :" +
subDir + "\n");
subDir.Create();
}
bool retVal;
retVal = this.CopyDirectory
(new MyFileSystemEntities()
{ CopyFrom = subDir.FullName, CopyTo =
Path.Combine(destDir.FullName, subDir.Name) }, e);
if (!retVal)
{
return false;
}
}
}
}
return true;
}
catch (Exception oEx)
{
this.log.LogM("CopyDirectory", oEx.ToString());
return false;
}
}
}
#endregion
}
}
MasterServices
implements IMasterService
. We have seen that in the method CopyDirectory
, there are two parameters one is of MyFileSystemEntities
another one is of DoWorkEventArgs
, this is an event which is used for background worker to start its task. As I have used here a single operation, I have maintained the background worker from the controller. If there is more than one controller for many operations, you might use different background work for different operations. You have seen that the function first does the sanity checking then it reads the full path of the source and destination directory. If the destination directory does not exist, it then creates it. In foreach
loop, it gets all the files and directories of source directories and also listens if anyone clicks the Stop button. That is if anyone clicks the stop button, the Background Worker Cancellation pending will be true and once it’s set to true, it sets DoWorkEventsArgs.Cancel
is true
which usually fires the Background Worker RunWorkerCompleted
Event means that it is interrupted to stop its working. Once Background Worker RunWorkerCompleted
gets executed, then it will be free to do another task.
namespace FileSystem.Replication.Server
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using FileSystem.Replication.Server.Entities;
using FileSystem.Replication.Server.Implementation;
using FileSystem.Replication.Server.Interfaces;
using FileSystem.Replication.Server.MyEventArguments;
public partial class FrmMain : Form, ILogger
{
#region Variables
private IController controller = null;
private bool isBackgroundWorkerFree = false;
private IEvent myEvent = null;
#endregion
public FrmMain()
{
InitializeComponent();
this.myEvent = new MyEventHandler();
this.controller = new Controller(this, this.myEvent);
this.myEvent.UIChanged +=
new FileSystem.Replication.Server.Delegates.MyDelegates.
UIChangedEventHandler(this.Controller_UIChanged);
}
#region properties
public WaitingDialogue WaitingDialog { get; set; }
public bool IsBackgroundWorkerFree
{
get
{
return this.isBackgroundWorkerFree;
}
set
{
this.isBackgroundWorkerFree = value;
this.myEvent.OnBackgroundWorkerChecked(new EventArgs());
}
}
#endregion
#region ILogger Members
public void Log(string submittedMsg)
{
if (this != null && !string.IsNullOrEmpty(submittedMsg))
{
if (this.TxtLog != null)
{
if (this.InvokeRequired)
{
this.BeginInvoke((MethodInvoker)delegate()
{
string logMsg = string.Format("[{0}] {1}\r\n",
DateTime.Now, submittedMsg);
this.TxtLog.AppendText(logMsg);
this.TxtLog.SelectionStart = this.TxtLog.TextLength;
this.TxtLog.ScrollToCaret();
});
}
else
{
string logMsg = string.Format("[{0}] {1}\r\n",
DateTime.Now, submittedMsg);
this.TxtLog.AppendText(logMsg);
this.TxtLog.SelectionStart = this.TxtLog.TextLength;
this.TxtLog.ScrollToCaret();
}
}
}
}
public void LogM(string submttedMethodName, string submittedMsg)
{
if (this != null && !string.IsNullOrEmpty(submittedMsg))
{
if (this.TxtLog != null)
{
if (this.InvokeRequired)
{
this.BeginInvoke((MethodInvoker)delegate()
{
string logMsg = string.Format("[{0}][{1}] {2}\r\n",
DateTime.Now, submttedMethodName, submittedMsg);
this.TxtLog.AppendText(logMsg);
this.TxtLog.SelectionStart = this.TxtLog.TextLength;
this.TxtLog.ScrollToCaret();
});
}
else
{
string logMsg = string.Format("[{0}][{1}] {2}\r\n",
DateTime.Now, submttedMethodName, submittedMsg);
this.TxtLog.AppendText(logMsg);
this.TxtLog.SelectionStart = this.TxtLog.TextLength;
this.TxtLog.ScrollToCaret();
}
}
}
}
#endregion
#region Controller_UIChanged
private void Controller_UIChanged(object sender, MyUIEventArgs e)
{
try
{
UIEntities entities = e.CurrentUIEntities;
if (this.InvokeRequired)
{
this.BeginInvoke((MethodInvoker)delegate
{
this.TxtCopyFrom.Enabled = entities.EnableTxtCopyFrom;
this.TxtCopyTo.Enabled = entities.EnableTxtCopyTo;
this.BtnStart.Enabled = entities.EnableBtnStart;
this.BtnStop.Enabled = entities.EnableBtnStop;
this.BtnClose.Enabled = entities.EnableBtnClose;
this.BtnCopyFrom.Enabled = entities.EnableBtnCopyFrom;
this.BtnCopyTo.Enabled = entities.EnableBtnCopyTo;
});
}
else
{
this.TxtCopyFrom.Enabled = entities.EnableTxtCopyFrom;
this.TxtCopyTo.Enabled = entities.EnableTxtCopyTo;
this.BtnStart.Enabled = entities.EnableBtnStart;
this.BtnStop.Enabled = entities.EnableBtnStop;
this.BtnClose.Enabled = entities.EnableBtnClose;
this.BtnCopyFrom.Enabled = entities.EnableBtnCopyFrom;
this.BtnCopyTo.Enabled = entities.EnableBtnCopyTo;
}
}
catch (Exception oEx)
{
this.LogM("Controller_UIChanged", oEx.ToString());
}
}
#endregion
#region BtnStart_Click
private void BtnStart_Click(object sender, EventArgs e)
{
try
{
TxtLog.Text = string.Empty;
string copyTO = !string.IsNullOrEmpty(this.TxtCopyTo.Text) ?
this.TxtCopyTo.Text : string.Empty;
string copyFrom = !string.IsNullOrEmpty(this.TxtCopyFrom.Text) ?
this.TxtCopyFrom.Text : string.Empty;
if (!string.IsNullOrEmpty(copyFrom) && !string.IsNullOrEmpty(copyTO))
{
MyFileSystemEventArgs args = new MyFileSystemEventArgs
(new MyFileSystemEntities() { CopyFrom = copyFrom,
CopyTo = copyTO, Messages = string.Empty });
this.myEvent.OnCopyStarted(args);
}
else
{
MessageBox.Show("Please Specify the 'Copy from' and 'Copy to'");
}
}
catch (Exception oEx)
{
this.LogM("BtnStart_Click", oEx.ToString());
}
}
#endregion
#region BtnStop_Click
instance containing the event data.</param>
private void BtnStop_Click(object sender, EventArgs e)
{
try
{
this.WaitingDialog = new WaitingDialogue();
this.myEvent.OnFileCopyStopped(e);
if (this.controller.BackgroundWorker.IsBusy)
{
this.WaitingDialog.ShowDialog();
}
}
catch (Exception oEx)
{
this.LogM("BtnStop_Click", oEx.ToString());
}
}
#endregion
#region BtnClose_Click
/// <summary>
/// Handles the Click event of the BtnClose control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.EventArgs"/>
///instance containing the event data.</param>
private void BtnClose_Click(object sender, EventArgs e)
{
if (!this.controller.BackgroundWorker.IsBusy)
{
Application.Exit();
}
else
{
MessageBox.Show("Server is running. Please stop it first");
}
}
#endregion
#region FrmMain_FormClosing
/// <summary>
/// Handles the FormClosing event of the FrmMain control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.Windows.Forms.FormClosingEventArgs"/>
/// instance containing the event data.</param>
private void FrmMain_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason == CloseReason.UserClosing)
{
if (!this.controller.BackgroundWorker.IsBusy)
{
Application.Exit();
}
else
{
MessageBox.Show("Server is running. Please stop it first");
e.Cancel = true;
}
}
}
#endregion
#region FrmMain_Load
/// <summary>
/// Handles the Load event of the FrmMain control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.EventArgs"/>
/// instance containing the event data.</param>
private void FrmMain_Load(object sender, EventArgs e)
{
UIEntities ui = new UIEntities()
{
EnableBtnClose = true,
EnableBtnCopyFrom = true,
EnableBtnCopyTo = true,
EnableBtnStop = false,
EnableBtnStart = true,
EnableTxtCopyFrom = true,
EnableTxtCopyTo = true
};
MyUIEventArgs uiArgs = new MyUIEventArgs(ui);
this.myEvent.OnUIChanged(uiArgs);
}
#endregion
#region BtnCopyFrom_Click
/// <summary>
/// Handles the Click event of the BtnCopyFrom control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.EventArgs"/>
/// instance containing the event data.</param>
private void BtnCopyFrom_Click(object sender, EventArgs e)
{
using (FolderBrowserDialog folderDialog = new FolderBrowserDialog())
{
folderDialog.ShowDialog();
this.TxtCopyFrom.Text = folderDialog.SelectedPath.ToString();
}
this.SetUpUIForStartOperation();
}
#endregion
#region BtnCopyTo_Click
/// <summary>
/// Handles the Click event of the BtnCopyTo control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.EventArgs"/>
/// instance containing the event data.</param>
private void BtnCopyTo_Click(object sender, EventArgs e)
{
using (FolderBrowserDialog folderDialog = new FolderBrowserDialog())
{
folderDialog.ShowDialog();
this.TxtCopyTo.Text = folderDialog.SelectedPath.ToString();
}
this.SetUpUIForStartOperation();
}
#endregion
#region SetUpUIForStartOperation
/// <summary>
/// Sets up UI for start operation.
/// </summary>
private void SetUpUIForStartOperation()
{
if (!string.IsNullOrEmpty(this.TxtCopyTo.Text) &&
!string.IsNullOrEmpty(this.TxtCopyFrom.Text))
{
if (this.IsItValidDirectory(this.TxtCopyFrom.Text) &&
this.IsAnyParentDirectory(this.TxtCopyTo.Text))
{
UIEntities ui = new UIEntities()
{
EnableBtnClose = true,
EnableBtnCopyFrom = true,
EnableBtnCopyTo = true,
EnableBtnStop = false,
EnableBtnStart = true,
EnableTxtCopyFrom = true,
EnableTxtCopyTo = true
};
MyUIEventArgs uiArgs = new MyUIEventArgs(ui);
this.myEvent.OnUIChanged(uiArgs);
}
else
{
MessageBox.Show("Please enter a valid directory path in 'Copy form'");
}
}
}
#endregion
#region IsItValidDirectory
/// <summary>
/// Determines whether [is it valid directory]
/// [the specified submitted full path].
/// </summary>
/// <param name="submittedFullPath">The submitted full path.</param>
/// <returns><c>true</c> if [is it valid directory]
/// [the specified submitted full path]; otherwise, <c>false</c>.</returns>
private bool IsItValidDirectory(string submittedFullPath)
{
if (!string.IsNullOrEmpty(submittedFullPath))
{
return Directory.Exists(submittedFullPath);
}
return false;
}
#endregion
#region IsAnyParentDirectory
/// <summary>
/// Determines whether [is any parent directory]
/// [the specified submitted full path].
/// </summary>
/// <param name="submittedFullPath">The submitted full path.</param>
/// <returns><c>true</c> if [is any parent directory]
/// [the specified submitted full path]; otherwise, <c>false</c>.</returns>
private bool IsAnyParentDirectory(string submittedFullPath)
{
if (!string.IsNullOrEmpty(submittedFullPath))
{
string parentPath = submittedFullPath.Trim
(' ', Path.DirectorySeparatorChar);
parentPath = parentPath.LastIndexOf
(Path.DirectorySeparatorChar) >
This class implements the ILogger
and contains the IController
, IMasterService
, IEvent
. When we click the button CopyFrom
/CopyTo
, it pops up Folder Browser to set the specific path. When we click Start button, it issues the event onCopyStarted
whereas when we click the stop button, it issues the event onFileCopyStopped
. It also maintains the log for all operations including if any exception occurred.
For better understanding, there is a need to clarify and explain the following methods:
public void Log(string submittedMsg)
{
if (this != null && !string.IsNullOrEmpty(submittedMsg))
{
if (this.TxtLog != null)
{
if (this.InvokeRequired)
{
this.BeginInvoke((MethodInvoker)delegate()
{
string logMsg = string.Format("[{0}] {1}\r\n",
DateTime.Now, submittedMsg);
this.TxtLog.AppendText(logMsg);
this.TxtLog.SelectionStart = this.TxtLog.TextLength;
this.TxtLog.ScrollToCaret();
});
}
else
{
string logMsg = string.Format("[{0}] {1}\r\n",
DateTime.Now, submittedMsg);
this.TxtLog.AppendText(logMsg);
this.TxtLog.SelectionStart = this.TxtLog.TextLength;
this.TxtLog.ScrollToCaret();
}
}
}
}
This function takes the submittedMessage
as a parameter and this will display on the form. What it does actually is that it first checks the parameter, then checks that if there is any invocation required. If it requires, then I have used here MethodInvoker which is a delegate that can execute any method in managed code. Using this.BeginInvoke((MethodInvoker)delegate()
ensures that the delegate is called on the GUI thread. This is a technique of avoiding code duplication when the same method can be called both from the GUI thread and other threads.
That's It
That's it for now, I hope this article and the result downloads inspire you enough to develop your own server software.