sept. 30

Regularly, you would manipulate several XmlDocument and you may need to select some XmlNode to copy / move them to another one.

Let's illustrate this behavior by a test.

[TestMethod]
public void TestMethod1()
{
   string xml = @"
         <myNode attribute=""MyAttributeValue"">
            <myChildNode />
         </myNode>
   ";
   /*Code ignored : this method will just put the XML 
    * in a single line like : 
    * <myNode attribute=""MyAttributeValue""><...
    */
   string xmlStripped = StripXml(xml);
 
   //1. Let's load the Source XmlDocument
   XmlDocument domSource = new XmlDocument();
   domSource.LoadXml(xmlStripped);
   Assert.AreEqual(xmlStripped, domSource.OuterXml);
 
   //2. Let's select a node
   XmlNode node = domSource.SelectSingleNode("myNode");
 
   //3. Copy it !
   XmlDocument domTarget = new XmlDocument();
   CopyTo(domTarget, node);
   Assert.AreEqual(xmlStripped, domTarget.OuterXml);
}

How could we code the CopyTo method ? Let's do that simply :

/// <summary>
/// Copy a node from a source XmlDocument to a target XmlDocument
/// </summary>
/// <param name="domTarget">The XmlDocument to which we want to copy</param>
/// <param name="node">The node we want to copy</param>
private void CopyTo(XmlDocument domTarget, XmlNode node)
{
   domTarget.AppendChild(node);
}

Too simple maybe, it fails with the following exception : "The node to be inserted is from a different context"

Why ? Simple in fact. Each XmlNode belongs to an XmlDocument and they simply cannot be moved like that. Let's try another implementation :

/// <summary>
/// Copy a node from a source XmlDocument to a target XmlDocument
/// </summary>
/// <param name="domTarget">The XmlDocument to which we want to copy</param>
/// <param name="node">The node we want to copy</param>
private void CopyTo(XmlDocument domTarget, XmlNode node)
{
   XmlNode nodeToMove = domTarget.ImportNode(node, true);
   domTarget.AppendChild(nodeToMove);
}

There is no extra things to do, you don't need to implement your own Copy method ! (Note that this behavior exist since .NET 1.0) Note also that if you manipulate an XmlNode and not an XmlDocument, you can just access the XmlDocument to which the node belongs to, by using the OwnerDocument property.

Tags: | |
sept. 25

We have seen previously how to create and deploy a windows service (here and here using a separate installer DLL). What we have seen also is that we can control the start type of the service to specify whether it will be launched manually or automatically. This is very interesting, however this will be used internally by windows only at the next startup of the operating system.

It seems very logical that if we install a service and that we want it to start in an "automatic way", this service shall be running at the end of the installation process, and this without any manual action. We can do this in a very easy way via the installer (I will start again from the previous installer DLL we have created in this post).

Updating the installer

We will need to work at the end of the installation, ie on the "Commit" event. To do so :

  • Right-Click on the setup project and choose "View / Custom Actions"
  • Right-Click on the "Commit" event and choose "Add Custom Action"
  • Choose the "Primary Output from MyWindowsServiceInstallerActions" (from the Application Folder) and click OK

Now our installer DLL will also get called when the commit is occuring.

Starting the event

We can now update the installer class as follows :

using System.Collections;
using System.ComponentModel;
using System.Configuration.Install;
using System.IO;
using System.ServiceProcess;
 
namespace MyWindowsServiceInstallerActions
{
   [RunInstaller(true)]
   public partial class MyInstaller : Installer
   {
      //Other implementation remained unchanged, and so ommited here
 
      protected override void OnCommitted(IDictionary savedState)
      {
         base.OnCommitted(savedState);
 
         ServiceController service = new ServiceController("MyPersonalService");
         service.Start();
      }
   }
}

Some explanation about the code :

  • We could have decided to do the same treatment on the "Commit" event
  • The ServiceController class let's us manipulate a service (based on the service name, as set in the constructor of the service)
  • If for any reason, the "Start" method doesn't work (problem in the service, event log full, ...) an exception will get raised, and so the installation will fail and rollback.

A more secure implementation would be something like (you must of course include a reference to System.WindowsForms.dll if you want to use the MessageBox class) :

protected override void OnCommitted(IDictionary savedState)
{
   base.OnCommitted(savedState);
 
   ServiceController service = new ServiceController("MyPersonalService");
   string errorMessage = string.Format("Could not start the service {0}. You should launch it manually"
                                       service.DisplayName);
 
   //We'll try starting the service only if not started yet
   if ( service.Status == ServiceControllerStatus.Stopped )
   {
      try
      {
         service.Start();
         //Let's wait (for max 10 seconds) that this service is running
         service.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, 10));
      }
      catch ( InvalidOperationException )
      {
         //The Start method failed
         MessageBox.Show(errorMessage);
      }
      catch ( System.ServiceProcess.TimeoutException )
      {
         //The Start method succeeded, but after 10 seconds, the service was still not running
         MessageBox.Show(errorMessage);
      }
   }
}
Note that we use the fully qualified name for the TimeOutException not to have any ambiguity with the same exception in the System namespace.
sept. 24

We have seen how to create and to deploy a windows service in C#. However this post was explaining how to use to the designer functionality to deploy our service. This is not really a problem (who wants to do himself what has already been done by another one ?) but it has a "huge" drawback. This installer needs to be next to our service. And who on earth really want to find in a same place the production code (ie the service) and the deployment code (ie the installer) ? The next step would probably to add some test code, and then...

So we will see here how we can create the installer class in a separate DLL.

Creating the installer's action class manually

  • Add a new project of type "Empty Project" (this type is avaiable under Visual C# / Windows) and let's name it "MyWindowsServiceInstallerActions"
    • Be sure to theck that this project is considered as a "Class Library" in your project properties
  • Right-clic on this project and select "Add / New Item" and then "General / Installer Class". Name it "MyInstaller"
  • Add a new reference to "System.ServiceProcess.dll"
  • Switch to code view and let's complete the constructor as follows
using System.Collections;
using System.ComponentModel;
using System.Configuration.Install;
using System.IO;
using System.ServiceProcess;
 
namespace MyWindowsServiceInstallerActions
{
   [RunInstaller(true)]
   public partial class MyInstaller : Installer
   {
      public MyInstaller()
      {
         InitializeComponent();
 
         //Create the installer that will configure the windows service
         //by writing some values in the registry
         //This class is specific to a single Windows Service
         ServiceInstaller myServiceInstaller = new ServiceInstaller();
         //This should be the same name as what you specified in the service constructor
         myServiceInstaller.ServiceName = "MyPersonalService";
         myServiceInstaller.DisplayName = "My Personal Service";
         myServiceInstaller.StartType = ServiceStartMode.Automatic;
         this.Installers.Add(myServiceInstaller);
 
         //Create the installer that will register the windows service
         //This class could register many services
         ServiceProcessInstaller myProcessInstaller = new ServiceProcessInstaller();
         myProcessInstaller.Account = ServiceAccount.LocalService;
         this.Installers.Add(myProcessInstaller);
      }
   }
}

We now have a functional installer class. Let's now update our setup project to use this one !

Update the setup project to use our installer

  • First we need to add a new output 
    • Right-clic on the setup project and choose "Add / Project Output"
    • Choose "MyWindowsServiceInstallerActions", "Primary Output" and "Release" and click OK
  • Then we need to update the actions
    • Right-clic on the setup project and choose "View / Custom Actions"
    • Remove any action that is already registered
    • Clic on Install and choose "Add Custom Action"
    • Select the "Primary Output from MyWindowsServiceInstallerActions" and click OK
    • Do the same for the Uninstall event 

Let's check if this is functional

Let's rebuild and install our setup project. If we then go to the Service manager we can again see our service. Let's start it !

This time it does not work. We have a nice error saying something like "Error 193 0xc1". When we check in the event viewer we can have a little bit more explanation : "The My Personal Service service failed to start due to the following error : My Personal Service is not a valid Win32 application."

Yes that's right, a service must be an ".exe" file and not a ".dll" file but of course our service project generates a ".exe" and no problem here. So where ?

Let's check if our service registration is correct. All services are registered in the registry. So let's check there ! Check the key "HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Services" and you will find an entry for all services you have on your computer. So you will find an entry for the "My Personal Service" and you can check the value of the key "ImagePath".

Surprise ! For Windows, our service assembly is the MyWindowsServiceInstallerActions.dll file. It's so highly normal it does not work. Why this value ? Simply because the ServiceInstaller class just register the current assembly. So let's correct that !

Note that when you have tried to start your project, you will need to uninstall it (via Visual Studio) and the to log off so Windows can correctly uninstall it.

Let's correct the setup project

First we will need to know where the project is being installed

  • Right-clic on the setup project and choose "View / Custom Actions"
  • Select the "Primary Output" under the "Install" event and check the property
  • Update the CustomActionData property to set /Target="[TARGETDIR]\"

Doing this, we declare a new parameter called "Target" that will have for value, the value of "TARGETDIR" which is a system value giving us the installation directory. Be careful not to forget the quotes (") and the trailing backslash (\). Indeed to set some parameters to system values you need to use of the two following syntax:

  • /MyPropertyName=[SystemValue]
  • /MyPropertyName="[SystemValue]\"

The second syntax must be used if the system value may contain spaces (which is typically the case of an installation directory).

Now we can update the code of our installer class as follows :

using System.Collections;
using System.ComponentModel;
using System.Configuration.Install;
using System.IO;
using System.ServiceProcess;
 
namespace MyWindowsServiceInstallerActions
{
   [RunInstaller(true)]
   public partial class MyInstaller : Installer
   {
      public MyInstaller()
      {
         //Unchanged, and so ommited
      }
 
      protected override void OnBeforeInstall(IDictionary savedState)
      {
         base.OnBeforeInstall(savedState);
 
         string targetPath = this.Context.Parameters["Target"];
         this.Context.Parameters["assemblypath"] = Path.Combine(targetPath, "MyWindowsService.exe");
      }
   }
}

A few notes about this code :  

  • You could have decided to override the Install method itself.
  • It's impossible to move this code to the constructor as the context parameters are unknown at that place
  • Note that the parameter "assemblyPath" is used internally by the ServiceInstaller to update the registry

Let's check again

We can now rebuild, reinstall and start the service again. Now this is working fine !

You now have a functional service with a correct separation of production code and deployment code. What would be the next step ? To update again the setup project to have our service start immediately after the installation. See you soon !

sept. 22

Let's say we have the following specifications :

  • We want an application that will turn continuously on a machine
  • This application shall require no manual interaction (starting automatically)
  • This application shall monitor a specific folder on the file

The specifications are very simple and we will probably start thinking to a windows service that would be launched in an automatic manner. This service could then use a FileSystemWatcher to do its job.

Let's create our service

  • Create a new Blank Solution
  • Add a new project of type "Windows Service" (this type is available under the Visual C# / Windows section) and let's call it MyWindowsService
  • Automatically, VS will create a new project containing a class called "Service1", inheriting from System.ServiceProcess.ServiceBase
  • Right-Click on the file and choose Rename. Rename it to MyWindowsService
  • Update the constructor to set a service name.
using System.ServiceProcess;
 
namespace MyWindowsService
{
   public partial class MyWindowsService : ServiceBase
   {
      public MyWindowsService()
      {
         InitializeComponent();
         this.ServiceName = "MyPersonalService";
      }
 
      protected override void OnStart(string[] args)
      {
      }
 
      protected override void OnStop()
      {
      }
   }
}

This class exposes a parameterless constructor and two methods OnStart and OnStop. These are the two methods that will be called when the service is being started (or stopped) by Windows. Note that automatically, when a service is being started or stopped, Windows will log an entry in the event viewer.

Let's check this !

Let's deploy our (empty) service 

To do so, we'll need to create a setup project and some installation actions to parameter the way the service is started.

  • Add a new project of type "Setup project" (this type is available under Other Project Types / Setup and deployment)
  • Let's now how add a reference to our project
    • Right-Clic on the setup project and choose "Add / Project Output"
    • Select the project "MyWindowsService", the "Primary Output" and the configuration "Release"
    • Click on OK 

We will need now to control the way the service will be started and to specify it is a service. For that we have two solutions, either we rely on the service itself, or we write it ourself. We will here only see how to create the installer thru the service itself. 

Creating the installer's action class via the Service class itself

  • Double-Click on "MyWindowsService.cs" and right-clic in the designer to choose "Add Installer"
  • This will add a new file called "ProjectInstaller.cs" that contains a "serviceInstaller" and a "serviceProcessInstaller"
  • Check the property of the ServiceInstaller
    • Change the ServiceName to "MyPersonalService" (shall be the same as what you specified in the service constructor)
    • Change the DisplayName to "My Personal Service"
    • Change the StartType to "Automatic"
  • Check the property of the ServiceProcessInstaller
    • Change the Account to... well any value depending of the behaviour you would like. In my case, I will choose "LocalService". You can check the Service.ServiceProcess.ServiceAccount class for more information about this. 

Let's now complete the installer

Now we can complete the installer to add the installer actions we have just created.

  • Right click on the setup project and choose "View / Custom Actions"
  • Now we need to add our installer class to be used for the events "Install" and "Uninstall"
    • Right-Clic on Install and choose "Add Custom Actions"
    • Double clic on Application Folder
    • Select the "Primary Output from MyWindowsService" and click OK
    • Do the same thing for the event "Uninstall"

Now we just need to compile and to install the service to check that it is working.

  • Set yourself in "Release Mode"
  • Right-Clic on the Setup project and choose "Rebuild"
  • Right-Clic on the project and choose "Install"

Let's check the behaviour

As I have said before a service will log some information when being started and stopped. We can so go in the "Services" management window.

 

Note that even if we have put the "start type" to "Automatic", you must start the service manually. It would start automatically only after the next windows reboot !

Let's start the service and check in the Event Viewer.

We can see two new events saying respecively

  • The My Personal Service service was succesfully sent a start control
  • The My Personal Service service entered the running state

So far so good !

Let's complete our service to let him work

Now that we have a functional service, we can complete him to work. We wanted him to monitor a folder. Let's modify our service and do that this way:

using System.IO;
using System.ServiceProcess;
 
namespace MyWindowsService
{
   public partial class MyWindowsService : ServiceBase
   {
      public MyWindowsService()
      {
         InitializeComponent();
         this.ServiceName = "MyPersonalService";
      }
 
      protected override void OnStart(string[] args)
      {
         //1. Let's check only txt files in a specific folder
         string path = @"c:\Temp\MyFolderToMonitor";
         string filter = "*.txt";
         FileSystemWatcher watcher = new FileSystemWatcher(path, filter);
 
         //2. We want information about the filename
         watcher.NotifyFilter = NotifyFilters.FileName;
 
         //3. Let's register the creation event only, and let's start the monitring
         watcher.Created += new FileSystemEventHandler(watcher_Created);
         watcher.EnableRaisingEvents = true;
      }
 
      /// <summary>
      /// When a file is created, we'll just add an entry in the EventLog
      /// Note that this.EventLog will log to the Application Log, 
      /// with the name of the service as source.
      /// To log in another log, you should create yourself an EventLog.
      /// </summary>
      private void watcher_Created(object sender, FileSystemEventArgs e)
      {
         string message = string.Format("{0} : {1}", e.ChangeType, e.Name);
         this.EventLog.WriteEntry(message);
      }
 
      protected override void OnStop()
      {
      }
   }
}

Now we can just rebuild and reinstall the service. We can then start it, create a file in the "Temp\FolderToMonitor" folder and stop the service. If we now look to the EventViewer, what can we see ? Three events :

  • Service started successfully
  • Created: Document1.txt
  • Service stopped successfully

And here we are ! We now have a fully functional windows service.

What's next ?

You could do many more things to improve your service (some proposals will be described in later posts)

sept. 16

This year, in Amsterdam, is organised the First International SOA Symposium ! And I will be there !

Join me in this "SOA Mass" to follow conferences given by Paul Brown, Sandy Carter, David Chappell, Thomas Erl and many more !

I haven't completely closed my course choice but no doubt this will be exciting and I will try to blog rapidly after that about the best conferences I would have seen !

Tags: |