Cinchoo – Xsd GUI tool

Download Source.zip – 52.5 KB

Download Binary.zip – 513.8 KB

Contents

1. Introduction

Many time we come across the need of parsing XML document and creating XML schema or common language runtime classes/entities. These entities are later passed between multiple methods, stored as configuration or used to perform some manipulation. Hand wrting code for parsing these entities from XML document is tedious.

Microsoft has a nifty little tool called xsd.exe which lets you create serializable classes for the .NET framework from an XSD or XML file. Unfortunately it doesn’t come with the latest versions of Visual Studio and you have to download the Windows SDK to get hold of it.

This article presents the new Cinchoo GUI interface to xsd.exe tool to to parse XML documents and generating entities. This new interface built using Cinchoo framework, using most of its features like Configuration, Command line parsing, Application host, binding to controls etc. This is one of the the working example of building an application using this versatile .NET framework easily.

2. Features

Cinchoo Xsd tool provides following featutes

  1. Simplified GUI interface
  2. Support of Nested Xml files (refer Handling Nested Xml files article for more information about it.)
  3. Can run as console mode (passing /#AM:Console as command line argument)
  4. Can run as tray application (on minimizing the window)

3. How to run

This tool can be started as

  • Console Application
  • Windows Application
  • Tray Application

3.1 Window Mode

By default, if you run the executable (ChoXsd.exe), this will start as Windows application. The main window looks like below

Image 3.1.1 Main window 

  • Xml File Path – Input xml file path.
  • Output Directory – Destination directory where the generated files are created. If not specified, the generated files will be created under the input xml file directory.
  • Options – Please refer Microsoft Xsd.exe tool document for each option.
  • Status – All the generation process status messages are posted.
  • Generate – Button is to start the generation process.
  • Cancel – Button to cancel the generation process.

3.2 Tray Application Mode

Sometime you may want to keep this tool running all the time and quickly go back to get access to this tool to carry out generation of entities. In such cases, you can let them run in tray mode. By pressing ‘minimize’ button, the tool will be made to run in tray mode.

In this mode, below features are available to you. These will be available when you right click on this tool application icon in tray taskbar

  • Open – To restore the window.
  • Always on Top – To keep the window as topmost of all other windows applications.
  • Run at System Startup – This option let the application start up automatically when system starts.
  • Show in Taskbar – To show the application in the Taskbar.

3.3 Console Mode

This tool can be started as Console Application by passing /#AM:Console command line parameter to the executable.

Listing 3.3.1 Running as console mode 

>ChoXsd.exe /#AM:Console

3.3.1 Help

To get help of the application, pass /? in command line

Listing 3.3.1.1 Getting help

>ChoXsd.exe /#AM:Console /?
ChoXsd [Version 1.0.0.1]
Copyright c  2014

CHOXSD xmlfile [/order] [/f] [/edb] [/n:] [/l:] [/c] [/o:] [/d] [/e:] [/eld]

        xmlfile Name of an xml file to infer xsd schema from.
        /order  Generate explicit order identifiers on all particle members.
        /f      Generate fields instead of properties.
        /edb    Implement INotifyPropertyChanged interface on all generated
                types to enable data binding..
        /n      The namespace for generated class files. The default
                namespace is the global namespace.
        /l      The language to use for the generated code. Choose from
                'CS', 'VB', 'JS', 'VJS', 'CPP' or provide a fully-qualified
                name for a class implementing
                System.CodeDom.Compiler.CodeDomProvider. The default
                language is 'CS' (CSharp).
        /c      Generate classes for this schema.
        /o      The output directory to create files in. The default is the
                current directory.
        /d      Generate sub-classed DataSet for this schema.
        /e      Element from schema to process.
        /eld    Generate LINQ-enabled sub-classed Dataset for the schemas
                provided.

Below is screenshot of help

Image 3.3.1.1 Console window 

4. Configuration

UPDATE:

This application uses Microsoft Xsd.exe tool to generate entities. This program automatically discovers Xsd.exe tool under ‘C:\Program Files\Microsoft SDKs\Windows’ folder, remember it at startup. If you wish to use different version of this tool (Xsd.exe) in different location, you can configure it by opening [AppExe].config file and specifying Microsoft SDK path containing Xsd.exe tool in ‘SDKDirectory‘ appsetting.

Listing 4.1 [AppExe].config file

<?xml version="1.0" encoding="utf-8"?>
<configuration>
 <appSettings>
   <add key="SDKDirectory" value="C:\Program Files\Microsoft SDKs\Windows" />
 </appSettings>
</configuration>

Cinchoo – Simplified Windows Tray Application Development

Download sample – 20.3 KB

Contents

1. Introduction

Cinchoo is the application framework for .NET. One of the main functionalities it provides to the users is application configuration management. Application configuration is the information that application reads and/or writes at run-time from the source.

One another important feature it offers to the developers community is the unified, generic application host to build and run application in different modes. Code once, run the application as either Console, Windows, WPF, Windows Service or Windows Tray application.

In this article, I’m going to illustrate how to use this library to create Windows Systems Tray application. It is a simpler, fluent model for configuring everything about your tray application in one place. Making it as library letting you to concentrate yourself on the core development tasks. Cinchoo provides a clean and easy API to develop and run Windows systems tray applications.

2. Requirement

The application host library is written in C# for the .NET 4.0 Framework. It is part of Cinchoo framework, which is a great library with lot of features like Configuration Management, common ApplicationHost, Shell features etc.

3. “Hello World!” Sample

Lets begin by looking into a simple example of a windows systems tray application displaying ‘Hello World!’ tooltip text.

Download the latest Cinchoo binary here. (Nuget Command: Install-Package Cinchoo)

  • Open VS.NET 2010 or higher
  • Create a sample VS.NET (.NET Framework 4) Console Application project
  • Add reference to Cinchoo.Core.dll
  • Use the Cinchoo.Core namespace
  • Copy and paste the below application host object

Listing 3.1 Defining ApplicationHost object

[ChoApplicationHost]
public class HelloWorldAppHost : ChoApplicationHost
{
    protected override void ApplyGlobalApplicationSettingsOverrides(ChoGlobalApplicationSettings obj)
    {
        obj.TrayApplicationBehaviourSettings.TurnOn = true;
        obj.TrayApplicationBehaviourSettings.TooltipText = "Hello World!";
    }
}

The code above illustrates about defining ApplicationHost object. First thing define a ApplicationHost (ex. HelloWorldAppHost) class from ChoApplicationHost, it indicates that this object is an ApplicationHost object. And it must be decorated with ChoApplicationHostAttribute to complete the definition.

In this example, we override ApplyGlobalApplicationSettingsOverrides method. In there, we instruct the application to run as Tray Application by setting TurnOn member of TrayApplicationBehaviourSettings object to true. Set the TooltipText value to “Hello World!”, this will display the message when you mouse over the tray icon.

Listing 3.2 Main Method

class Program
{
    static void Main(string[] args)
    {
        ChoApplication.Run(args);
    }
}

We start by calling ChoApplication.Run() method in the main entry of the application. That all. All the heavy lifting of configuring, running the application as tray application is done by the library under the hood. Now your application is tray application enabled application. Lets try for yourself.

4. Defining Application Host Object

4.1 ChoApplicationHostAttribute

All application host objects must be decorated by ChoApplicationHostAttribute. An application must have atleast one application host object defined. Cinchoo framework will discover them at the application startup.

4.2 ChoApplicationHost

All application host objects must be derived from ChoApplicationHost class. By default, it provides basic wire frame of running tray application without making any customization. Some cases, you may want to customize the way you want. In such cases, override nessasary overrides in them. An application must have one application host object defined either in the entry assembly or referenced assemblies. In this object, you can override number of methods for your needs to customize the tray application.

4.2.1 ApplyGlobalApplicationSettingsOverrides() Method

When implemented in a derived class, executes when a application is launched. In here, you have option to override tray application configuration parameters to customize it. Please refer section TrayApplicationBehaviourSettings for more information.

4.2.2 AfterNotifyIconConstructed() Method

When implemented in a derived class, executes when a application runs in Tray application mode. In this method, you can change the tray icon properies. Please refer section ChoNotifyIcon section for more information.

4.2.3 OnTrayAppAboutMenuClicked() Method

When implemented in a derived class, executes when user clicks the ‘About’ menu item from the context menu. In here, you can display the product information of your application.

4.2.4 OnTrayAppExitMenuClicked() Method

When implemented in a derived class, executes when user clicks the ‘Exit’ menu item from the context menu. In here, you can perform any cleanup action of your application.

4.2.5 OnTrayAppHelpMenuClicked() Method

When implemented in a derived class, executes when user clicks the ‘Help’ menu item from the context menu. In here, you can display help information for your application.

4.2.6 OnTrayAppOpenMenuClicked() Method

When implemented in a derived class, executes when user clicks the ‘Open’ menu item from the context menu. By default, Cinchoo framework will open the main window of your application, if any specified. This default behaviour can be overriable to perform any custom actions.

4.2.7 MainWindowObject Property

It is a property returns the main window of your application. Default is null. In this case, your application is windowless tray application. This property can be overridable to return either System.Windows.Forms.Form or System.Windows.Window object.

4.2.8 ApplicationObject Property

This property mainly used for WPF application. It returns an object of System.Windows.Application object.

5. Configuration

5.1 TrayApplicationBehaviourSettings

This is easiest way to customize your tray application. Either override the ApplyGlobalApplicationSettingsOverrides() method in your Application Host object or open the ChoCoreFrx.xml configuration file to manipulate this object properties. Cinchoo framework uses them to customize the tray application before launching them.

5.1.1 TurnOn

Gets or sets the turn on switch the Tray Application.

Listing 5.1.1.1 Set TurnOn programmatically

[ChoApplicationHost]
public class ChoAppHost : ChoApplicationHost
{
    protected override void ApplyGlobalApplicationSettingsOverrides(ChoGlobalApplicationSettings obj)
    {
        obj.TrayApplicationBehaviourSettings.TurnOn = true;
        obj.TrayApplicationBehaviourSettings.BalloontipText = "Hello World!";
    }
}

Listing 5.1.1.2 Set TurnOn via ChoCoreFrx.xml file

Open ChoCoreFrx.xml file, specify the boolean value in ‘turnOn’ attribute under ‘trayApplicationBehaviourSettings’ element.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <globalApplicationSettings applicationId="WindowlessTrayApp.exe" eventLogSourceName="WindowlessTrayApp.exe">
    <behaviourSettings hideWindow="false" bringWindowToTop="false" alwaysOnTop="false" runAtStartup="false" runOnceAtStartup="false" singleInstanceApp="true" activateFirstInstance="false" showEnvironmentSelectionWnd="true" />
    <trayApplicationBehaviourSettings turnOn="true" showInTaskbar="true" hideMainWindowAtStartup="false" tooltipText="" balloonTipText="Hello World!" hideTrayIconWhenMainWindowShown="false" turnOnMode="OnMinimize">
      <trayIcon />
      <fontSettings fontName="Helvetica" fontSize="8">
        <fontColor />
      </fontSettings>
    </trayApplicationBehaviourSettings>
  </globalApplicationSettings>
</configuration>

5.1.2 BalloonTipText

Gets or sets the text to display on the balloon tip associated with the NotifyIcon.

Listing 5.1.2.1 Set BalloonTipText programmatically

[ChoApplicationHost]
public class ChoAppHost : ChoApplicationHost
{
    protected override void ApplyGlobalApplicationSettingsOverrides(ChoGlobalApplicationSettings obj)
    {
        obj.TrayApplicationBehaviourSettings.TurnOn = true;
        obj.TrayApplicationBehaviourSettings.BalloontipText = "Hello World!";
    }
}

Listing 5.1.2.2 Set BalloontipText via ChoCoreFrx.xml file

Open ChoCoreFrx.xml file, specify the text in ‘balloonTipText’ attribute under ‘trayApplicationBehaviourSettings’ element.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <globalApplicationSettings applicationId="WindowlessTrayApp.exe" eventLogSourceName="WindowlessTrayApp.exe">
    <behaviourSettings hideWindow="false" bringWindowToTop="false" alwaysOnTop="false" runAtStartup="false" runOnceAtStartup="false" singleInstanceApp="true" activateFirstInstance="false" showEnvironmentSelectionWnd="true" />
    <trayApplicationBehaviourSettings turnOn="false" showInTaskbar="true" hideMainWindowAtStartup="false" tooltipText="" balloonTipText="Hello World!" hideTrayIconWhenMainWindowShown="false" turnOnMode="OnMinimize">
      <trayIcon />
      <fontSettings fontName="Helvetica" fontSize="8">
        <fontColor />
      </fontSettings>
    </trayApplicationBehaviourSettings>
  </globalApplicationSettings>
</configuration>

5.1.3 TooltipText

Gets or sets the ToolTip text displayed when the mouse pointer rests on a notification area icon.

Listing 5.1.3.1 Set TooltipText programmatically

[ChoApplicationHost]
public class ChoAppHost : ChoApplicationHost
{
    protected override void ApplyGlobalApplicationSettingsOverrides(ChoGlobalApplicationSettings obj)
    {
        obj.TrayApplicationBehaviourSettings.TurnOn = true;
        obj.TrayApplicationBehaviourSettings.BalloontipText = "Hello World!";
    }
}

Listing 5.1.3.2 Set TooltipText via ChoCoreFrx.xml file

Open ChoCoreFrx.xml file, specify the text in ‘tooltipText’ attribute under ‘trayApplicationBehaviourSettings’ element.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <globalApplicationSettings applicationId="WindowlessTrayApp.exe" eventLogSourceName="WindowlessTrayApp.exe">
    <behaviourSettings hideWindow="false" bringWindowToTop="false" alwaysOnTop="false" runAtStartup="false" runOnceAtStartup="false" singleInstanceApp="true" activateFirstInstance="false" showEnvironmentSelectionWnd="true" />
    <trayApplicationBehaviourSettings turnOn="false" showInTaskbar="true" hideMainWindowAtStartup="false" tooltipText="" balloonTipText="Hello World!" hideTrayIconWhenMainWindowShown="false" turnOnMode="OnMinimize">
      <trayIcon />
      <fontSettings fontName="Helvetica" fontSize="8">
        <fontColor />
      </fontSettings>
    </trayApplicationBehaviourSettings>
  </globalApplicationSettings>
</configuration>

5.1.4 TrayIcon

Gets or sets the current icon. It will display an icon for an application in the notification area.

Listing 5.1.4.1 Set TrayIcon programmatically

[ChoApplicationHost]
public class ChoAppHost : ChoApplicationHost
{
    protected override void ApplyGlobalApplicationSettingsOverrides(ChoGlobalApplicationSettings obj)
    {
        obj.TrayApplicationBehaviourSettings.TurnOn = true;
        obj.TrayApplicationBehaviourSettings.TrayIcon = @"C:\Sample.ico";
    }
}

Listing 5.1.4.2 Set TrayIcon via ChoCoreFrx.xml file

Open ChoCoreFrx.xml file, specify the path to icon file in ‘trayIcon’ element under ‘trayApplicationBehaviourSettings’ element.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <globalApplicationSettings applicationId="WindowlessTrayApp.exe" eventLogSourceName="WindowlessTrayApp.exe">
    <behaviourSettings hideWindow="false" bringWindowToTop="false" alwaysOnTop="false" runAtStartup="false" runOnceAtStartup="false" singleInstanceApp="true" activateFirstInstance="false" showEnvironmentSelectionWnd="true" />
    <trayApplicationBehaviourSettings turnOn="false" showInTaskbar="true" hideMainWindowAtStartup="false" tooltipText="" balloonTipText="Hello World!" hideTrayIconWhenMainWindowShown="false" turnOnMode="OnMinimize">
      <trayIcon>C:\Sample.ico</trayIcon>
      <fontSettings fontName="Helvetica" fontSize="8">
        <fontColor />
      </fontSettings>
    </trayApplicationBehaviourSettings>
  </globalApplicationSettings>
</configuration>

5.1.5 ShowInTaskbar

Gets or sets a value indicating whether the main window is displayed in the Windows taskbar.

Listing 5.1.5.1 Set ShowInTaskbar programmatically

[ChoApplicationHost]
public class ChoAppHost : ChoApplicationHost
{
    protected override void ApplyGlobalApplicationSettingsOverrides(ChoGlobalApplicationSettings obj)
    {
        obj.TrayApplicationBehaviourSettings.TurnOn = true;
        obj.TrayApplicationBehaviourSettings.ShowInTaskbar = true;
    }
}

Listing 5.1.5.2 Set ShowInTaskbar via ChoCoreFrx.xml file

Open ChoCoreFrx.xml file, specify boolean value in ‘showInTaskbar’ attribute under ‘trayApplicationBehaviourSettings’ element.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <globalApplicationSettings applicationId="WindowlessTrayApp.exe" eventLogSourceName="WindowlessTrayApp.exe">
    <behaviourSettings hideWindow="false" bringWindowToTop="false" alwaysOnTop="false" runAtStartup="false" runOnceAtStartup="false" singleInstanceApp="true" activateFirstInstance="false" showEnvironmentSelectionWnd="true" />
    <trayApplicationBehaviourSettings turnOn="false" showInTaskbar="true" hideMainWindowAtStartup="false" tooltipText="" balloonTipText="Hello World!" hideTrayIconWhenMainWindowShown="false" turnOnMode="OnMinimize">
      <trayIcon>C:\Sample.ico</trayIcon>
      <fontSettings fontName="Helvetica" fontSize="8">
        <fontColor />
      </fontSettings>
    </trayApplicationBehaviourSettings>
  </globalApplicationSettings>
</configuration>

5.1.6 HideMainWindowAtStartup

Gets or sets a value indicating whether the main window should be displayed or not at the application startup.

Listing 5.1.6.1 Set ShowInTaskbar programmatically

[ChoApplicationHost]
public class ChoAppHost : ChoApplicationHost
{
    protected override void ApplyGlobalApplicationSettingsOverrides(ChoGlobalApplicationSettings obj)
    {
        obj.TrayApplicationBehaviourSettings.TurnOn = true;
        obj.TrayApplicationBehaviourSettings.HideMainWindowAtStartup = true;
    }
}

Listing 5.1.6.2 Set ShowInTaskbar via ChoCoreFrx.xml file

Open ChoCoreFrx.xml file, specify boolean value in ‘hideMainWindowAtStartup’ attribute under ‘trayApplicationBehaviourSettings’ element.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <globalApplicationSettings applicationId="WindowlessTrayApp.exe" eventLogSourceName="WindowlessTrayApp.exe">
    <behaviourSettings hideWindow="false" bringWindowToTop="false" alwaysOnTop="false" runAtStartup="false" runOnceAtStartup="false" singleInstanceApp="true" activateFirstInstance="false" showEnvironmentSelectionWnd="true" />
    <trayApplicationBehaviourSettings turnOn="false" showInTaskbar="true" hideMainWindowAtStartup="false" tooltipText="" balloonTipText="Hello World!" hideTrayIconWhenMainWindowShown="false" turnOnMode="OnMinimize">
      <trayIcon>C:\Sample.ico</trayIcon>
      <fontSettings fontName="Helvetica" fontSize="8">
        <fontColor />
      </fontSettings>
    </trayApplicationBehaviourSettings>
  </globalApplicationSettings>
</configuration>

5.1.7 HideTrayIconWhenMainWindowShown

Gets or sets a value indicating whether the tray icon should be hidden when main window is displayed.

Listing 5.1.7.1 Set HideTrayIconWhenMainWindowShown programmatically

[ChoApplicationHost]
public class ChoAppHost : ChoApplicationHost
{
    protected override void ApplyGlobalApplicationSettingsOverrides(ChoGlobalApplicationSettings obj)
    {
        obj.TrayApplicationBehaviourSettings.TurnOn = true;
        obj.TrayApplicationBehaviourSettings.HideTrayIconWhenMainWindowShown = true;
    }
}

Listing 5.1.7.2 Set HideTrayIconWhenMainWindowShown via ChoCoreFrx.xml file

Open ChoCoreFrx.xml file, specify boolean value in ‘hideTrayIconWhenMainWindowShown’ attribute under ‘trayApplicationBehaviourSettings’ element.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <globalApplicationSettings applicationId="WindowlessTrayApp.exe" eventLogSourceName="WindowlessTrayApp.exe">
    <behaviourSettings hideWindow="false" bringWindowToTop="false" alwaysOnTop="false" runAtStartup="false" runOnceAtStartup="false" singleInstanceApp="true" activateFirstInstance="false" showEnvironmentSelectionWnd="true" />
    <trayApplicationBehaviourSettings turnOn="false" showInTaskbar="true" hideMainWindowAtStartup="false" tooltipText="" balloonTipText="Hello World!" hideTrayIconWhenMainWindowShown="false" turnOnMode="OnMinimize">
      <trayIcon>C:\Sample.ico</trayIcon>
      <fontSettings fontName="Helvetica" fontSize="8">
        <fontColor />
      </fontSettings>
    </trayApplicationBehaviourSettings>
  </globalApplicationSettings>
</configuration>

5.1.8 TurnOnMode

Gets or sets a value indicating when the application is turned on to tray application mode. There are 3 modes to choose from

  1. OnMinimize (Default)
  2. OnClose
  3. OnMinimizeOrClose

Listing 5.1.8.1 Set TurnOnMode programmatically

[ChoApplicationHost]
public class ChoAppHost : ChoApplicationHost
{
    protected override void ApplyGlobalApplicationSettingsOverrides(ChoGlobalApplicationSettings obj)
    {
        obj.TrayApplicationBehaviourSettings.TurnOn = true;
        obj.TrayApplicationBehaviourSettings.TurnOnMode = ChoTrayAppTurnOnMode.OnMinimize;
    }
}

Listing 5.1.8.2 Set TurnOnMode via ChoCoreFrx.xml file

Open ChoCoreFrx.xml file, specify the ChoTrayAppTurnOnMode value in ‘turnOnMode’ attribute under ‘trayApplicationBehaviourSettings’ element.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <globalApplicationSettings applicationId="WindowlessTrayApp.exe" eventLogSourceName="WindowlessTrayApp.exe">
    <behaviourSettings hideWindow="false" bringWindowToTop="false" alwaysOnTop="false" runAtStartup="false" runOnceAtStartup="false" singleInstanceApp="true" activateFirstInstance="false" showEnvironmentSelectionWnd="true" />
    <trayApplicationBehaviourSettings turnOn="false" showInTaskbar="true" hideMainWindowAtStartup="false" tooltipText="" balloonTipText="Hello World!" hideTrayIconWhenMainWindowShown="false" turnOnMode="OnMinimize">
      <trayIcon>C:\Sample.ico</trayIcon>
      <fontSettings fontName="Helvetica" fontSize="8">
        <fontColor />
      </fontSettings>
    </trayApplicationBehaviourSettings>
  </globalApplicationSettings>
</configuration>

5.1.9 ContextMenu Settings

Gets or Sets the visibility of each of menu items in the default context menu displayed in the tray icon. Below are menu items can be controled via this settings object

  1. About
  2. Help
  3. Exit
  4. AlwaysOnTop
  5. RunAtSystemsStartup
  6. ShowInTaskbar

Listing 5.1.9.1 Set ContextMenuSettings programmatically

[ChoApplicationHost]
public class ChoAppHost : ChoApplicationHost
{
    protected override void ApplyGlobalApplicationSettingsOverrides(ChoGlobalApplicationSettings obj)
    {
        obj.TrayApplicationBehaviourSettings.TurnOn = true;
        obj.TrayApplicationBehaviourSettings.ContextMenuSettings.DisplayShowInTaskbarMenuItem = false;
    }
}

Listing 5.1.9.2 Set ContextMenuSettings via ChoCoreFrx.xml file

Open ChoCoreFrx.xml file, specify the boolean values to each attributes in ‘contextMenuSettings’ element under ‘trayApplicationBehaviourSettings’ element.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <globalApplicationSettings applicationId="WindowlessTrayApp.exe" eventLogSourceName="WindowlessTrayApp.exe">
    <behaviourSettings hideWindow="false" bringWindowToTop="false" alwaysOnTop="false" runAtStartup="false" runOnceAtStartup="false" singleInstanceApp="true" activateFirstInstance="false" showEnvironmentSelectionWnd="true" />
    <trayApplicationBehaviourSettings turnOn="false" showInTaskbar="true" hideMainWindowAtStartup="false" tooltipText="" balloonTipText="Hello World!" hideTrayIconWhenMainWindowShown="false" turnOnMode="OnMinimize">
      <trayIcon>C:\Sample.ico</trayIcon>
      <fontSettings fontName="Helvetica" fontSize="8">
        <fontColor />
      </fontSettings>
      <contextMenuSettings displayAlwaysOnTopMenuItem="true" displayRunAtSystemsStartupMenuItem="true" displayShowInTaskbarMenuItem="true" displayAboutMenuItem="true" displayHelpMenuItem="true" displayExitMenuItem="true" />
    </trayApplicationBehaviourSettings>
  </globalApplicationSettings>
</configuration>

5.1.10 Font Settings

Gets or Sets the fonts propeties to display text instead of icon in the task tray. Using one of the ShowText() overloads to set this text. These are default application wide settings, can be overriable via one of the ShowText() overloads.

Available properties are

  1. FontColor
  2. FontName
  3. FontSize

Listing 5.1.10.1 Set FontSettings programmatically

[ChoApplicationHost]
public class ChoAppHost : ChoApplicationHost
{
    protected override void ApplyGlobalApplicationSettingsOverrides(ChoGlobalApplicationSettings obj)
    {
        obj.TrayApplicationBehaviourSettings.TurnOn = true;
        obj.TrayApplicationBehaviourSettings.ContextMenuSettings.DisplayShowInTaskbarMenuItem = false;
        obj.TrayApplicationBehaviourSettings.FontSettings.FontSize = 9;
    }
}

Listing 5.1.10.2 Set FontSettings via ChoCoreFrx.xml file

Open ChoCoreFrx.xml file, specify the values to each applicable attributes in ‘fontSettings’ element under ‘trayApplicationBehaviourSettings’ element.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <globalApplicationSettings applicationId="WindowlessTrayApp.exe" eventLogSourceName="WindowlessTrayApp.exe">
    <behaviourSettings hideWindow="false" bringWindowToTop="false" alwaysOnTop="false" runAtStartup="false" runOnceAtStartup="false" singleInstanceApp="true" activateFirstInstance="false" showEnvironmentSelectionWnd="true" />
    <trayApplicationBehaviourSettings turnOn="false" showInTaskbar="true" hideMainWindowAtStartup="false" tooltipText="" balloonTipText="Hello World!" hideTrayIconWhenMainWindowShown="false" turnOnMode="OnMinimize">
      <trayIcon>C:\Sample.ico</trayIcon>
      <fontSettings fontName="Helvetica" fontSize="8">
        <fontColor />
      </fontSettings>
      <contextMenuSettings displayAlwaysOnTopMenuItem="true" displayRunAtSystemsStartupMenuItem="true" displayShowInTaskbarMenuItem="true" displayAboutMenuItem="true" displayHelpMenuItem="true" displayExitMenuItem="true" />
    </trayApplicationBehaviourSettings>
  </globalApplicationSettings>
</configuration>

6. Advanced Topics

Now that we learned about configuraring tray application via programmatically as well as configuration file. In this section we dive into more advanced approach to customize your tray application programatically, something seldom used.

6.1 AfterNotifyIconConstructed() Method

When implemented in a derived class, executes after the ChoNotifyIcon object constructed by the framework. In this method, you can manipulate most of the ChoNotifyIcon properties based on your neeeds. Please refer ChoNotidyIcon class help for more information. Besides standard properties exposed via this object, it also provides some additional helper methods to use. 

Listing 6.1.1 AfterNotifyIconContructed() sample

[ChoApplicationHost]
public class ChoAppHost : ChoApplicationHost
{
    protected override void ApplyGlobalApplicationSettingsOverrides(ChoGlobalApplicationSettings obj)
    {
        obj.TrayApplicationBehaviourSettings.TurnOn = true;
    }

    public override object MainWindowObject
    {
        get
        {
            return new MainWindow();
        }
    }

    protected override void AfterNotifyIconConstructed(Cinchoo.Core.Windows.Forms.ChoNotifyIcon ni)
    {
        ni.Text = "AfterConstruct";
        ni.ShowText("R");
    }
}

6.2 Running in Console Mode

The applications created using Cinchoo framework can be run as either Console, Windows, Tray or Service applications. Unified interface to switch application from one mode to another. After successful development of tray application, pass /#AM:Console command line argument in order to run your tray application as console mode.

Listing 6.2.1 Run tray application in console mode

>HelloWorldTrayApp /#AM:Console

7. ChoNotifyIcon Class

Specifies a component that creates an icon in the notification area. This class cannot be inherited. Icons in the notification area are shortcuts to processes that are running in the background of a computer, such as a virus protection program or a volume control. These processes do not come with their own user interfaces. The NotifyIcon class provides a way to program in this functionality. The Icon property defines the icon that appears in the notification area. Pop-up menus for an icon are addressed with the ContextMenu property. ChoNotifyIcon provides property notification to all changes.

7.1 Methods

7.1.1 ShowText() Overloads

Showing text instead of icon in the tray.

7.1.2 SetAnimationClip() Overloads

Sets the animation clip that will be displayed in the tray. After setting the clips, you can control the animation by calling StartAnimation and StopAnimation methods.

7.1.3 ShowBalloonTip() Overloads

Displays a balloon tip with the specified title, text, and icon in the taskbar for the specified time period.

7.1.4 StartAnimation() Method

Start showing the animation. This needs to be called after setting the clip using SetAnimationClip() methods.

7.1.5 StopAnimation() Method

Stop animation started with StartAnimation method.

7.2 Properties

7.2.1 BalloonTipText Property

Gets or sets the text to display on the balloon tip associated with the ChoNotifyIcon.

7.2.2 BalloonTipTitle Property

Gets or sets the title of the balloon tip displayed on theChoNotifyIcon.

7.2.3 ContextMenu Property

Gets or sets the shortcut menu associated with the theChoNotifyIcon.

7.2.4 ContextMenu Property

Gets or sets the shortcut menu for the icon.

7.2.5 ContextMenuStrip Property

Gets or sets the shortcut menu associated with the ChoNotifyIcon.

7.2.6 Icon Property

Gets or sets the current icon..

7.2.7 Tag Property

Gets or sets an object that contains data about the ChoNotifyIcon.

7.2.8 Text Property

Gets or sets the ToolTip text displayed when the mouse pointer rests on a  notification area icon.

7.2.9 Visible Property

Gets or sets a value indicating whether the icon is visible in the notification area of the taskbar..

7.3 Events

7.3.1 BalloonTipClicked Event

Occurs when the balloon tip is clicked.

7.3.2 BalloonTipClosed Event

Occurs when the balloon tip is closed by the user.

7.3.3 BalloonTipShown Event

Occurs when the balloon tip is displayed on the screen.

7.3.4 Click Event

Occurs when the user clicks the icon in the notification area.

7.3.5 DoubleClick Event

Occurs when the user double-clicks the icon in the notification area of the taskbar.

7.3.6 MouseClick Event

Occurs when the user clicks a System.Windows.Forms.NotifyIcon with the mouse.

7.3.7 MouseDoubleClick Event

Occurs when the user double-clicks the System.Windows.Forms.NotifyIcon with the mouse.

7.3.8 MouseDown Event

Occurs when the user presses the mouse button while the pointer is over the icon in the notification area of the taskbar.

7.3.9 MouseMove Event

Occurs when the user moves the mouse while the pointer is over the icon in the notification area of the taskbar.

7.3.8 MouseUp Event

Occurs when the user releases the mouse button while the pointer is over the icon in the notification area of the taskbar.

Cinchoo – Simplified Windows Service Development

 Download Sample.zip

Contents

1. Introduction

Cinchoo is the application framework for .NET. One of the main functionalities it provides to the users is application configuration management. Application configuration is the information that application reads and/or writes at run-time from the source.

One another important feature it offers to the developers community is the unified, generic application host to build and run application in different modes. Code once, run the application as either Console, Windows, WPF, Windows Service or Windows Tray application.

In this article, I’m going to illustrate how to use this library to create Windows Service application. It is very simple and easy. Making it as library letting you to concentrate yourself on the core development tasks. Cinchoo provides a clean and easy API to develop and run Windows Service applications. It allows you

  1. Self installable service application
  2. Install and run multi-instances of services
  3. Able to run as Console application for ease of debugging
  4. Pass service arguments via command line
  5. Arguments are saved and used when service is automatically started

2. Requirement

The application host library is written in C# for the .NET 4.0 Framework. It is part of Cinchoo framework, which is a great library with lot of features like Configuration Management, common ApplicationHost, Shell features etc.

3. “Hello World!” Sample

Lets begin by looking into a simple example of a windows service application printing ‘Hello World!’ message into log file.

Download the latest Cinchoo binary here. (Nuget Command: Install-Package Cinchoo)

  • Open VS.NET 2010 or higher
  • Create a sample VS.NET (.NET Framework 4) Console Application project
  • Add reference to Cinchoo.Core.dll
  • Use the Cinchoo.Core namespace
  • Copy and paste the below application host object

Listing 3.1 Defining ApplicationHost object

[ChoApplicationHost]
public class HelloWorldAppHost : ChoApplicationHost
{
    protected override void OnStart(string[] args)
    {
        ChoProfile.WriteLine("Hello world!");
        base.OnStart(args);
    }
}

The code above illustrates about defining ApplicationHost object. First thing define a ApplicationHost (ex. HelloWorldAppHost) class from ChoApplicationHost, it indicates that this object is a ApplicationHost object. And it must be decorated with ChoApplicationHostAttribute to complete the definition. In this example, we override OnStart method with printing “Hello World!” message to log file.

Listing 3.2 Main Method

class Program
{
    static void Main(string[] args)
    {
        ChoApplication.Run(args);
    }
}

We start by calling ChoApplication.Run() method in the main entry of the application. That all. All the heavy lifting of installing, running the application as service is done by the library under the hood. Now your application is service enabled application. It can also be run as Console Application for easy debugging the service application.

Listed below are the steps to install, uninstall, start and stop the service

Listing 3.3 Install HelloWorld.exe service

>HelloWorld.exe /@I
HelloWorld [Version 1.0.0.0]
Copyright c  2014

[SC] CreateService SUCCESS

[SC] ChangeServiceConfig SUCCESS

Listing 3.4 Start HelloWorld.exe service

>HelloWorld.exe /@S
HelloWorld [Version 1.0.0.0]
Copyright c  2014

Listing 3.5 Stop HelloWorld.exe service

>HelloWorld.exe /@T
HelloWorld [Version 1.0.0.0]
Copyright c  2014

Listing 3.6 Uninstall HelloWorld.exe service

>HelloWorld.exe /@I
HelloWorld [Version 1.0.0.0]
Copyright c  2014

[SC] DeleteService SUCCESS

Listing 3.7 HelloWorld.exe with /@? argument

C:\Personal\WindowsService.Test\HelloWorld\bin\Debug>HelloWorld.exe /@?
HelloWorld [Version 1.0.0.0]
Copyright c  2014

HELLOWORLD [/@SN:<string>] [/@SD:<string>] [/@I] [/@U] [/@S] [/@T] [/@P] [/@C] [
/@E:<int>] [/@SP:<string>]

        /@SN    Service Name.
        /@SD    Service Description.
        /@I     Install Service.
        /@U     Uninstall Service.
        /@S     Start Service.
        /@T     Stop Service.
        /@P     Pause Service.
        /@C     Continue Service.
        /@E     Execute Command.
        /@SP    Command Line Parameters.

‘@?’ argument will print out the service level arguments accepted by HelloWorld application.

Listing 3.8 Install HelloWorld.exe service with ‘Test1′ name

>HelloWorld.exe /@I /@SN:Test1
HelloWorld [Version 1.0.0.0]
Copyright c  2014

[SC] DeleteService SUCCESS

Listing 3.9 Run HelloWorld.exe as console application

>HelloWorld.exe
HelloWorld [Version 1.0.0.0]
Copyright c  2014

        Hello world!

4. Defining Application Host Object

4.1 ChoApplicationHostAttribute

All application host objects must be decorated by ChoApplicationHostAttribute. An application must have atleast one application host object defined. Cinchoo framework will discover them at the application startup.

4.2 ChoApplicationHost

All application host objects must be derived from ChoApplicationHost class. Override nessasary overrides in them. An application must have one application host object defined either in the entry assembly or referenced assemblies. In this object, you can override number of methods for your needs.

4.2.1 OnStart() Method

When implemented in a derived class, executes when a Start command is sent to the service by the Service Control Manager (SCM) or when the operating system starts (for a service that starts automatically) or when the application passed with ‘/@S’ switch or when application starts as console application.

Use OnStart to specify the processing that occurs when the service receives a Start command. OnStart is the method in which you specify the behavior of the service. OnStart can take arguments as a way to pass data.

Do not use the constructor to perform processing that should be in OnStart. Use OnStart to handle all initialization of your service. The constructor is called when the application’s executable runs, not when the service runs. The executable runs before OnStart. When you continue, for example, the constructor is not called again because the SCM already holds the object in memory. If OnStop releases resources allocated in the constructor rather than in OnStart, the needed resources would not be created again the second time the service is called.

Process initialization arguments for the service in the OnStart method, not in the Main method. The arguments in the args parameter array can be set manually in the properties window for the service in the Services console or can be passed via command line argument with ‘/@SP’ switch.

4.2.2 OnStop() Method

When implemented in a derived class, executes when a Stop command is sent to the service by the Service Control Manager (SCM) or when the application passed with ‘/@T’ switch. Specifies actions to take when a service stops running. When implemented in a derived class, executes when a Stop command is sent to the service by the Service Control Manager (SCM). Specifies actions to take when a service stops running.

When the SCM receives a Stop command, it uses the value of ChoServiceInstallerSettings.CanStop to verify whether the service accepts Stop commands. If ChoServiceInstallerSettings.CanStop is true, the Stop command is passed to the service, and the OnStop method is called if it is defined. If OnStop is not implemented in the service, the SCM handles the Stop command. If ChoServiceInstallerSettings.CanStop is false, the SCM ignores the Stop command. 

4.2.3 OnContinue() Method

When implemented in a derived class, OnContinue runs when a Continue command is sent to the service by the Service Control Manager (SCM) or when the application passed with ‘/@C’ switch. Specifies actions to take when a service resumes normal functioning after being paused.

Implement OnContinue to mirror your application’s response to OnPause. When you continue the service (either through the Services console or programmatically), the OnContinue processing runs, and the service becomes active again.

OnContinue is expected to be overridden when the ChoServiceInstallerSettings.CanPauseAndContinue property is true. If ChoServiceInstallerSettings.CanPauseAndContinue is false, the SCM will not pass Pause or Continue requests to the service, so the OnPause and OnContinue methods will not be called even if they are implemented. In the SCM, the Pause and Continue controls are disabled when ChoServiceInstallerSettings.CanPauseAndContinue is false.

4.2.4 OnPause() Method

When implemented in a derived class, executes when a Pause command is sent to the service by the Service Control Manager (SCM) or when the application passed with ‘/@P’ switch. Specifies actions to take when a service pauses.

Use OnPause to specify the processing that occurs when the service receives a Pause command. OnPause is expected to be overridden when the ChoServiceInstallerSettings.CanPauseAndContinue property is true. When you continue a paused service (either through the Services console or programmatically via ‘/@P’ switch), the OnContinue processing is run, and the service becomes active again.

Sending a Pause request to the service can conserve system resources because Pause need not release all system resources. For example, if threads have been opened by the process, pausing a service rather than stopping it can allow the threads to remain open, obviating the need to reallocate them when the service continues. If you define Pause to release all system resources, it behaves like a Stop command.

Set ChoServiceInstallerSettings.CanPauseAndContinue to true, and override OnPause and OnContinue to specify the processing that should occur when the SCM passes a Pause or Continue request to your service. OnContinue should be implemented to undo the processing in OnPause.

If ChoServiceInstallerSettings.CanPauseAndContinue is false, the SCM will not pass Pause or Continue requests to the service, so the OnPause and OnContinue methods will not be called even if implemented. In the SCM, the Pause and Continue controls are disabled when ChoServiceInstallerSettings.CanPauseAndContinue is false.

4.2.5 OnPowerEvent() Method

When implemented in a derived class, executes when the computer’s power status has changed. This applies to laptop computers when they go into suspended mode, which is not the same as a system shutdown.

Use OnPowerEvent to specify the processing that occurs when the system event indicated in the System.ServiceProcess.PowerBroadcastStatus enumeration occurs–for example, when the computer is placed in suspended mode or indicates low battery power.

OnPowerEvent is expected to be overridden when the ChoServiceInstallerSettings.CanHandlePowerEvent property is true.

4.2.6 OnShutdown() Method

When implemented in a derived class, executes when the system is shutting down. Specifies what should occur immediately prior to the system shutting down.

Use OnShutdown to specify the processing that occurs when the system shuts down. This event occurs only when the operating system is shut down, not when the computer is turned off. OnShutdown is expected to be overridden when the ChoServiceInstallerSettings.CanShutdown property is true.

4.2.7 OnCustomCommand() Method

When implemented in a derived class, OnCustomCommand executes when the Service Control Manager (SCM) passes a custom command to the service or when the application passed with ‘/@E’ switch . Specifies actions to take when a command with the specified parameter value occurs.

OnCustomCommand lets you specify additional functionality beyond starting, stopping, pausing and continuing services. The SCM does not examine the custom command to verify whether the service supports the command parameter passed in. It passes the custom command directly to the service. If the service does not recognize the command parameter, it does nothing.

Custom commands are raised by an ExecuteCommand statement in a ServiceController component or by passing command through ‘/@E’ command line switch. Use a switch statement or if..then condition to handle the custom commands you define on your service. The only values for a custom command that you can define in your application or use in OnCustomCommand are those between 128 and 255. Integers below 128 correspond to system-reserved values.

If the ChoServiceInstallerSettings.AutoLog property is true, custom commands, like all other commands, write entries to the event log to report whether the method execution succeeded or failed.

4.2.8 OnSessionChange() Method

Executes when a change event is received from a Terminal Server session. You must set the ChoServiceInstallerSettings.CanHandleSessionChangeEvent property to true to enable the execution of this method.

4.2.9 RequestAdditionalTime() Method

Requests additional time for a pending operation. The RequestAdditionalTime method is intended to be called by the overridden OnContinue, OnPause, OnStart, or OnStop methods to request additional time for a pending operation, to prevent the Service Control Manager (SCM) from marking the service as not responding. If the pending operation is not a continue, pause, start, or stop, an InvalidOperationException is thrown.

4.2.10 ApplyGlobalApplicationSettingsOverrides() Method

When implemented in a derived class, ApplyGlobalApplicationSettingsOverrides executes when the framework initializes. In here you can overrides ChoGlobalApplicationSettings members.

4.2.11 ApplyAppFrxSettingsOverrides() Method

When implemented in a derived class, ApplyAppFrxSettingsOverrides executes when the framework initializes. In here you can overrides ChoAppFrxSettings members.

4.2.12 ApplyMetaDataFilePathSettingsOverrides() Method

When implemented in a derived class, ApplyMetaDataFilePathSettingsOverrides executes when the framework initializes. In here you can overrides ChoMetaDataFilePathSettings members.

4.2.13 BeforeInstall() Method

When implemented in a derived class, BeforeInstall() method executes before the service install happens Note that this action is only executed if the service is being installed.

4.2.14 AfterInstall() Method

When implemented in a derived class, AfterInstall() method executes after the service install happens Note that this action is only executed if the service is being installed.

4.2.15 BeforeUninstall() Method

When implemented in a derived class, BeforeUninstall() method executes before the service uninstall happens Note that this action is only executed if the service is being uninstalled.

4.2.16 AfterUninstall() Method

When implemented in a derived class, AfterUninstall() method executes after the service uninstall happens Note that this action is only executed if the service is being uninstalled.

5. Managing Services

Using Cinchoo framework for developing windows service application provides many options via command line arguments to self-install, execute and control the service application.

5.1 Install Service

If you’re developing a Windows Service by using the .NET Framework, you usually install your service applications by using a command-line utility called InstallUtil.exe. You do not need this utility anymore when using Cinchoo framework for your service development. These services are self-installable.

5.1.1 Install Single Instance Service

By default, a service application developed using Cinchoo framework are single instance service application. It means that you can install and run atmost one instance of it at any time. By passing ‘/@I’ command line argument to service executable will install the service with the executable name as service name. For the sample below, the ‘HelloWorld’ service will be installed in your machine.

Listing 5.1.1.1 Install service

>HelloWorld.exe /@I
HelloWorld [Version 1.0.0.0]
Copyright c  2014

[SC] CreateService SUCCESS

[SC] ChangeServiceConfig SUCCESS

5.1.2 Install Multi Instance Service

Sometime you may want to install multiple services with different names. You can do so by turning off ‘SingleInstanceApp’ flag in the ChoGlobalApplicationSettings class. Please visit the below url on how to turn off this option in the application.

Cinchoo – Allow multi-instance application

The below sample shows how to install a service with a ‘TestService’ name by using ‘/@SN’ command line switch.

Listing 5.1.2.1 Install ‘TestService’ service

>HelloWorld.exe /@I /@SN:TestService
HelloWorld [Version 1.0.0.0]
Copyright c  2014

[SC] CreateService SUCCESS

[SC] ChangeServiceConfig SUCCESS

5.1.2 Uninstall Service

To uninstall a service, you must use ‘/@U’ command line switch. The below samples shows how to uninstall a service

Listing 5.1.2.1 Uninstall service

>HelloWorld.exe /@U
HelloWorld [Version 1.0.0.0]
Copyright c  2014

[SC] DeleteService SUCCESS

Listing 5.1.2.2 Uninstall ‘TestService’ service

>HelloWorld.exe /@U /@SN:TestService
HelloWorld [Version 1.0.0.0]
Copyright c  2014

[SC] DeleteService SUCCESS

5.1.3 Start Service

To start a service, you must use ‘/@S’ command line switch. The below samples shows how to start a service

Listing 5.1.3.1 Start service

>HelloWorld.exe /@S
HelloWorld [Version 1.0.0.0]
Copyright c  2014

[SC] DeleteService SUCCESS

Listing 5.1.3.2 Start ‘TestService’ service

>HelloWorld.exe /@S /@SN:TestService
HelloWorld [Version 1.0.0.0]
Copyright c  2014

5.1.3 Stop Service

To stop a service, you must use ‘/@T’ command line switch. SCM uses the value of ChoServiceInstallerSettings.CanStop to verify whether the service accepts Stop commands. If ChoServiceInstallerSettings.CanStop is true, the Stop command is passed to the service, and the OnStop method is called if it is defined. If OnStop is not implemented in the service, the SCM handles the Stop command. If ChoServiceInstallerSettings.CanStop is false, the SCM ignores the Stop command. 

The below samples shows how to stop a service

Listing 5.1.3.1 Stop service

>HelloWorld.exe /@S
HelloWorld [Version 1.0.0.0]
Copyright c  2014

[SC] DeleteService SUCCESS

Listing 5.1.3.2 Stop ‘TestService’ service

>HelloWorld.exe /@S /@SN:TestService
HelloWorld [Version 1.0.0.0]
Copyright c  2014

5.1.4 Continue Service

To continue a service, you must use ‘/@C’ command line switch.

SCM uses the value of ChoServiceInstallerSettings.CanPauseAndContinue to verify whether the service accepts Continue commands. If ChoServiceInstallerSettings.CanPauseAndContinue is true, the Continue command is passed to the service, and the OnContinue method is called if it is defined. If OnContinue is not implemented in the service, the SCM handles the Continue command. If ChoServiceInstallerSettings.CanPauseAndContinue is false, the SCM ignores the Continue command. 

The below samples shows how to continue a service

Listing 5.1.4.1 Continue service

>HelloWorld.exe /@C
HelloWorld [Version 1.0.0.0]
Copyright c  2014

[SC] DeleteService SUCCESS

Listing 5.1.4.2 Continue ‘TestService’ service

>HelloWorld.exe /@C /@SN:TestService
HelloWorld [Version 1.0.0.0]
Copyright c  2014

5.1.5 Pause Service

To pause a service, you must use ‘/@P’ command line switch.

SCM uses the value of ChoServiceInstallerSettings.CanPauseAndContinue to verify whether the service accepts Pause commands. If ChoServiceInstallerSettings.CanPauseAndContinue is true, the Pause command is passed to the service, and the OnPause method is called if it is defined. If OnPause is not implemented in the service, the SCM handles the Pause command. If ChoServiceInstallerSettings.CanPauseAndContinue is false, the SCM ignores the Pause command. 

The below samples shows how to pause a service

Listing 5.1.5.1 Continue service

>HelloWorld.exe /@C
HelloWorld [Version 1.0.0.0]
Copyright c  2014

[SC] DeleteService SUCCESS

Listing 5.1.5.2 Continue ‘TestService’ service

>HelloWorld.exe /@C /@SN:TestService
HelloWorld [Version 1.0.0.0]
Copyright c  2014

5.1.5 Execute Custom Command

To execute a custom command, you must use ‘/@E’ command line switch with command identifier (integer).

The only values for a custom command that you can define in your application or use in OnCustomCommand are those between 128 and 255. Integers below 128 correspond to system-reserved values.

The below samples shows how to pause a service

Listing 5.1.5.1 Execute Custom Command

>HelloWorld.exe /@E:230
HelloWorld [Version 1.0.0.0]
Copyright c  2014

[SC] DeleteService SUCCESS

Listing 5.1.5.2 Execute Custom Command on ‘TestService’ service

>HelloWorld.exe /@E:230 /@SN:TestService
HelloWorld [Version 1.0.0.0]
Copyright c  2014

5.1.6 Service Arguments

Of course every application needs some arguments to run against occasionally to initialize the process. Windows services are indeed no exceptions. There are two different types of arguments you can define for the services

  • Permanent
  • One-Time

The arguments can be passed to the services using /@SP’ command line switch. There arguments are passed to OnStart() method by the infrastructure automatically.

Arguments passed while installing services will be saved permanently in the registry. All the subsequent runs, these parameters are retrived by SCM and passed to service OnStart() automatically.

Listing 5.1.6.1 Permanent Service Arguments

>HelloWorld.exe /@I /@SP:"/name:Tom /msg:Hello"
HelloWorld [Version 1.0.0.0]
Copyright c  2014

[SC] CreateService SUCCESS

[SC] ChangeServiceConfig SUCCESS

Sometime you may want to override the arguments when starting the services. You can do so by passing the arguments in number of ways

  • By passing arguments via command line when starting service
  • The arguments in the args parameter array can be set manually in the properties window for the service in the Services console.

In the sample below, the arguments passed while starting the service will be a one-time basis arguments.

Listing 5.1.5.2 One-Time service arguments

>HelloWorld.exe /@S /@SP:"/name:Tom /msg:Hello"
HelloWorld [Version 1.0.0.0]
Copyright c  2014

6. Application Host Discovery

To successfully deploy your Windows Service application, you must understand how the Cinchoo framework locates and loads the Application Host object that make up your application. By default, Cinchoo framework attempts to locates and loads Application Host object from entry assembly. This default behavior can be overridden by configuration file settings.

Cinchoo framework performs a number of steps when attempting to locate Application Host object.

  1. Examining the App.Cofig/Web.Config file
  2. Locate the ApplicationHost object in Entry assembly
  3. Locate first discovered ApplicationHost object in the referenced assemblies.

6.1 Examine in App.Config/Web.Config file

Cinchoo framework checks the application configuration file for information that overrides the application host object information. The following code provides a example of specifying application host information in the application configuration file

Listing 6.1.1 App.Config file

<?xml version="1.0"?>
<configuration>
  <configSections>
    <section name="appFrxSettings" type="Cinchoo.Core.ChoAppFrxSettings, Cinchoo.Core" />
  </configSections>
  <appFrxSettings appEnvironment="" appFrxFilePath="" applicationHostType="HelloWorldApp.TestAppHost, HelloWorld" />
</configuration>

6.2 Locate in Entry Assembly

Cinchoo framework checks for the application host object defined in the entry assembly when there is no type specified in the configuration file.

6.3 Locate first discovered ApplicationHost object

Cinchoo framework scans all the referenced assemblies for the application host object defined in them and use the first discovered such object to manage the Windows Service. This step happens when the framework failed to locate the application host object from previous steps.

7. Customization

There are ways to customize your windows service application, like service start mode, service identify, custom install actions, service dependencies etc. The default settings should be good enough for most applications, but they are there for your customizations if you need to.

Customization can be done in couple of ways

  • Programmatically
  • Configuration file

7.1 Configuration File

All the customizable parameters are defined in the ChoServiceInstallerSettings.xml file under Config folder.

Below is the sample contents of the configuration file

Listing 7.1.1 ChoServiceInstallerSettings.xml file

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <serviceInstallerSettings serviceName="" serviceStartMode="Automatic" depends="" account="LocalSystem" userName="" password="" timeoutInTicks="-1" canHandlePowerEvent="false" canHandleSessionChangeEvent="false" canPauseAndContinue="false" canShutdown="true" canStop="true" autoLog="false" exitCode="0">
    <description />
    <parameters><![CDATA[]]></parameters>
    <recoverySettings resetFailedCounterAfter="0">
      <rebootMessage><![CDATA[]]></rebootMessage>
      <command><![CDATA[]]></command>
      <actions><![CDATA[]]></actions>
    </recoverySettings>
  </serviceInstallerSettings>
</configuration>

7.2 Programmatically

This is one another way to customize your windows service application programmatically.

Here is the sample on how to do

Listing 7.1.1 Override Service Installer Settings

[ChoApplicationHost]
public class AppHost : ChoApplicationHost
{
    protected override void OnStart(string[] args)
    {
        Console.WriteLine("OnStart");
        base.OnStart(args);
    }

    protected override void ApplyServiceParametersOverrides(ChoServiceInstallerSettings obj)
    {
        //Customize the parameters
    }
}

8. Configuration

Once Cinchoo framework is added to your project for your services development, you can configure the services using framework API or through configuration file. In this section, will talk about most common configuration parameters used by services and how you can manipulate them via API/Configration file.

8.1 Service Configuration

8.1.1 Service Name

Specify the base name of the service, as it is registered in the services control manager. This setting is optional and by default uses entry assembly (executable) name.

It is recommended that service names does not contains spaces or other whitespace characters.

Listing 8.1.1.1 Set ServiceName programmatically

[ChoApplicationHost]
public class AppHost : ChoApplicationHost
{
    protected override void OnStart(string[] args)
    {
        Console.WriteLine("OnStart");
        base.OnStart(args);
    }

    protected override void ApplyServiceParametersOverrides(ChoServiceInstallerSettings obj)
    {
        obj.ServiceName = "TestService";
    }
}

Listing 8.1.1.2 Set ServiceName via ChoServiceInstallerSettings.xml file

Open ChoServiceInstallerSettings.xml file, specify the service name in ‘serviceName’ attribute.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <serviceInstallerSettings serviceName="TestService" serviceStartMode="Automatic" depends="" account="LocalSystem" userName="" password="" timeoutInTicks="-1" canHandlePowerEvent="false" canHandleSessionChangeEvent="false" canPauseAndContinue="false" canShutdown="true" canStop="true" autoLog="false" exitCode="0">
    <description />
    <parameters><![CDATA[]]></parameters>
    <recoverySettings resetFailedCounterAfter="0">
      <rebootMessage><![CDATA[]]></rebootMessage>
      <command><![CDATA[]]></command>
      <actions><![CDATA[]]></actions>
    </recoverySettings>
  </serviceInstallerSettings>
</configuration>

Listing 8.1.1.3 Set ServiceName via command line option

>HelloWorld.exe /@I /@SN:TestService

The order of taking the service names are as follows,

  1. Command line option (/@SN)
  2. Programmatically
  3. ChoServiceInstallerSettings.xml file

Each service on the system must have a unique name. If you need to run multiple instances of the same service, consider using the InstanceName command-line option when installing the service.

8.1.2 Display Name

Specify the display name of the service in the services control manager. This setting is optional and defaults to the service name.

Listing 8.1.2.1 Set DisplayName programmatically

[ChoApplicationHost]
public class AppHost : ChoApplicationHost
{
    protected override void OnStart(string[] args)
    {
        Console.WriteLine("OnStart");
        base.OnStart(args);
    }

    protected override void ApplyServiceParametersOverrides(ChoServiceInstallerSettings obj)
    {
        obj.DisplayName = "TestServiceDisplayName";
    }
}

Listing 8.1.2.2 Set DisplayName via command line option

>HelloWorld.exe /@I /@DN:TestServiceDisplayName

The order of taking the display names are as follows,

  1. Command line option (/@DN)
  2. Programmatically

8.1.3 Instance Name

Specify the instance name of the service. It is combined with the service name and seperated by $. This setting is optional and only added if specified.

Listing 8.1.3.1 Set InstanceName programmatically

[ChoApplicationHost]
public class AppHost : ChoApplicationHost
{
    protected override void OnStart(string[] args)
    {
        Console.WriteLine("OnStart");
        base.OnStart(args);
    }

    protected override void ApplyServiceParametersOverrides(ChoServiceInstallerSettings obj)
    {
        obj.InstanceName = "instance1";
    }
}

Listing 8.1.3.2 Set InstanceName via command line option

>HelloWorld.exe /@I /@IN:instance1

The order of taking the display names are as follows,

  1. Command line option (/@IN)
  2. Programmatically

This option is typically set to run multiple instances of the same service.

8.1.4 Service Description

Specify the description of the service in the services control manager. This is optional and defaults to the service name.

Listing 8.1.4.1 Set ServiceDescription programmatically

[ChoApplicationHost]
public class AppHost : ChoApplicationHost
{
    protected override void OnStart(string[] args)
    {
        Console.WriteLine("OnStart");
        base.OnStart(args);
    }

    protected override void ApplyServiceParametersOverrides(ChoServiceInstallerSettings obj)
    {
        obj.ServiceDescription = "TestService Description";
    }
}

Listing 8.1.4.2 Set ServiceDescription via ChoServiceInstallerSettings.xml file

Open ChoServiceInstallerSettings.xml file, specify the service description in ‘description’ element.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <serviceInstallerSettings serviceName="TestService" serviceStartMode="Automatic" depends="" account="LocalSystem" userName="" password="" timeoutInTicks="-1" canHandlePowerEvent="false" canHandleSessionChangeEvent="false" canPauseAndContinue="false" canShutdown="true" canStop="true" autoLog="false" exitCode="0">
    <description>TestService Description</description>
    <parameters><![CDATA[]]></parameters>
    <recoverySettings resetFailedCounterAfter="0">
      <rebootMessage><![CDATA[]]></rebootMessage>
      <command><![CDATA[]]></command>
      <actions><![CDATA[]]></actions>
    </recoverySettings>
  </serviceInstallerSettings>
</configuration>

Listing 8.1.4.3 Set ServiceDescription via command line option

>HelloWorld.exe /@I /@SD:"TestService Description"

The order of taking the service description are as follows,

  1. Command line option (/@SD)
  2. Programmatically
  3. ChoServiceInstallerSettings.xml file

8.2 Service Start Modes

There are multiple service start modes, each of which can be specified by the configuration. This option is only used if the service is being installed.

  1. DelayedAutomatic
  2. Automatic
  3. Manual
  4. Disabled

Listing 8.2.1 Set ServiceStartMode via ChoServiceInstallerSettings.xml file

Open ChoServiceInstallerSettings.xml file, specify the mode in ‘serviceStartMode’ attribute.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <serviceInstallerSettings serviceName="TestService" serviceStartMode="Automatic" depends="" account="LocalSystem" userName="" password="" timeoutInTicks="-1" canHandlePowerEvent="false" canHandleSessionChangeEvent="false" canPauseAndContinue="false" canShutdown="true" canStop="true" autoLog="false" exitCode="0">
    <description>TestService Description</description>
    <parameters><![CDATA[]]></parameters>
    <recoverySettings resetFailedCounterAfter="0">
      <rebootMessage><![CDATA[]]></rebootMessage>
      <command><![CDATA[]]></command>
      <actions><![CDATA[]]></actions>
    </recoverySettings>
  </serviceInstallerSettings>
</configuration>

8.3 Service Identity

Services can be configured to run as a number of different identities, using the configuration option that is most appropriate.

Possible identies are

  1. Network Service
  2. Local System (Default)
  3. Local Service
  4. User

8.3.1 Network Service Account

Runs the service using the NETWORK_SERVICE built-in account. The network service account is a predefined local account used by the SCM. A service that runs in the context of the NetworkService account presents the computer’s credentials to remote servers. You can set the service to run on this account as below

Listing 8.3.1.1 Set Identity as Network Service account programmatically

[ChoApplicationHost]
public class AppHost : ChoApplicationHost
{
    protected override void OnStart(string[] args)
    {
        Console.WriteLine("OnStart");
        base.OnStart(args);
    }

    protected override void ApplyServiceParametersOverrides(ChoServiceInstallerSettings obj)
    {
        obj.RunAsNetworkService();
    }
}

Listing 8.3.1.2 Set Identity as Network Service account via ChoServiceInstallerSettings.xml file

Open ChoServiceInstallerSettings.xml file, specify the account as ‘NetworkService’ in ‘account’ attribute.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <serviceInstallerSettings serviceName="TestService" serviceStartMode="Automatic" depends="" account="NetworkService" userName="" password="" timeoutInTicks="-1" canHandlePowerEvent="false" canHandleSessionChangeEvent="false" canPauseAndContinue="false" canShutdown="true" canStop="true" autoLog="false" exitCode="0">
    <description>TestService Description</description>
    <parameters><![CDATA[]]></parameters>
    <recoverySettings resetFailedCounterAfter="0">
      <rebootMessage><![CDATA[]]></rebootMessage>
      <command><![CDATA[]]></command>
      <actions><![CDATA[]]></actions>
    </recoverySettings>
  </serviceInstallerSettings>
</configuration>

8.3.2 Local System Account

Runs the service using the local system account. The LocalSystem account is a predefined local account used by the service control manager.  It has extensive privileges on the local computer, and acts as the computer on the network. A service that runs in the context of the LocalSystem account inherits the security context of the SCM. The account is not associated with any logged-on user account. It is the default account set to when you install a service. You can set the service to run on this account explicitly as below

Listing 8.3.2.1 Set Identity as Local System account programmatically

[ChoApplicationHost]
public class AppHost : ChoApplicationHost
{
    protected override void OnStart(string[] args)
    {
        Console.WriteLine("OnStart");
        base.OnStart(args);
    }

    protected override void ApplyServiceParametersOverrides(ChoServiceInstallerSettings obj)
    {
        obj.RunAsLocalSystem();
    }
}

Listing 8.3.2.2 Set Identity as Local System via ChoServiceInstallerSettings.xml file

Open ChoServiceInstallerSettings.xml file, specify the account as ‘LocalSystem’ in ‘account’ attribute.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <serviceInstallerSettings serviceName="TestService" serviceStartMode="Automatic" depends="" account="LocalSystem" userName="" password="" timeoutInTicks="-1" canHandlePowerEvent="false" canHandleSessionChangeEvent="false" canPauseAndContinue="false" canShutdown="true" canStop="true" autoLog="false" exitCode="0">
    <description>TestService Description</description>
    <parameters><![CDATA[]]></parameters>
    <recoverySettings resetFailedCounterAfter="0">
      <rebootMessage><![CDATA[]]></rebootMessage>
      <command><![CDATA[]]></command>
      <actions><![CDATA[]]></actions>
    </recoverySettings>
  </serviceInstallerSettings>
</configuration>

8.3.3 Local Service Account

Runs the service using the local service account. The LocalService account is a predefined local account used by the service control manager.  It has minimum privileges on the local computer and presents anonymous credentials on the network.

Listing 8.3.3.1 Set Identity as Local System account programmatically

[ChoApplicationHost]
public class AppHost : ChoApplicationHost
{
    protected override void OnStart(string[] args)
    {
        Console.WriteLine("OnStart");
        base.OnStart(args);
    }

    protected override void ApplyServiceParametersOverrides(ChoServiceInstallerSettings obj)
    {
        obj.RunAsLocalSystem();
    }
}

Listing 8.3.3.2 Set Identity as Local System via ChoServiceInstallerSettings.xml file

Open ChoServiceInstallerSettings.xml file, specify the account as ‘LocalService’ in ‘account’ attribute.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <serviceInstallerSettings serviceName="TestService" serviceStartMode="Automatic" depends="" account="LocalSystem" userName="" password="" timeoutInTicks="-1" canHandlePowerEvent="false" canHandleSessionChangeEvent="false" canPauseAndContinue="false" canShutdown="true" canStop="true" autoLog="false" exitCode="0">
    <description>TestService Description</description>
    <parameters><![CDATA[]]></parameters>
    <recoverySettings resetFailedCounterAfter="0">
      <rebootMessage><![CDATA[]]></rebootMessage>
      <command><![CDATA[]]></command>
      <actions><![CDATA[]]></actions>
    </recoverySettings>
  </serviceInstallerSettings>
</configuration>

8.3.4 User Account

Runs the service using the specified username and password. User name may be specified either as “<domain>\<username>” or  “<username>”. It can be configured as follows

Listing 8.3.4.1 Set Identity as User account programmatically

[ChoApplicationHost]
public class AppHost : ChoApplicationHost
{
    protected override void OnStart(string[] args)
    {
        Console.WriteLine("OnStart");
        base.OnStart(args);
    }

    protected override void ApplyServiceParametersOverrides(ChoServiceInstallerSettings obj)
    {
        obj.RunAsUser("DOMAIN\XBBA123", "passwd");
    }
}

Listing 8.3.4.2 Set Identity as User Account via ChoServiceInstallerSettings.xml file

Open ChoServiceInstallerSettings.xml file, specify the account as ‘User’ in ‘account’ attribute. Then specify the username and password in the corresponding ‘userName’ and ‘password’ attributes. Keeping password as plain text in the configuration file is not a safe.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <serviceInstallerSettings serviceName="TestService" serviceStartMode="Automatic" depends="" account="User" userName="DOMAIN\XBBA123" password="passwd" timeoutInTicks="-1" canHandlePowerEvent="false" canHandleSessionChangeEvent="false" canPauseAndContinue="false" canShutdown="true" canStop="true" autoLog="false" exitCode="0">
    <description>TestService Description</description>
    <parameters><![CDATA[]]></parameters>
    <recoverySettings resetFailedCounterAfter="0">
      <rebootMessage><![CDATA[]]></rebootMessage>
      <command><![CDATA[]]></command>
      <actions><![CDATA[]]></actions>
    </recoverySettings>
  </serviceInstallerSettings>
</configuration>

8.3.5 Prompt User Account

When the service is installed, the installer will prompt for the username/password combination used to launch the service. User name may be specified either as “<domain>\<username>” or  “<username>”. It can be configured as follows

Listing 8.3.5.1 Set Identity as User account to be prompted programmatically

[ChoApplicationHost]
public class AppHost : ChoApplicationHost
{
    protected override void OnStart(string[] args)
    {
        Console.WriteLine("OnStart");
        base.OnStart(args);
    }

    protected override void ApplyServiceParametersOverrides(ChoServiceInstallerSettings obj)
    {
        obj.RunAsPrompt();
    }
}

Listing 8.3.5.2 Set Identity as User account to be prompted via ChoServiceInstallerSettings.xml file

Open ChoServiceInstallerSettings.xml file, specify the account as ‘User’ in ‘account’ attribute. Then empty the username and password in the corresponding ‘userName’ and ‘password’ attributes.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <serviceInstallerSettings serviceName="TestService" serviceStartMode="Automatic" depends="" account="User" userName="" password="" timeoutInTicks="-1" canHandlePowerEvent="false" canHandleSessionChangeEvent="false" canPauseAndContinue="false" canShutdown="true" canStop="true" autoLog="false" exitCode="0">
    <description>TestService Description</description>
    <parameters><![CDATA[]]></parameters>
    <recoverySettings resetFailedCounterAfter="0">
      <rebootMessage><![CDATA[]]></rebootMessage>
      <command><![CDATA[]]></command>
      <actions><![CDATA[]]></actions>
    </recoverySettings>
  </serviceInstallerSettings>
</configuration>

8.4 Service Dependencies

Specifies the names of services that must start before this service starts. The names are separated by forward slashes (/).  This is managed by the windows services control manager.

Listing 8.4.1 Set Service Depend programmatically

[ChoApplicationHost]
public class AppHost : ChoApplicationHost
{
    protected override void OnStart(string[] args)
    {
        Console.WriteLine("OnStart");
        base.OnStart(args);
    }

    protected override void ApplyServiceParametersOverrides(ChoServiceInstallerSettings obj)
    {
        obj.Depend("MSSQL", "IIS");
    }
}

Listing 8.4.2 Set Service Depend via ChoServiceInstallerSettings.xml file

Open ChoServiceInstallerSettings.xml file, specify the dependent service names in ‘depends’ attribute. The names must be seperated by slashes(/).

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <serviceInstallerSettings serviceName="TestService" serviceStartMode="Automatic" depends="MSSQL/IIS" account="User" userName="" password="" timeoutInTicks="-1" canHandlePowerEvent="false" canHandleSessionChangeEvent="false" canPauseAndContinue="false" canShutdown="true" canStop="true" autoLog="false" exitCode="0">
    <description>TestService Description</description>
    <parameters><![CDATA[]]></parameters>
    <recoverySettings resetFailedCounterAfter="0">
      <rebootMessage><![CDATA[]]></rebootMessage>
      <command><![CDATA[]]></command>
      <actions><![CDATA[]]></actions>
    </recoverySettings>
  </serviceInstallerSettings>
</configuration>

8.5 Advanced Settings

8.5.1 CanPauseAndContinue

Specifies that the service supports pause and continue, allowing the services control manager to pass pause and continue commands to the service.

When a service is paused, it halts what it is doing. When you continue the service (either through the Service Control Manager or programmatically), OnContinue runs. Sending a Pause request to the service can conserve system resources. Pause may not release all system resources, but Stop does. OnPause and OnContinue are often implemented to perform less processing than OnStop and OnStart.

When CanPauseAndContinue is true, override OnPause and OnContinue to specify the processing that should occur when the Service Control Manager (SCM) passes a Pause or Continue request to your service. OnContinue should be implemented to undo the processing in OnPause.

If CanPauseAndContinue is false, the SCM will not pass Pause or Continue requests to the service, so the OnPause and OnContinue methods will not be called even if they are implemented. In the SCM, the Pause and Continue controls are disabled when CanPauseAndContinue is false.

Listing 8.5.1.1 Set CanPauseAndContinue programmatically

[ChoApplicationHost]
public class AppHost : ChoApplicationHost
{
    protected override void OnStart(string[] args)
    {
        Console.WriteLine("OnStart");
        base.OnStart(args);
    }

    protected override void ApplyServiceParametersOverrides(ChoServiceInstallerSettings obj)
    {
        obj.CanPauseAndContinue = true;
    }

    protected override void OnContinue()
    {
    }

    protected override void OnPause()
    {
    }
}

Listing 8.5.1.2 Set CanPauseAndContinue via ChoServiceInstallerSettings.xml file

Open ChoServiceInstallerSettings.xml file, specify the true/false in ‘canPauseAndContinue’ attribute.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <serviceInstallerSettings serviceName="TestService" serviceStartMode="Automatic" depends="MSSQL/IIS" account="User" userName="" password="" timeoutInTicks="-1" canHandlePowerEvent="false" canHandleSessionChangeEvent="false" canPauseAndContinue="true" canShutdown="true" canStop="true" autoLog="false" exitCode="0">
    <description>TestService Description</description>
    <parameters><![CDATA[]]></parameters>
    <recoverySettings resetFailedCounterAfter="0">
      <rebootMessage><![CDATA[]]></rebootMessage>
      <command><![CDATA[]]></command>
      <actions><![CDATA[]]></actions>
    </recoverySettings>
  </serviceInstallerSettings>
</configuration>

8.5.2 CanShutdown

Specifies that the service supports the shutdown service command, allowing the services control manager to quickly shutdown the service. If CanShutdown is true, the service is notified when the system is shutting down. At shutdown, the OnShutdown method is called if it has been implemented in your derived class.

Listing 8.5.2.1 Set CanShutdown programmatically

[ChoApplicationHost]
public class AppHost : ChoApplicationHost
{
    protected override void OnStart(string[] args)
    {
        Console.WriteLine("OnStart");
        base.OnStart(args);
    }

    protected override void ApplyServiceParametersOverrides(ChoServiceInstallerSettings obj)
    {
        obj.CanShutdown = true;
    }

    protected override void OnShutdown()
    {
    }
}

Listing 8.5.2.2 Set CanShutdown via ChoServiceInstallerSettings.xml file

Open ChoServiceInstallerSettings.xml file, specify the true/false in ‘canShutdown’ attribute.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <serviceInstallerSettings serviceName="TestService" serviceStartMode="Automatic" depends="MSSQL/IIS" account="User" userName="" password="" timeoutInTicks="-1" canHandlePowerEvent="false" canHandleSessionChangeEvent="false" canPauseAndContinue="false" canShutdown="true" canStop="true" autoLog="false" exitCode="0">
    <description>TestService Description</description>
    <parameters><![CDATA[]]></parameters>
    <recoverySettings resetFailedCounterAfter="0">
      <rebootMessage><![CDATA[]]></rebootMessage>
      <command><![CDATA[]]></command>
      <actions><![CDATA[]]></actions>
    </recoverySettings>
  </serviceInstallerSettings>
</configuration>

8.5.3 CanHandlePowerEvent

Specifies that the service can handle notifications of computer power status changes. When the computer power status changes, the Service Control Manager (SCM) verifies whether the service accepts power event commands using the value of CanHandlePowerEvent.

If CanHandlePowerEvent is true, the command is passed to the service and the OnPowerEvent method is called if defined. If OnPowerEvent is not implemented in the derived class, the SCM handles the power event through the empty base class ServiceBase.OnPowerEvent method.

Listing 8.5.3.1 Set CanHandlePowerEvent programmatically

[ChoApplicationHost]
public class AppHost : ChoApplicationHost
{
    protected override void OnStart(string[] args)
    {
        Console.WriteLine("OnStart");
        base.OnStart(args);
    }

    protected override void ApplyServiceParametersOverrides(ChoServiceInstallerSettings obj)
    {
        obj.CanHandlePowerEvent = true;
    }

    protected override bool OnPowerEvent(PowerBroadcastStatus powerStatus)
    {
    }
}

Listing 8.5.3.2 Set CanHandlePowerEvent via ChoServiceInstallerSettings.xml file

Open ChoServiceInstallerSettings.xml file, specify the true/false in ‘canHandlePowerEvent’ attribute.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <serviceInstallerSettings serviceName="TestService" serviceStartMode="Automatic" depends="MSSQL/IIS" account="User" userName="" password="" timeoutInTicks="-1" canHandlePowerEvent="true" canHandleSessionChangeEvent="false" canPauseAndContinue="false" canShutdown="true" canStop="true" autoLog="false" exitCode="0">
    <description>TestService Description</description>
    <parameters><![CDATA[]]></parameters>
    <recoverySettings resetFailedCounterAfter="0">
      <rebootMessage><![CDATA[]]></rebootMessage>
      <command><![CDATA[]]></command>
      <actions><![CDATA[]]></actions>
    </recoverySettings>
  </serviceInstallerSettings>
</configuration>

8.5.4 CanHandleSessionChangedEvent

Specifies that the service can handle session change events received from a Terminal Server session.

Listing 8.5.4.1 Set CanHandleSessionChangedEvent programmatically

[ChoApplicationHost]
public class AppHost : ChoApplicationHost
{
    protected override void OnStart(string[] args)
    {
        Console.WriteLine("OnStart");
        base.OnStart(args);
    }

    protected override void ApplyServiceParametersOverrides(ChoServiceInstallerSettings obj)
    {
        obj.CanHandleSessionChangedEvent = true;
    }

    protected override void OnSessionChange(SessionChangeDescription changeDescription)
    {
    }
}

Listing 8.5.4.2 Set CanHandleSessionChangedEvent via ChoServiceInstallerSettings.xml file

Open ChoServiceInstallerSettings.xml file, specify the true/false in ‘canHandleSessionChanged’ attribute.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <serviceInstallerSettings serviceName="TestService" serviceStartMode="Automatic" depends="MSSQL/IIS" account="User" userName="" password="" timeoutInTicks="-1" canHandlePowerEvent="false" canHandleSessionChangeEvent="true" canPauseAndContinue="false" canShutdown="true" canStop="true" autoLog="false" exitCode="0">
    <description>TestService Description</description>
    <parameters><![CDATA[]]></parameters>
    <recoverySettings resetFailedCounterAfter="0">
      <rebootMessage><![CDATA[]]></rebootMessage>
      <command><![CDATA[]]></command>
      <actions><![CDATA[]]></actions>
    </recoverySettings>
  </serviceInstallerSettings>
</configuration>

8.5.5 CanStop

Specifies whether the service can be stopped once it has started. When Stop is called on a service, the Service Control Manager (SCM) verifies whether the service accepts Stop commands using the value of CanStop. For most services, the value of CanStop is true, but some operating system services, for example, do not allow the user to stop them.

If CanStop is true, the Stop command is passed to the service and the OnStop method is called if it is defined. If OnStop is not implemented in the derived class, the SCM handles the Stop command through the empty base class ServiceBase.OnStop method.

Listing 8.5.5.1 Set CanStop programmatically

[ChoApplicationHost]
public class AppHost : ChoApplicationHost
{
    protected override void OnStart(string[] args)
    {
        Console.WriteLine("OnStart");
        base.OnStart(args);
    }

    protected override void ApplyServiceParametersOverrides(ChoServiceInstallerSettings obj)
    {
        obj.CanStop = true;
    }

    protected override void OnStop()
    {
    }
}

Listing 8.5.5.2 Set CanStop via ChoServiceInstallerSettings.xml file

Open ChoServiceInstallerSettings.xml file, specify the true/false in ‘canStop’ attribute.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <serviceInstallerSettings serviceName="TestService" serviceStartMode="Automatic" depends="MSSQL/IIS" account="User" userName="" password="" timeoutInTicks="-1" canHandlePowerEvent="false" canHandleSessionChangeEvent="true" canPauseAndContinue="false" canShutdown="true" canStop="true" autoLog="false" exitCode="0">
    <description>TestService Description</description>
    <parameters><![CDATA[]]></parameters>
    <recoverySettings resetFailedCounterAfter="0">
      <rebootMessage><![CDATA[]]></rebootMessage>
      <command><![CDATA[]]></command>
      <actions><![CDATA[]]></actions>
    </recoverySettings>
  </serviceInstallerSettings>
</configuration>

8.5.6 ExitCode

Specifies exit code of the service. Set the ExitCode property to a non-zero value before stopping the service to indicate an error to the Service Control Manager.

Listing 8.5.6.1 Set ExitCode programmatically

[ChoApplicationHost]
public class AppHost : ChoApplicationHost
{
    protected override void OnStart(string[] args)
    {
        Console.WriteLine("OnStart");
        base.OnStart(args);
    }

    protected override void ApplyServiceParametersOverrides(ChoServiceInstallerSettings obj)
    {
        obj.ExitCode = -1;
    }
}

Listing 8.5.6.2 Set ExitCode via ChoServiceInstallerSettings.xml file

Open ChoServiceInstallerSettings.xml file, specify the integer value in ‘exitCode’ attribute.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <serviceInstallerSettings serviceName="TestService" serviceStartMode="Automatic" depends="MSSQL/IIS" account="User" userName="" password="" timeoutInTicks="-1" canHandlePowerEvent="false" canHandleSessionChangeEvent="true" canPauseAndContinue="false" canShutdown="true" canStop="true" autoLog="false" exitCode="-1">
    <description>TestService Description</description>
    <parameters><![CDATA[]]></parameters>
    <recoverySettings resetFailedCounterAfter="0">
      <rebootMessage><![CDATA[]]></rebootMessage>
      <command><![CDATA[]]></command>
      <actions><![CDATA[]]></actions>
    </recoverySettings>
  </serviceInstallerSettings>
</configuration>

8.5.7 AutoLog

Specifies the service whether to report Start, Stop, Pause, and Continue commands in the event log. Setting AutoLog to true instructs the service to use the Application event log to report command failures, as well as state change information for Start, Stop, Pause, and Continue events on the service. The name of the service is used as the log’s EventLog.Source.

To report information to a custom event log rather than the Application log, set AutoLog to false and write instructions within the command-handling methods OnContinue, OnPause, or OnStop to post to the appropriate log.

Listing 8.5.7.1 Set AutoLog programmatically

[ChoApplicationHost]
public class AppHost : ChoApplicationHost
{
    protected override void OnStart(string[] args)
    {
        Console.WriteLine("OnStart");
        base.OnStart(args);
    }

    protected override void ApplyServiceParametersOverrides(ChoServiceInstallerSettings obj)
    {
        obj.AutoLog = true;
    }
}

Listing 8.5.7.2 Set AutoLog via ChoServiceInstallerSettings.xml file

Open ChoServiceInstallerSettings.xml file, specify ‘true/false’ in ‘autoLog’ attribute.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <serviceInstallerSettings serviceName="TestService" serviceStartMode="Automatic" depends="MSSQL/IIS" account="User" userName="" password="" timeoutInTicks="-1" canHandlePowerEvent="false" canHandleSessionChangeEvent="true" canPauseAndContinue="false" canShutdown="true" canStop="true" autoLog="true" exitCode="-1">
    <description>TestService Description</description>
    <parameters><![CDATA[]]></parameters>
    <recoverySettings resetFailedCounterAfter="0">
      <rebootMessage><![CDATA[]]></rebootMessage>
      <command><![CDATA[]]></command>
      <actions><![CDATA[]]></actions>
    </recoverySettings>
  </serviceInstallerSettings>
</configuration>

8.5.7 Parameters

Specifies the service command line arguments. The command line arguments set during installation service will be saved permanently in the registry and used automatically everyone the service is started.

The command line arguments passed during start of the service will be used temporary during the running session of the service.

Listing 8.5.7.1 Set Parameters programmatically

[ChoApplicationHost]
public class AppHost : ChoApplicationHost
{
    protected override void OnStart(string[] args)
    {
        Console.WriteLine("OnStart");
        base.OnStart(args);
    }

    protected override void ApplyServiceParametersOverrides(ChoServiceInstallerSettings obj)
    {
        obj.SetServiceArguments(@"/name:Mark /msg:Hello");
    }
}

Listing 8.5.7.2 Set Parameters via ChoServiceInstallerSettings.xml file

Open ChoServiceInstallerSettings.xml file, specify service arguments’ in ‘parameters’ attribute.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <serviceInstallerSettings serviceName="TestService" serviceStartMode="Automatic" depends="MSSQL/IIS" account="User" userName="" password="" timeoutInTicks="-1" canHandlePowerEvent="false" canHandleSessionChangeEvent="true" canPauseAndContinue="false" canShutdown="true" canStop="true" autoLog="true" exitCode="-1">
    <description>TestService Description</description>
    <parameters><![CDATA[/name:Mark /msg:Hello]]></parameters>
    <recoverySettings resetFailedCounterAfter="0">
      <rebootMessage><![CDATA[]]></rebootMessage>
      <command><![CDATA[]]></command>
      <actions><![CDATA[]]></actions>
    </recoverySettings>
  </serviceInstallerSettings>
</configuration>

8.6 Service Recovery

Windows Services support the ability to automatically perform some defined action in response to a failure. Cinchoo framework allows you to define actions that can be performed on the 1st failure, 2nd failure, and subsequent failures, and also provides support for resetting the failure counters and how long to wait before taking the action. The allowed actions are

  • Take No Action (default)
  • Restart the Service
  • Run a Program
  • Restart the Computer

Having this type of functionality is really helpful from the perspective of a developer of services. Who wants to re-invent the wheel and have to write recovery code in the service if you can get it for free. Plus it allows the recovery to be reconfigured as an IT task as opposed to rebuilding the software.

8.6.1 ResetFailedCounterAfter

Specifies the length of the period (in seconds) with no failures after which the failure count should be reset to 0 (zero). If you want to reset the counters you can set to zero which will cause the counters to reset after each failure.

Listing 8.6.1.1 Set ResetFailedCounterAfter programmatically

[ChoApplicationHost]
public class AppHost : ChoApplicationHost
{
    protected override void OnStart(string[] args)
    {
        Console.WriteLine("OnStart");
        base.OnStart(args);
    }

    protected override void ApplyServiceParametersOverrides(ChoServiceInstallerSettings obj)
    {
        obj.RecoverySettings.ResetFailCountAfter(100);
    }
}

Listing 8.6.1.2 Set ResetFailedCounterAfter via ChoServiceInstallerSettings.xml file

Open ChoServiceInstallerSettings.xml file, specify value in seconds in ‘resetFailedCounterAfter’ attribute.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <serviceInstallerSettings serviceName="TestService" serviceStartMode="Automatic" depends="MSSQL/IIS" account="User" userName="" password="" timeoutInTicks="-1" canHandlePowerEvent="false" canHandleSessionChangeEvent="true" canPauseAndContinue="false" canShutdown="true" canStop="true" autoLog="true" exitCode="-1">
    <description>TestService Description</description>
    <parameters><![CDATA[/name:Mark /msg:Hello]]></parameters>
    <recoverySettings resetFailedCounterAfter="1000">
      <rebootMessage><![CDATA[]]></rebootMessage>
      <command><![CDATA[]]></command>
      <actions><![CDATA[]]></actions>
    </recoverySettings>
  </serviceInstallerSettings>
</configuration>

8.6.2 RestartService

Specifies the failure action to the service as to restart. The service will restart automatically after the specified period (in min) of time lapsed.

Listing 8.6.2.1 Set RestartService programmatically

[ChoApplicationHost]
public class AppHost : ChoApplicationHost
{
    protected override void OnStart(string[] args)
    {
        Console.WriteLine("OnStart");
        base.OnStart(args);
    }

    protected override void ApplyServiceParametersOverrides(ChoServiceInstallerSettings obj)
    {
        obj.RecoverySettings.RestartService(10);
    }
}

Listing 8.6.2.2 Set RestartService via ChoServiceInstallerSettings.xml file

Open ChoServiceInstallerSettings.xml file, specify ‘restart/600000′ in ‘actions’ element.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <serviceInstallerSettings serviceName="TestService" serviceStartMode="Automatic" depends="MSSQL/IIS" account="User" userName="" password="" timeoutInTicks="-1" canHandlePowerEvent="false" canHandleSessionChangeEvent="true" canPauseAndContinue="false" canShutdown="true" canStop="true" autoLog="true" exitCode="-1">
    <description>TestService Description</description>
    <parameters><![CDATA[/name:Mark /msg:Hello]]></parameters>
    <recoverySettings resetFailedCounterAfter="1000">
      <rebootMessage><![CDATA[]]></rebootMessage>
      <command><![CDATA[]]></command>
      <actions><![CDATA[restart/600000]]></actions>
    </recoverySettings>
  </serviceInstallerSettings>
</configuration>

8.6.4 RebootSystem

Specifies the failure action to the service as to reboot the system. SCM will reboot the system automatically after the specified period (in min) of time lapsed.

Listing 8.6.4.1 Set RunProgram programmatically

[ChoApplicationHost]
public class AppHost : ChoApplicationHost
{
    protected override void OnStart(string[] args)
    {
        Console.WriteLine("OnStart");
        base.OnStart(args);
    }

    protected override void ApplyServiceParametersOverrides(ChoServiceInstallerSettings obj)
    {
        obj.RecoverySettings.RebootSystem(10, "Service down.");
    }
}

Listing 8.6.4.2 Set RunProgram via ChoServiceInstallerSettings.xml file

Open ChoServiceInstallerSettings.xml file, specify ‘reboot/600000′ in ‘actions’ element and specify ‘Service down.’ to ‘rebootMessage’ element.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <serviceInstallerSettings serviceName="TestService" serviceStartMode="Automatic" depends="MSSQL/IIS" account="User" userName="" password="" timeoutInTicks="-1" canHandlePowerEvent="false" canHandleSessionChangeEvent="true" canPauseAndContinue="false" canShutdown="true" canStop="true" autoLog="true" exitCode="-1">
    <description>TestService Description</description>
    <parameters><![CDATA[/name:Mark /msg:Hello]]></parameters>
    <recoverySettings resetFailedCounterAfter="1000">
      <rebootMessage><![CDATA[]]></rebootMessage>
      <command><![CDATA[]]></command>
      <actions><![CDATA[run/600000]]></actions>
    </recoverySettings>
  </serviceInstallerSettings>
</configuration>

8.6.3 RunProgram

Specifies the failure action to the service as to run a specified program. SCM will start the specified program automatically after the specified period (in min) of time lapsed.

Listing 8.6.3.1 Set RunProgram programmatically

[ChoApplicationHost]
public class AppHost : ChoApplicationHost
{
    protected override void OnStart(string[] args)
    {
        Console.WriteLine("OnStart");
        base.OnStart(args);
    }

    protected override void ApplyServiceParametersOverrides(ChoServiceInstallerSettings obj)
    {
        obj.RecoverySettings.RunProgram(10, "Notepad.exe", @"C:\sample.txt");
    }
}

Listing 8.6.3.2 Set RunProgram via ChoServiceInstallerSettings.xml file

Open ChoServiceInstallerSettings.xml file, specify ‘run/600000′ in ‘actions’ element and specify ‘Notepad.exe C:\sample.txt’ to ‘command’ element.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <serviceInstallerSettings serviceName="TestService" serviceStartMode="Automatic" depends="MSSQL/IIS" account="User" userName="" password="" timeoutInTicks="-1" canHandlePowerEvent="false" canHandleSessionChangeEvent="true" canPauseAndContinue="false" canShutdown="true" canStop="true" autoLog="true" exitCode="-1">
    <description>TestService Description</description>
    <parameters><![CDATA[/name:Mark /msg:Hello]]></parameters>
    <recoverySettings resetFailedCounterAfter="1000">
      <rebootMessage><![CDATA[]]></rebootMessage>
      <command><![CDATA[Notepad.exe C:\sample.txt]]></command>
      <actions><![CDATA[run/600000]]></actions>
    </recoverySettings>
  </serviceInstallerSettings>
</configuration>

Please bookmark this article as more updates will come shortly.

Cinchoo – Simplified Command Line Argument Parser

Download Samples.zip

Contents

1. Introduction

Cinchoo is the application framework for .NET. One of the main functionalities it provides to the users is application configuration management. Application configuration is the information that application reads and/or writes at run-time from the source.

Most programs accept command line arguments in some forms. It is repetitive and boring task to be programmed to parse and consume the command line arguments. Making it as library letting you to concentrate yourself on the core development tasks. There are many parsers available to use and does the job for you. Cinchoo is one among them, but very easy to use in your project. Try and feel for yourself.

Cinchoo provides a clean and easy API to consume command line arguments. It allows you

  1. To display usage text
  2. Way to report syntex errors
  3. Type-safe object management
  4. Support of value converters
  5. Support of custom validations
  6. Read commandline parameters from file etc. 

2. Requirement

This command line library is written in C# for the .NET 4.0 Framework. It is part of Cinchoo framework, which is a great library with lot of features like Configuration Management, common ApplicationHost, Shell features etc.

3. “Hello World!” Sample

Lets begin by looking into a simple example of an application accepting two command line arguments name (-name) and message (-msg). name is required argument and msg is optional argument with default value “Good Morning”. If msg is not passed, it will take the default value.

  • Download the latest Cinchoo binary here. (Nuget Command: Install-Package Cinchoo)
  • Open VS.NET 2010 or higher
  • Create a sample VS.NET (.NET Framework 4) Console Application project
  • Add reference to Cinchoo.Core.dll
  • Use the Cinchoo.Core.Shell namespace
  • Copy and paste the below command line object

Listing 3.1 Defining Command Line Object

[ChoCommandLineArgObject(ApplicationName = "Hello world", Copyright = "Copyright 2014 Cinchoo Inc.")]
public class HelloWorldCmdLineParams : ChoCommandLineArgObject
{
    [ChoCommandLineArg("name", IsRequired = true, Description = "Name of the person.")]
    public string Name
    {
        get;
        set;
    }
    [ChoCommandLineArg("msg", DefaultValue = "Good Morning", Description = "Greeting message.")]
    public string Message
    {
        get;
        set;
    }

    public override string ToString()
    {
        return "{0}! {1}.".FormatString(Message, Name);
    }
}

The code above illustrates about defining command line argument object. First thing define a command line argument (ex. HelloWorldCmdLineParams) class from ChoCommandLineArgObject, it indicates that this object is a command line argument object. And it must be decorated with ChoCommandLineArgObjectAttribute to complete the definition. In this example, we specify the name of application and copyright message. These values are used by parser to display the formatted message on the console window. If these options are not specified, AssemblyTitle and AssemblyCopyright of the entry assembly will be defaulted to these members respectively.

Define command line argument members name and msg either as public fields or properies with get and set in it. Decorate them with ChoCommandLineArg attribute to indicate that they are command line arguments. In this sample, name property is given name as command line switch with IsRequired as true. It means that this argument must be passed with value as command line argument to this executable when it runs. Description is specified to give short help text to this option. Message property is given msg switch name with DefaultValue. It is an optional command line argument, if not passed with value, it will be defaulted to “Good Morning” text.

It is very simple and clear way of specifying command line options using attributes.

Listing 3.2 Main Method

class Program
{
    static void Main(string[] args)
    {
        HelloWorldCmdLineParams cmdLineParams = new HelloWorldCmdLineParams();
        Console.WriteLine(cmdLineParams.ToString());
    }
}

We start by creating new instance of HelloWorldCmdLineParams object, That all. All the heavy lifting of parsing and loading command line arguments to the object done by the parser under the hood. If you run the executable with name argument value as Raj, it will display “Good Morning! Raj.”

Listed below are test runs of the programs with different set of arguments and its output.

Listing 3.3 Test1.exe with no arguments

>Test1.exe
Hello world [Version 1.0.0.0]
Copyright 2014 Cinchoo Inc.

Missing arg value for 'name' required command line switch.

Test1.exe [/msg:<string>] /name:<string>

        /msg    Greeting message.
        /name   Name of the person.

Listing 3.4 Test1.exe with /?

>Test1.exe /?
Hello world [Version 1.0.0.0]
Copyright 2014 Cinchoo Inc.

Test1.exe [/msg:<string>] /name:<string>

        /msg    Greeting message.
        /name   Name of the person.

Listing 3.5 Test1.exe with -name argument

>Test1.exe /name:Raj
Hello world [Version 1.0.0.0]
Copyright 2014 Cinchoo Inc.

Good Morning! Raj.

Listing 3.6 Test1.exe with quoted argument

>Test1.exe /name:"Raj Nagalingam"
Hello world [Version 1.0.0.0]
Copyright 2014 Cinchoo Inc.

Good Morning! Raj Nagalingam.

Listing 3.7 Test1.exe with -name and -msg arguments

>Test1.exe /name:"Raj Nagalingam" /msg:"Hello world"
Hello world [Version 1.0.0.0]
Copyright 2014 Cinchoo Inc.

Hello world! Raj Nagalingam.

Listing 3.8 Test1.exe with error conditions

>Test1.exe /name:" " /msg:"Hello world"
Hello world [Version 1.0.0.0]
Copyright 2014 Cinchoo Inc.

Missing arg value for 'name' required command line switch.

ConsoleApplication2.exe [/msg:<string>] /name:<string>

        /msg    Greeting message.
        /name   Name of the person.

So far we have seen some of the samples of using Command line arguments in the executable. Try with some other combinations by yourself to test it out.

4. Types of Command Line arguments

There are two types of command line arguments available to choose from

  1. Positional Command Line Arguments
  2. Switch Named Command Line Arguments

4.1 Positional Command Line Argument

It is an argument defined to accept value from command line arguments from a specified position. Position starts with value 1. A command line object member decorated with ChoPositionalCommandLineArgAttribute will be called positional argument.

Listing 4.1 Command Line Argument Object with Positional Arguments

[ChoCommandLineArgObject(ApplicationName = "Hello world", Copyright = "Copyright 2014 Cinchoo Inc.")]
public class HelloWorldCmdLineParams : ChoCommandLineArgObject
{
    [ChoPositionalCommandLineArg(1, "Pos1")]
    public string PosArg1;

    [ChoPositionalCommandLineArg(2, "Pos2")]
    public string PosArg2;

    [ChoCommandLineArg("name1", Aliases="n1, m", IsRequired = true, Description = "Name of the person.")]
    public string Name;

    [ChoCommandLineArg("msg", DefaultValue = "Good Morning", Description = "Greeting message.")]
    public string Message
    {
        get;
        set;
    }
}

In the sample above illustrates that PosArg1 and PosArg2 are positional command line arguments. The parser looks at the passed command line arguments, load them with appropriate positional values to them.

Listing 4.2 Test1.exe with positional arguments

>Test1.exe CPos1 /name:"Raj" /msg:"Hello world" CPos2

Hello world [Version 1.0.0.0]
Copyright 2014 Cinchoo Inc.

-- ConsoleApplication2.HelloWorldCmdLineParams State --
        Message: Hello world
        PosArg1: CPos1
        PosArg2: CPos2
        Name: Raj

After executing the program with the above command line argument, the above object members PosArg1 and PosArg2 will be loaded with APos1 and APos2 values. Please note, the positional argument values are mixed up with Switch Named command line arguments. When it comes to parse and load the positional arguments, parser identifies position arguments in the order from left to right starting with position 1 and loads them.

4.2 Switch Named Command Line Argument

It is an argument defined to accept value from command line arguments for a specified command line switch. A command line object member decorated with ChoCommandLineArgAttribute will be called switch named command line argument.

Listing 4.3 Command Line Argument Object with Switch Named Arguments

[ChoCommandLineArgObject(ApplicationName = "Hello world", Copyright = "Copyright 2014 Cinchoo Inc.")]
public class HelloWorldCmdLineParams : ChoCommandLineArgObject
{
    [ChoPositionalCommandLineArg(1, "Pos1")]
    public string PosArg1;

    [ChoPositionalCommandLineArg(2, "Pos2")]
    public string PosArg2;

    [ChoCommandLineArg("name", Aliases="n, m", IsRequired = true, Description = "Name of the person.")]
    public string Name;

    [ChoCommandLineArg("msg", DefaultValue = "Good Morning", Description = "Greeting message.")]
    public string Message
    {
        get;
        set;
    }
}

In the sample above illustrates that Name and Message are switch named command line arguments. The parser looks at the passed command line arguments for the switches ‘name’ and ‘msg’, load those arguments with the matching switch to the corresponding members in the command line object.

Listing 4.4 Test1.exe with positional arguments

>Test1.exe CPos1 /name:"Raj" /msg:"Hello world" CPos2

Hello world [Version 1.0.0.0]
Copyright 2014 Cinchoo Inc.

-- ConsoleApplication2.HelloWorldCmdLineParams State --
        Message: Hello world
        PosArg1: CPos1
        PosArg2: CPos2
        Name: Raj

After executing the program with the above command line argument, the above object members Name and Message will be loaded with ‘Raj’ and ‘Hello World’ values.

5. Defining Command Line Argument Object

5.1 Attributes

5.1.1 ChoCommandLineArgObjectAttribute

This attribute is applied to the class which will recieve and loaded with command line arguments, ie. the class defining all the command line arguments available to the program. This object will reveive the values specified in the command line arguments to the executable.

  • ApplicationName – Optional. Application name. If not specified, it will be defaulted to AssemblyTitle. If AssemblyTitle is empty, it will defaulted to entry assembly Executable Name.
  • Copyright – Optional. Copyright information. If not specified, it will be defaulted to entry AssemblyCopyright.
  • Description – Optional. Description information. If not specified, it will be defaulted to entry AssemblyDescription.
  • Version – Optional. Version information. If not specified, it will be defaulted to entry AssemblyVersion.
  • AdditionalInfo – Optional. Additional detailed help text can be specified here.
  • DoNotShowUsageDetail – Optional. This flag instructs framework to show or not to show the details in the usage text. By default, it shows details in the usage text. Default value is false.
  • ShowUsageIfEmpty – Optional. This flag instructs the framework to show usage if no arguments passed to the executable at all. Default value is CommandLineParserSettings.ShowUsageIfEmpty value.

5.1.2 ChoCommandLineArgAdditionalUsageAttribute

This attribute is applied to the command line argument class in order to provide very detailed descriptive help text to the them. You may specify them multiple times to the class. These text are displayed at the end of Usage Text seperated by newline.

  • AdditionalUsageText – Required. Detailed descriptive usage text.

5.1.3 ChoCommandLineArgAttribute

This attribute can be applied to the fields and properties of class to designate as switch named command line arguments. This attribute designate the members of the class receive the command line argument value for the specified switch.

5.1.3.1 Descriptive Parameters

There are three descriptive parameters in this attribute.

  • CommandLineSwitch – Required. Command line switch.
  • ShortName – Optional. Short name of the switch, used to produce help text.
  • Description – Optional. Description of the switch, used to produce help text.
  • Aliases – Optional. Specify alternative switches seperated by ‘,’ or ‘;’
  • Order – Optional. Specify the order of the parameters to be validated as well as to be displayed in the usage text.
5.1.3.2 Make argument as required

IsRequired will enforce the command line argument parameter as mandatory parameter. If the value is not specified at the command line argument to the executable for the required option, will throw ChoCommandLineArgException.

5.1.3.3 Provide default value

Providing default value for a command line argument will allow the parser assign this default value to the member when the parameter is not specified at the command line to the executable. Note that setting this parameter does not prevent the user from specifying a different value on the command line, it merely provides the default value should the user opt not to provide one.

5.1.3.4 Providing fallback value

In case the command line argument is passed for the concerned option and failed convert and assign to the member, fallback value is assigned to the member.

5.1.3.5 Specifying Aliases for a command line argument

The Aliases property names the aliases for a command line argument. These are other names with which the command line argument may be referred to. It is a string property, and multiple names can be specified seperated by comma or semi-colon. Any alias specified here must be unique among other aliases and command line switches. Parser will not check for name uniqueness of these options.

5.1.3.6 Formatting parameters

These parameters are optional to be used to formatting and make it to display elegant usage text. Below are the available parameters for use to format the usage text

  • NoOfTabsSwitchDescFormatSeperator – Optional. Number of TAB characters to be placed between switch and description. Default is 1.
  • DescriptionFormatLineSize – Optional. Command line switch description line size. Default is 60.
  • DescriptionFormatLineBreakChar – Optional. Command line switch description line break character. Default is a space (‘ ‘) character.

5.1.4 ChoPositionalCommandLineArgAttribute

This attribute can be applied to the fields and properties of class to designate as positional command line arguments. This attribute designate the members of the class receive the command line argument value for the specified position. Position always starts with 1.

5.1.4.1 Descriptive Parameters

There are three descriptive parameters in this attribute.

  • Position – Required. Command line argument position. Must be value of 1 and above.
  • ShortName – Required. Short name of the switch, used to produce help text.

Besides above descriptive properties, you can specify fallback value, default value, formatting parameters to this argument. Alias can’t specified to this argument.

6. Supported Types

Command Line Argument parser supports all members defined with any CLR datatype. No restriction at all. All the built-in datatypes are automatically managed by parser, no special handling of conversion is required. Below are the supported built-in types are

  • enum
  • bool
  • byte
  • sbyte
  • char
  • decimal
  • double
  • float
  • int
  • uint
  • long
  • ulong
  • short
  • ushort
  • string

If there is an exception occurs during the parser conversion and assignment of the the command line value to a member, it will throw ChoCommandLineArgException.

6.1 Special handling of bool value

This section illustrates about special way of passing and handling bool values from command line arguments. Most of the command line values can be passed to the executable in the below switch:value format

Listing 6.1 Test1.exe with switch and values

>Test1.exe /name:"Raj" /msg:"Hello world"

But bool parameters can be handled in couple of ways

6.1.1 Explicit way

In here the bool values are passed as string value (True/False). This sample illustrates that the switch -r value is passed with True text. Parser takes that value, converts and assign it to the corresponding data member.

Listing 6.2 Test1.exe with explicit bool TRUE value

>Test1.exe /name:"Raj" /msg:"Hello world" /r:True

Listing 6.3 Test1.exe with explicit bool FALSE value

>Test1.exe /name:"Raj" /msg:"Hello world" /r:False

6.1.2 Implicit way

This section illustrates how bool values are set without passing values explicitly. Parser looks for switch presents in the command line arguments for bool parameters. If the bool switch specified (/r), parser takes them as True value. If the bool switch specified as (/r-), parser takes them as False value.

Listing 6.4 Test1.exe with implicit bool TRUE value

>Test1.exe /name:"Raj" /msg:"Hello world" /r

Listing 6.5 Test1.exe with implicit bool FALSE value

>Test1.exe /name:"Raj" /msg:"Hello world" /r-

6.2 Handling of complex datatype members

This section illustrates how to handle complex UDT data type members. There are number of ways to recieve, parse and assign the values to command line argument members.

  • Callback Machanism
  • ValueConverter Mechanism

6.2.1 Callback Machanism

This is easiest way to receive, parse and load the command line argument value to a member. This gives you ultimate control over handling any type of command line argument values in your object. Override OnBeforeCommandLineArgLoaded method in the command line argument object, which will be invoked for each command argument switch by the parser. There you write custom logic for the concerned member and load the member with value. Once you handle the situation, simply return True to tell the parser that you handled the conversion.

Listing 6.4 CommandLineArgObject with Callback override

[ChoCommandLineArgObject(ApplicationName = "Hello world", Copyright = "Copyright 2014 Cinchoo Inc.")]
public class HelloWorldCmdLineParams : ChoCommandLineArgObject
{
    [ChoCommandLineArg("name", Aliases="n", IsRequired = true, Description = "Name of the person.")]
    public string Name;

    [ChoCommandLineArg("msg", DefaultValue = "Good Morning", Description = "Greeting message")]
    public string Message
    {
        get;
        set;
    }

    [ChoCommandLineArg("s", ShortName = "<int | INFINITE>", Description = "Sleep period.")]
    public int Sleep
    {
        get;
        set;
    }

    protected override bool OnBeforeCommandLineArgLoaded(string memberName, ref string value, object defaultValue, object fallbackValue)
    {
        if (memberName == "Sleep")
        {
            if (value == null)
                Sleep = 0;
            else
            {
                if (String.Compare(value.ToString(), "INFINITE", true) == 0)
                    Sleep = -1;
                else
                {
                    int timeout = 0;
                    int.TryParse(value.ToString(), out timeout);
                    Sleep = timeout;
                }
            }
            return true;
        }
        else
            return base.OnBeforeCommandLineArgLoaded(memberName, ref value, defaultValue, fallbackValue);
    }
}

In the sample above, Sleep parameter is int type. But it gives option to take either int or INFINITE value as command line argument. In the callback machanism, we handle it by overriding OnBeforeCommandLineArgLoaded method.

Listing 6.5 Test1.exe with Sleep value passed

>Test1.exe /name:"Raj" /msg:"Hello world" /s:INFINITE

Hello world [Version 1.0.0.0]
Copyright 2014 Cinchoo Inc.

-- ConsoleApplication2.HelloWorldCmdLineParams State --
        Message: Hello world
        Sleep: -1
        Name: Raj

6.2.2 ValueConverter Machanism

This is alternative way to receive, parse and load the command line argument value to a member. This gives you opportunity to perform conversion using ValueConverters. Either you can use existing converters or create new one and use them. In here I’ll show you to how create a new converter and use them in command line argument members. This approach lets you to reuse the conversion logic in a pluggable architecture.

First, lets define a ChoTimeoutConverter. It converts input string value to Timeout (int) value. You can define the converter class derived from either System.Windows.Data.IValueConverter (PresentationFramework.dll) or Cinchoo.Core.IChoValueConverter (Cinchoo.Core.dll). Both interfaces have the same methods. You can use whatever interface suited for your needs.

Listing 6.6 ChoTimeoutConverter class

public class ChoTimeoutConverter : IChoValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value == null)
            return 0;

        int timeout = 0;
        if (String.Compare("INFINITE", value.ToString(), true) == 0)
            return -1;
        else if (int.TryParse(value.ToString(), out timeout))
            return timeout;
        else
            return 0;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return value != null ? value.ToString() : String.Empty;
    }
}

In above, defined ChoTimeoutConverter from IChoValueConverter interface. Implemented the methods. It is self-explanatory.

Now I’ll illustrate how to use the above converter in the command line argument member. Couple of ways you can use them in command line argument object.

  • Defining Converter Attribute and use it
  • Using ChoTypeConverterAttribute directly
6.2.2.1 Defining Converter Attribute and use it

This approach will let you define converter attribute for each converter. In the sample below shows you to define ChoTimeoutConverterAttribute for ChoTimeoutConverter class. The converter attribute must be drived from ChoTypeConverterAttribute class and pass converter type to its converter. See below example

Listing 6.7 ChoTimeoutConverterAttribute class

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public class ChoTimeoutConverterAttribute : ChoTypeConverterAttribute
{
    public ChoTimeoutConverterAttribute()
        : base(typeof(ChoTimeoutConverter1))
    {
    }
}

Once you have attribute defined as above, it can be used against command line argument member to perform conversion of the argument value as below

Listing 6.8 Using ChoTimeoutConverterAttribute in a member

[ChoCommandLineArgObject(ApplicationName = "Hello world", Copyright = "Copyright 2014 Cinchoo Inc.")]
public class HelloWorldCmdLineParams : ChoCommandLineArgObject
{
    [ChoCommandLineArg("name", Aliases="n", IsRequired = true, Description = "Name of the person.")]
    public string Name;

    [ChoCommandLineArg("msg", DefaultValue = "Good Morning", Description = "Greeting message")]
    public string Message
    {
        get;
        set;
    }

    [ChoCommandLineArg("t", ShortName = "<int | INFINITE>", Description = "Timeout period.")]
    [ChoTimeoutConverter]
    public int Timeout
    {
        get;
        set;
    }
}
6.2.2.2 Using ChoTypeConverterAttribute

This illustrates how to use the converter class (ChoTimeoutConverter) directly to the command line object member without defining converter attribute for each converter class. Cinchoo framework provides common attribute class for this purpose. It is ChoTypeConverterAttribute. Simply use this attribute and pass the ChoTimeoutConverter type to its constructor as below.

Listing 6.9 Using ChoTypeConverterAttribute in a member

[ChoCommandLineArgObject(ApplicationName = "Hello world", Copyright = "Copyright 2014 Cinchoo Inc.")]
public class HelloWorldCmdLineParams : ChoCommandLineArgObject
{
    [ChoCommandLineArg("name", Aliases="n", IsRequired = true, Description = "Name of the person.")]
    public string Name;

    [ChoCommandLineArg("msg", DefaultValue = "Good Morning", Description = "Greeting message")]
    public string Message
    {
        get;
        set;
    }

    [ChoCommandLineArg("t", ShortName = "<int | INFINITE>", Description = "Timeout period.")]
    [ChoTypeConverter(typeof(ChoTimeoutConverter))]
    public int Timeout
    {
        get;
        set;
    }
}

So far you have learned how to parse, convert and load command line argument value to its members using various methodologies.

6.3 Special handling of Enum

This section illustrates about special way of passing and handling enum values from command line arguments. Most of the command line values can be passed to the executable in the below switch:value format

Listing 6.10 Test1.exe with switch and values

>Test1.exe /name:"Raj" /msg:"Hello world"

Lets begin by defining enum type and command line argument object with enum member as below

public enum Action { ADD, DEL, UPDATE };

[ChoCommandLineArgObject]
public class COMPUTERCmdLineArgObject : ChoCommandLineArgObject
{
 [ChoPositionalCommandLineArg(1, "\\\\computername", IsRequired = true, Order = 0)]
 public string ComputerName;

 [ChoCommandLineArg("action", IsRequired = true)]
 public Action ActionValue;
}

Above code illustrates that Action enum is defined. It is consumed by COMPUTERCmdLineArgObject class by declaring ActionValue member. It must be declared with ChoCommandLineArgAttribute. All enum types must be decorated with ChoCommandLineArgAttribute. 

Below is the test run shows how to pass enum value as command line argument

Listing 6.11 Test1.exe with enum value (Implicit way)

>Test1.exe "\\NYCDR1234234" /UPDATE

It illustrates that the enum values may be passed in /[EnumValue] format.

Listing 6.12 Test1.exe with enum value (Explicit way)

>Test1.exe "\\NYCDR1234234" /action:UPDATE

It illustrates that the enum values may be passed in /Switch:EnumValue format.

7. Customizations

There are ways to customize the parser, like command line switch characters, assignment seperators, usage switches, case sensitive handling of switches etc. The default settings should be good enough for most applications, but they are there for your customizations if you need to.

7.1 Specifying switch characters

This is special character used to prefix with swtiches when you pass command line arguments to the executable. Parser comes with default switch characters ‘/’, ‘-‘. It means that these are the available characters can be prefixed with switches. It can be customizable by opening [appExe].config or App.config file, add or edit custom configuration section ‘commandLineParserSettings’ to it.

Listing 7.1 CommandLineParserSettings config section

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
        <section name="commandLineParserSettings" type="Cinchoo.Core.Shell.ChoCommandLineParserSettings, Cinchoo.Core" />
    </configSections>
    <commandLineParserSettings switchChars="/, -" valueSeperators = ":, =" />
</configuration>

Cinchoo.Core.Shell.ChoCommandLineParserSettings is the configuration section object. Below are the available parameters for you to customize the parser

  • switchChars – Optional. List of possible command line switch charactors can be specified here. Multiple characters acceptable. Must specify one character. Characters are separated by comma. Default values: “/, -“.
  • valueSeparators – Optional. Specify list of possible switch-value separator characters. Multiple characters can be specified. Must specify atleast one character. Characters are separated by comma. Default values: “:. =”.
  • usageSwitches – Optional. This is the switch used to display usage text. Specify list of possible usage switch strings. Multiple values can be specified. Must specify atleast one value. Strings are separated by comma. Default values: “?, h, help”.
  • fileArgSwitches – Optional. This switch used to specify the file path containing command line arguments. Some programs may have long list of arguments. In order to avoid repeat passing the command line argument values to the executable, you can save them to a file and input the file using this switch. Multiple values can be specified. Must have atleast one value. Characters are seperated by comma. Default values: ‘@’.
  • ignoreCase – Optional. True, command line switches are case insensitive. Otherwise false. Default value: True.
  • showUsageIfEmpty – Optional. True, will show detailed usage text. Otherwise false. Default value: True.
  • doNotShowHeader – Optional. True, will show header information like AssemblyTitle, Version, and Description. Otherwise false. Default value: True.

7.2 Case sensitive of Command Line Switches

You have a option to specify whether the command line switches are case sensitive or not, through ignoreCase parameter in ChoCommandLineParserSettings. Default is that switches are NOT case sensitive.

8. CommandLineObject Overrides

This section illustrates some of the important methods which can be overrideable in order to customize the parser.

8.1 GetUsage() Method

In general, the parser does the quite job of creating the usage text for you in most cases. In rare situation, you may want to customize the way you want the usage text would be. In this case, you can override GetUsage() method. It is a public method, return string value.

8.2 OnBeforeCommandLineArgLoaded Method

This method gets called before each command line argument value is loaded. In this method you have opportunity to validate the command line value, perform conversion or even replace with new value etc. Return true, instruct parser to stop processing of the value. Otherwise continue the process of loading the value.

8.3 OnAfterCommandLineArgLoaded Method

This method gets called for each command line argument is loaded. In here you have option to perform any post assignment tasks like dependent property values can be derived from this property.

8.4 OnCommandLineArgLoadError Method

This method gets called when there is an exception occurs when loading command line argument to a member. In here, you can perform exception handling. Return true, instruct the parser that you handled the exception. Otherwise false.

8.5 OnBeforeCommandLineArgObjectLoaded Method

This method gets called before the command line argument object is loaded. In this method you have opportunity to lookup the payload of the command line arguments overall, perform some pre validation or even replace the values with new ones etc. Return true, instruct parser to stop the parsing. Otherwise continue the process of loading the object.

8.6 OnAfterCommandLineArgLoaded Method

This method gets called after command line argument object is loaded. In here you have option to perform any post assignment tasks like dependent property values can be derived, validation etc.

8.7 OnCommandLineArgObjectLoadError Method

This method gets called when there is an exception occurs when parsing and loading whole command line argument object. In here, you can perform exception handling. Return true, instruct the parser that you handled the exception. Otherwise false.

8.8 OnCommandLineArgNotFound Method

This method gets called when no command line argument found in the argument list to the executable.

9. Advanced Topics

In this topic, I’m going to talk about some of the key advanced topics to understand and use in your projects when you use this parser. It helps you how effective the parser can be utilized in your projects.

9.1 Specifying Argument with Whitespaces

Most of the cases, the command line argument value without any whitespaces can be specified as it is. In case where the value contains white spaces, it must be surrounded by double quotes. For example,

Listing 9.1 Test1.exe with value contains white spaces

>Test1.exe /name:"Raj Nagalngam" /msg:"Hello world" /r:True

9.2 Specifying Quotation Marks into the value

By default double quotes (“) are the only recognized quotation marks. Inside the quotes you may escape quote character itself to insert a literal quotation mark into the value. It can be inserted as below (escaped using additional double quotes)

Listing 9.2 Test1.exe with value contains double quotes

>Test1.exe /name:"Raj""s msg" /msg:"Hello world" /r:True

9.3 Loading arguments from external file

A program may have multiple large, complex sets of command line arguments and wanted to run them multiple times, in this case you can save the values to external file and pass the file to the program. It saves time and effort of retyping the command line argument values for each run.

Listing 9.2 Test1.exe with value contains double quotes

>Test1.exe @"C:\Sample\CmdLineArgs.txt"

10. Validation

In this section, I’ll talk about how to validate each command line argument member values, different approaches of validation methods, etc. Any application that accepts input from command line arguments must ensure that the information is valid in terms of some set of rules that you specify. For example, you may need to check that a customer’s phone number has the correct number of digits, or that a date falls within a particular range. In addition, if the validation fails, you may need to send an error message that explains what is wrong.

Cinchoo provides library of classes, called validators, that supplies the code for validating command line argument members. For example, one validator checks for null strings and another validator checks that a string contains only specific set of characters etc. In some cases, you may want to define your own custom validator and use them to validate the member values.

There are special validaters used to compose, aggregate other validators and group them together in a rule set.

  • ChoAndCompositeValidator – It groups other validators, all validators in this composite validator must be true for a successful validation.
  • ChoOrCompositeValidator – It groups other validators, at least one validators in the composite validator must be true for a successful validation.

If you want to avoid validations using this library feature and take control of validation, you can do so in couple of ways

  • Callback mechanism – OnAfterCommandLineArgLoaded override method can be used to put custom logic to validate the command line argument value.
  • Property Setter mechanism – This is another place where you can do custom validation on these member values.

10.1 Using Validators

The following procedures explains how to use validators in your command line argument object members. There are two ways to use validators for your command line argument members

  • ChoValidatorAttribute – An generic validator attribute, used in case specific validator does not have corresponding attribute.
  • Validator specific attribute – These are customized validator attributes for each validator. For example, ChoStringValidatorAttribute is the attribute for ChoStringValidator class.

Cinchoo supports the following validators out of the box

  • ChoValidators – Validators that Cinchoo library provides itself. You may find number of them under Cinchoo.Core namespace. For example, ChoNotNullValidator, ChoNotNullOrEmptyValidator etc.
  • BCL Configuration Validators – Validators that .NET BCL library provides. You can find them System.Configuration namespace. For example, StringValidator, IntegerValidator etc.
  • BCL DataAnnotation Validators – Another set of validators that .NET BCL library provides under System.ComponentModel.DataAnnotations namespace.

10.1.1 Using ChoValidatorAttribute

This is one way of specifying validation rules to command line argument members. In case where there is no validator attribute available for a specific validator, this attribute is here to rescue. This attribute takes validator type as input. The validator type can be one of below derived classes

  • Cinchoo.Core.ChoValidator – There are number of validators that Cinchoo framework library offers to use in your application.
  • System.Configuration.ConfigurationValidatorBase (System.Configuration.dll) – These set of validators .NET BCL offers to use.

Below are the available optional properties for this attribute

  • ConstructorArgs – Optional. Most of the validators accepts some mandorary parameters through constructor to perform the validation on command line object members. These parameters can be specified through this property. It takes object[] as value. These values are positional constructor arguments.
  • ConstructorArgsText – Optional string value. The constructor arguments can be specified as text and each value is seperated by ‘;’.
  • KeyValuePropertiesText – Optional string value. It is a key value pair seperated by ‘;’ character to set validator property values. For example, “MinValue=1;MaxValue=10″.

Listing 10.1 CommandLine Argument Object with validators

[ChoCommandLineArgObject(Description = "Utility to generate schema or class files from given source.")]
public sealed class ChoAppCmdLineParams : ChoCommandLineArgObject
{
    [ChoPositionalCommandLineArg(1, "xmlfile", IsRequired = true, Description = "Name of an xml file to infer xsd schema from.")]
    [ChoValidator(typeof(ChoNotNullOrWhiteSpaceValidator))]
    [ChoValidator(typeof(StringValidator), ConstructorArgsText = "10")]
    public string XmlFilePath
    {
        get;
        set;
    }

    [ChoCommandLineArg("o", Description = "The output directory to create files in. The default is the current directory.")]
    [ChoStringValidator(MinLength = 10)]
    [ChoContainsCharactersValidator("A")]
    public string OutputDirectory
    {
        get;
        set;
    }
}

In the sample above, the member ‘XmlFilePath’ is specified with 2 validations. It must be not null and must contain minimum 10 characters. The sample shows how you can use ChoValidatorAttribute to define the validators those dont have corresponding custom validator attributes. Note, multiple validators specified at the top level are combined and validated as AND condition by default. Validation orders are NOT guaranteed sequencial.

‘OutputDirectory’ member is decorated with custom validator attributes correponding to validators. In here, it is decorated with ChoStringValidatorAttribute and ChoContainsCharatersValidatorAttribute. It gives flexibilty, simplicity and type safety in declaring validators.

Create a console application and create an object of ChoAppCmdLineParams as below

Listing 10.2 Main method

class Program
{
    static void Main(string[] args)
    {
        ChoAppCmdLineParams cmdLineParams = new ChoAppCmdLineParams();
        Console.WriteLine(cmdLineParams.ToString());
    }
}

Listed below are test runs of the program with different arguments

Listing 10.3 Test1.exe with no arguments

>Test1.exe
ConsoleApplication2 [Version 1.0.0.0]
Copyright c  2014

Utility to generate schema or class files from given source.

Found exception while loading `xmlfile` command line argument.

The string must be at least 10 characters long.

ConsoleApplication2.exe xmlfile [/o:<string>]

        xmlfile Name of an xml file to infer xsd schema from.
        /o      The output directory to create files in. The default is the
                current directory.

Press any key to continue . . .

In the above run, the executable was run without passing any command line arguments. The application is trying to instantiate the ChoAppCmdLineParams object. While creating such object, the framework tries to validate the member values. In this case, the positional command line argument ‘xmlFilePath’ is failed to validate. The application termintes with exception.

Listing 10.4 Test1.exe with incorrect output directory value

>Test1.exe "C:\Windows\Systems\cpanal.xml" /o:"C:\Windows\Systems\Xcpanal.xml"
ConsoleApplication2 [Version 1.0.0.0]
Copyright c  2014

Utility to generate schema or class files from given source.

Found exception while loading `o` command line argument.

The value must contains ALL of the 'A' characters.

ConsoleApplication2.exe xmlfile [/o:<string>]

        xmlfile Name of an xml file to infer xsd schema from.
        /o      The output directory to create files in. The default is the
                current directory.

Press any key to continue . . .

In the above test run, the executable was run with arguments. The positional argument ‘XmlFilePath’ passed the validation (not null AND contains more than 10 characters). But it failed at validating ‘OutputDirectory’. It does not contain character ‘A’ in it. The application terminates with exception.

Listing 10.5 Test1.exe with correct values

>Test1.exe "C:\Windows\Systems\cpanal.xml" /o:"C:\Windows\Systems\Acpanal.xml"
ConsoleApplication2 [Version 1.0.0.0]
Copyright c  2014

Utility to generate schema or class files from given source.

-- ConsoleApplication2.ChoAppCmdLineParams1 State --
        XmlFilePath: C:\Windows\Systems\cpanal.xml
        OutputDirectory: C:\Windows\Systems\Acpanal.xml

Press any key to continue . . .

In the above test run, the executable was run with valid arguments. The positional argument ‘XmlFilePath’ as well as ‘OutputDirectory’ passed the validation without any errors.

10.1.2 Using Custom Validator Attributes

Previous section I’ve talked about using generic validator attribute in defining validations on command line argument members. It paves the way to use the validators without validator attributes. Some validators comes with custom validator attributes. It is simple and straight forward to use and type safe. In the sample below, OutoutDirectory member defines 2 validators with its custom attributes.

Listing 10.6 CommandLine Argument Object with validators

[ChoCommandLineArgObject(Description = "Utility to generate schema or class files from given source.")]
public sealed class ChoAppCmdLineParams : ChoCommandLineArgObject
{
    [ChoPositionalCommandLineArg(1, "xmlfile", IsRequired = true, Description = "Name of an xml file to infer xsd schema from.")]
    [ChoValidator(typeof(ChoNotNullOrWhiteSpaceValidator))]
    [ChoValidator(typeof(StringValidator), ConstructorArgsText = "10")]
    public string XmlFilePath
    {
        get;
        set;
    }

    [ChoCommandLineArg("o", Description = "The output directory to create files in. The default is the current directory.")]
    [ChoStringValidator(MinLength = 10)]
    [ChoContainsCharactersValidator("A")]
    public string OutputDirectory
    {
        get;
        set;
    }
}

10.2 Callback Mechanism

In cases you want to perform some complex validation on members, you can do so with this mechanism. Simply override OnAfterCommandLineArgLoaded method in your command line class as below

Listing 10.6 CommandLine argument object with custom validation

[ChoCommandLineArgObject(Description = "Utility to generate schema or class files from given source.")]
public sealed class ChoAppCmdLineParams1 : ChoCommandLineArgObject
{
    [ChoPositionalCommandLineArg(1, "xmlfile", IsRequired = true, Description = "Name of an xml file to infer xsd schema from.")]
    [ChoValidator(typeof(ChoNotNullOrWhiteSpaceValidator))]
    [ChoValidator(typeof(StringValidator), ConstructorArgsText = "10")]
    public string XmlFilePath
    {
        get;
        set;
    }

    [ChoCommandLineArg("o", Description = "The output directory to create files in. The default is the current directory.")]
    [ChoStringValidator(MinLength = 10)]
    [ChoContainsCharactersValidator("A")]
    public string OutputDirectory
    {
        get;
        set;
    }

    protected override void OnAfterCommandLineArgLoaded(string memberName, object value)
    {
        if (memberName == "OutputDirectory")
        {
            if (!((string)value).StartsWith(@"C:\"))
                throw new ArgumentException("Invalid directory passed.");
        }
        else
            base.OnAfterCommandLineArgLoaded(memberName, value);
    }
}

In the above sample code shows how to validate members using callback mechanism. If the ‘OutputDirectory’ member value does not starts with ‘C:\’, throws an exception.

Listing 10.7 Test1.exe with incorrect output directory value

>Test1.exe "C:\Windows\Systems\cpanal.xml" /o:"D:\Windows\Systems\Xcpanal.xml"
ConsoleApplication2 [Version 1.0.0.0]
Copyright c  2014

Utility to generate schema or class files from given source.

Found exception while loading `o` command line argument.

Invalid directory passed.

ConsoleApplication2.exe xmlfile [/o:<string>]

        xmlfile Name of an xml file to infer xsd schema from.
        /o      The output directory to create files in. The default is the
                current directory.

Press any key to continue . . .

If you run the sample with the above argument values, validation failes for ‘OutputDirectory’ member with exception ‘Invalid directory passed.’.

10.3 Property Setter Mechanism

This is one another way to perform complex validation on members. It is exact similar approach as Callback Machanism. In here the validation can be carried out at the property setter for property command line members. See below sample

Listing 10.8 CommandLine argument object with custom validation

[ChoCommandLineArgObject(Description = "Utility to generate schema or class files from given source.")]
public sealed class ChoAppCmdLineParams1 : ChoCommandLineArgObject
{
    [ChoPositionalCommandLineArg(1, "xmlfile", IsRequired = true, Description = "Name of an xml file to infer xsd schema from.")]
    [ChoValidator(typeof(ChoNotNullOrWhiteSpaceValidator))]
    [ChoValidator(typeof(StringValidator), ConstructorArgsText = "10")]
    public string XmlFilePath
    {
        get;
        set;
    }

    private string _outputDirectory;

    [ChoCommandLineArg("o", Description = "The output directory to create files in. The default is the current directory.")]
    [ChoStringValidator(MinLength = 10)]
    [ChoContainsCharactersValidator("A")]
    public string OutputDirectory
    {
        get { return _outputDirectory; }
        set
        {
            if (!value.StartsWith(@"C:\"))
                throw new ArgumentException("Invalid directory passed.");

            _outputDirectory = value;
        }
    }
}

In the above sample code shows how to validate members using property setter mechanism. The ‘OutputDirectory’ property validation is handled in property setter. If the value does not starts with ‘C:\’, throws an exception.

Listing 10.9 Test1.exe with incorrect output directory value

>Test1.exe "C:\Windows\Systems\cpanal.xml" /o:"D:\Windows\Systems\Xcpanal.xml"
ConsoleApplication2 [Version 1.0.0.0]
Copyright c  2014

Utility to generate schema or class files from given source.

Found exception while loading `o` command line argument.

Invalid directory passed.

ConsoleApplication2.exe xmlfile [/o:<string>]

        xmlfile Name of an xml file to infer xsd schema from.
        /o      The output directory to create files in. The default is the
                current directory.

Press any key to continue . . .

If you run the sample with the above argument values, validation failes for ‘OutputDirectory’ member with exception ‘Invalid directory passed.’.

11. Command Line Object Builder (Grouping)

So far we have learned about defining and consuming individual command line argument object in applications. It best suited for applications with single functionality with the arguments. Some applications performs multiple functions with their own arguments. For example, you have an application which processes data for various input data (network, file, internet etc.) which requires their own respective arguments. It means about having some sort of factory which you can define these contexts with. Basically the first argument would define the context to look up (network, file, internet etc.) then any following arguments would just act as that specific context’s arguments.

‘NET’ dos command is the perfect example of this scenario. If you type ‘net’ produce this

c:\>net
The syntax of this command is:

NET
    [ ACCOUNTS | COMPUTER | CONFIG | CONTINUE | FILE | GROUP | HELP |
      HELPMSG | LOCALGROUP | PAUSE | SESSION | SHARE | START |
      STATISTICS | STOP | TIME | USE | USER | VIEW ]
In the above, ACCOUNTS, COMPUTER, CONFIG etc. are contexts to ‘NET’ command. Each takes their own set of arguments. If you want to see the arguments of ACCOUNTS context, type ‘net ACCOUNTS /?’ as below
c:\>net ACCOUNTS /?
The syntax of this command is:

NET ACCOUNTS
[/FORCELOGOFF:{minutes | NO}] [/MINPWLEN:length]
              [/MAXPWAGE:{days | UNLIMITED}] [/MINPWAGE:days]
              [/UNIQUEPW:number] [/DOMAIN]
Here I’m going to walk you through mimicing ‘NET’ command with START, COMPUTER, STOP contexts only using Cinchoo library. First define a Command Line Object Builder object as below

11.1 Defining Command Line Argument Builder

First define command line object builder as below

Listing 11.1 Command line argument builder object

[ChoCommandLineArgBuilder]
public class NetCmdBuilder : ChoCommandLineArgBuilder
{
    [ChoCommandLineArgBuilderCommand("START", typeof(STARTCmdLineArgObject), Order = 0)]
    [ChoCommandLineArgBuilderCommand("STOP", typeof(STOPCmdLineArgObject), Order = 1)]
    [ChoCommandLineArgBuilderCommand("COMPUTER", typeof(COMPUTERCmdLineArgObject), Order = -1)]
    public NetCmdBuilder()
    {
    }
}

The code above illustrates about defining command line object builder object. First thing define a builder class (ex. NetCmdBuilder) from ChoCommandLineArgBuilder class, it indicates that this object is a command line argument builder object. And it must be decorated with ChoCommandLineArgBuilderAttribute to complete the definition. Besides that, we need to start defining commands mapping using ChoCommandLineArgBuilderCommandAttribute in the default constructor of the builder. In the above example, the builder defined with 3 commands START, STOP and COMPUTER mapped with STARTCmdLineArgObject, STOPCmdLineArgObject and COMPUTERCmdLineArgObject respectively. The order in the attribute describes about the display order of the commands in the usage text.

Listing 11.2 COMPUTERCmdLineArgObject

public enum Action { ADD, DEL };

[ChoCommandLineArgObject(DoNotShowUsageDetail = true)]
public class COMPUTERCmdLineArgObject : ChoCommandLineArgObject
{
    [ChoPositionalCommandLineArg(1, "\\\\computername", IsRequired = true, Order = 0)]
    public string ComputerName;

    [ChoCommandLineArg("action", IsRequired = true)]
    public Action Action;
}

The above command line argument contains two members, ComputerName - positional required argument and Action - an required enum argument. Note, DoNotShowUsageDetail is true tells the framework not to show the details of each member argument in the usage text.

Listing 11.3 STARTCmdLineArgObject

[ChoCommandLineArgObject]
public class STARTCmdLineArgObject : ChoCommandLineArgObject
{
    [ChoPositionalCommandLineArg(1, "service")]
    public string ServiceName;
}

Listing 11.4 STOPCmdLineArgObject

[ChoCommandLineArgObject(DoNotShowUsageDetail = true)]
public class STOPCmdLineArgObject : ChoCommandLineArgObject
{
    [ChoPositionalCommandLineArg(1, "service", IsRequired = true)]
    public string ServiceName;
}

Listing 11.5 Main Method

class Program
{
    static void Main(string[] args)
    {
        NetCmdBuilder netCmdBuilder = new NetCmdBuilder();
        Console.WriteLine(netCmdBuilder.CommandLineArgObject.ToString());
    }
}

Lets start by creating new instance of netCmdBuilder object. Thats all. The framework does the major lifting of the work behind the scene. After successful load of the arguments, CommandLineArgObject member of the builder object is loaded with matched command object based on the context passed to the executable as argument.

Listed below are test runs of the programs with different set of arguments and its output.

Listing 11.6 net.exe with no arguments

c:\>net

The syntax of this command is:

NET [COMPUTER | START | STOP]

Press any key to continue . . .

Listing 11.7 net.exe with COMPUTER context, no computername passed

c:\>net COMPUTER

Missing '\\computername' argument.

NET COMPUTER \\computername {/ADD | /DEL}
Press any key to continue . . .

Listing 11.7 net.exe with COMPUTER context with valid arguments

c:\>net COMPUTER \\NYC123453 /DEL

-- CommadLineObjectBuilderSample.COMPUTERCmdLineArgObject State --
        ComputerName: \\NYC123453
        Action: DEL

Press any key to continue . . .

In the above sample successful run, netCmdBuilder.CommandLineArgObject is populated with instance of COMPUTERCmdLineArgObject object.

11.2 Attributes

11.2.1 ChoCommandLineArgObjectAttribute

This attribute is applied to the class which will construct the command line argument object for the passed context and load with them the command line arguments. It contains same set of properties as ChoCommandLineArgObjectAttribute. Please refer the section.

11.2.2 ChoCommandLineArgBuilderCommandAttribute

This attribute is applied to the constructor of the builder class to define the mapping of the commands with command line argument classes. Multiple attributes may be used to define the mapping.

Listing 11.8 Sample command line builder object

[ChoCommandLineArgBuilder]
public class NetCmdBuilder : ChoCommandLineArgBuilder
{
    [ChoCommandLineArgBuilderCommand("START", typeof(STARTCmdLineArgObject), Order = 0)]
    [ChoCommandLineArgBuilderCommand("STOP", typeof(STOPCmdLineArgObject), Order = 1)]
    [ChoCommandLineArgBuilderCommand("COMPUTER", typeof(COMPUTERCmdLineArgObject), Order = -1)]
    public NetCmdBuilder()
    {
    }
}

The above code illustrates that the constructor defined with three commands using ChoCommandLineArgBuildCommandAttribute.

  • Command – Required. Command name.
  • CommandType – Required. A command line argument object type.
  • Order – Optional order to display the commands in the usage text.

11.3 Overrrides

11.2.2 GetCommandLineArgObjectType()

This method can be overridden to take control of discovering the command type for the passed command. Usually all the mapping is done using ChoCommandLineArgBuildCommandAttribute in the builder class. Occationally you may want avoid doing so by overriding this method to handle it.

[ChoCommandLineArgBuilder]
public class ChoNETCommandLineArgBuilder : ChoCommandLineArgBuilder
{
    [ChoCommandLineArgBuilderCommand("ACCOUNTS", typeof(ChoAppCmdLineParams))]
    public ChoNETCommandLineArgBuilder()
    {
    }

    public override Type GetCommandLineArgObjectType(string command)
    {
        if (command == "COMPUTER")
            return typeof(COMPUTERCmdLineArgObject);
        else
            return base.GetCommandLineArgObjectType(command);
    }
}

The above code illustrates how to override GetCommandLineArgObjectType method.

Cinchoo – Allow multi-instance application

Multi-Instance Application

Cinchoo is an application framework for .NET. One of the main functionality it provides to users is application configuration management. It also offers few other key functionalities like Common ApplicationHost, BCL etc.

When you choose to use this library in your application, it will inherently allow only one instance of your application running at any time. This is the default behavior. When you run the second instance, it will throw below application error and followed by application termination.

Already another instance of this application running.

This behavior can be override able to allow more than one instance of your application can be runnable at any time. There are couple of ways you can do so.

Programmatically

This section illustrates on how to allow multiple instances of your application run at the same time. Simply subscribe to ChoApplication.ApplyGlobalApplicationSettingsOverrides event as below

class Program
{
    static void Main(string[] args)
    {
        ChoApplication.ApplyGlobalApplicationSettingsOverrides += new EventHandler<ChoEventArgs<ChoGlobalApplicationSettings>>(ChoApplication_ApplyGlobalApplicationSettingsOverrides);

        ChoConsole.PauseLine();
    }

    static void ChoApplication_ApplyGlobalApplicationSettingsOverrides(object sender, ChoEventArgs<ChoGlobalApplicationSettings> e)
    {
        e.Value.ApplicationBehaviourSettings.SingleInstanceApp = false;
    }
}

PS: Make sure these initializations happens at the application entry point. In Windows Service, the event subscriptions are done at OnStart() method in ServiceBase subclass. In ASP.NET, the subscriptions are done in Application_Start() in Global.aspx file.

Configuration

This section illustrates how to make this through via configuration. Open the ChoCodeFrx.xml file. Look for singleInstanceApp attribute and assign ‘false’ as value. See sample below

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <globalApplicationSettings applicationId="ConfigFileOverride.Test.exe" eventLogSourceName="ConfigFileOverride.Test.exe">
    <behaviourSettings hideWindow="false" bringWindowToTop="false" alwaysOnTop="false" runAtStartup="false" runOnceAtStartup="false" singleInstanceApp="true" activateFirstInstance="false" showEnvironmentSelectionWnd="true" />
    ...
  </globalApplicationSettings>
  <metaDataFilePathSettings>
    ...
  </metaDataFilePathSettings>
</configuration>

 

Cinchoo – Configuration Framework, Part 31

Disable logging

Cinchoo is an application framework for .NET. One of the main functionality it provides to users is application configuration management. Application configuration is the information that an application reads and/or writes at run-time from the source.

Please visit jump start article [Cinchoo – Simplified Configuration Manager] for more information about Cinchoo configuration manager.

In this section, I’ll show to disable logging of Cinchoo framework produces. Open a app.config file from your project. Add the below section

<configuration>
 <system.diagnostics>
   <switches>
     <add name="ChoSwitch" value="0" />
   </switches>
 </system.diagnostics>
</configuration>

ChoSwitch is a TraceSwitch, you can assign different levels to it to specify the types of trace or debug messages the application outputs. Setting the value to “0” will disable all the logging of the framework.

Other possible values can be assigned to control the output of the logs

  • 0 – Off
  • 1 – Error
  • 2 – Warning
  • 3 – Info
  • 4 – Verbose

Cinchoo – Configuration Framework, Part 30

Consume configuration values from any database.

Download Sample (Zip)

Cinchoo is an application framework for .NET. One of the main functionality it provides to users is application configuration management. Application configuration is the information that an application reads and/or writes at run-time from the source.

Please visit jump start article [Cinchoo – Simplified Configuration Manager] for more information about Cinchoo configuration manager.

In this section, I’ll detail about new configuration section handler, DbGenericeKeyValueConfigurationSection. It is generic database configuration section handler to read and store configuration data in any database. It eliminates creating new configuration section handler for each targeted database. All you need ADO.NET driver for the corresponding database to work with this handler. This section handler sufficient enough to interact with any database to read/store configuration data. Only downside of this handler is that all the values are converted to string before storing in the database.

Here are steps to work with this section handler

  • Define a table similar to the below schema. The table name can be given anything. This configuration handler needs a table with 2 columns. First column (KEY) holds the configuration parameter names. Must be VARCHAR(50), where length must be minimum of 50. Second column (VALUE) holds the configuration parameter values. It can be size of any. By default, this handler looks for KEY, VALUE as columns. The names of these columns can be given any. In this case, it must be passed as parameter to the configuration object. Will discuss this later about passing these parameters to the configuration object.
CREATE TABLE TEST_CONFIG
(
    [KEY] VARCHAR (50) NOT NULL,
    VALUE VARCHAR (100),
    PRIMARY KEY ([KEY])
)
  • Define configuration object targeted for Sqlite database as below
[ChoDbGenericKeyValueConfigurationSection("dbApplicationSettings",
@"CONNECTION_STRING='Data Source=.\SqliteTest.db;Version=3;Pooling=True;Max Pool Size=100;';
 TABLE_NAME=TEST_CONFIG;PROVIDER_ASSEMBLY_FILE_PATH=C:\Program Files\System.Data.SQLite\2010\bin\System.Data.SQLite.dll;PROVIDER_NAMESPACE=System.Data.SQLite;
 LAST_UPDATE_DATETIME_KEY_NAME=''", Silent = true)]
public class DbApplicationSettings : ChoConfigurableObject
{
 [ChoPropertyInfo("name", DefaultValue = "Mark")]
 public string Name;

 [ChoPropertyInfo("address", DefaultValue = "10 River Road, Edison NJ 08820")]
 public string Address;

 protected override void OnAfterConfigurationObjectLoaded()
 {
 Console.WriteLine(this.ToString());
 }
}

This configuration object must be decorated with ChoDbGenericKeyValueConfigurationSectionAttribute.

First parameter is the configElementPath, a xpath to the configuration section in the [appName].exe.xml file.

Second parameter is the configObjectAdapterParams, parameter list in key-value pairs format separated by ‘;’ character. (PS. If any value contains ‘;’ character, the whole value must be surrounded by single quote in order to escape it.) Below are the list of applicable parameters needed by this section handler

  1. CONNECTION_STRING (string) – Required. ADO.NET connection string to the database.
  2. TABLE_NAME (string) – Required. Configuration table name.
  3. PROVIDER_ASSEMBLY_FILE_PATH (string) – Optional. Only specify if the ADO.NET assembly is not referenced in the project. File path to the ADO.NET assembly if not in GAC. Otherwise, fully qualified assembly name.
  4. PROVIDER_NAMESPACE (string) – Required. Namespace of the ADO.NET provider (Ex. for Sqlite, it is System.Data.Sqlite)
  5. KEY_COLUMN_NAME (string) – Optional. Default is ‘KEY’. If you have different key column name, please specify it here.
  6. VALUE_COLUMN_NAME (string) – Optional. Default is ‘VALUE’. If you have different value column name, please specify it here.
  7. LAST_UPDATE_DATETIME_KEY_NAME (string) – Optional. Default is Empty string. In such case, this provider checks the configuration values for any changes in the underlying table. If you specify a valid key name, it must carry the value as timestamp. To notify the values changes by touching this value to last updated time stamp.
  8. PARAM_PREFIX_CHAR (char) – Optional. Default is ‘@’.
  9. TRUNCATE_VALUE_DATA (bool) – Optional. Default is True.  It will truncate the value if it is over the Value column size.

The above two parameters can be hard-coded in the code and also be modifiable through configuration. Here is how you can modify them through configuration. Open the [AppName].exe.xml file under Config folder

<?xml version="1.0" encoding="utf-8"?>
<configuration>
 <configSections>
 <section name="dbApplicationSettings" type="Cinchoo.Core.Configuration.ChoDictionarySectionHandler, Cinchoo.Core" />
 </configSections>
 <dbApplicationSettings cinchoo:configObjectAdapterType="Cinchoo.Core.Configuration.ChoDbGenericKeyValueConfigStorage, Cinchoo.Core" xmlns:cinchoo="http://schemas.cinchoo.com/cinchoo/01/framework">
 <cinchoo:configObjectAdapterParams xmlns:cinchoo="http://schemas.cinchoo.com/cinchoo/01/framework"><![CDATA[CONNECTION_STRING='Data Source=.\SqliteTest.db;Version=3;Pooling=True;Max Pool Size=100;';
 TABLE_NAME=TEST_CONFIG;PROVIDER_ASSEMBLY_FILE_PATH=C:\Program Files\System.Data.SQLite\2010\bin\System.Data.SQLite.dll;PROVIDER_NAMESPACE=System.Data.SQLite;
 LAST_UPDATE_DATETIME_KEY_NAME='']]></cinchoo:configObjectAdapterParams>
 </dbApplicationSettings>
</configuration>

Now lets try to create and consume the above configuration object. Below code shows how to consume and modify the object members

static void Main(string[] args)
{
    DbApplicationSettings DbApplicationSettings = new DbApplicationSettings();

    ChoConsole.PauseLine();
}

Compile and run this sample to see the output stored in the database.

That’s it folks, it is as easy as to store and consume Configuration values in any databases.

Cinchoo – Configuration Framework, Part 29

Support of Complex Object as configuration value

Download Sample (Zip)

Cinchoo is an application framework for .NET. One of the main functionality it provides to users is application configuration management. Application configuration is information that an application reads and/or writes at run-time from the source.

Please visit jump start article [Cinchoo – Simplified Configuration Manager] for more information about Cinchoo configuration manager.

In this section, I’ll detail about new feature, using and saving custom user defined object as configuration values. So far Cinchoo framework supports only scalar text values. It limits the configuration object member values from having any complex objects.

In standard .NET framework, it is not supported in straight way. You will have to create custom configuration section and put a plumbing code to parse the values. In Cinchoo framework, it is simplified to handle the complex objects as configuration values.

Here is how you can define configuration members to support custom user defined type

[ChoNameValueConfigurationSection("appSettings")]
public class AppSettings : ChoConfigurableObject
{
	[ChoPropertyInfo("name", DefaultValue = "Tom")]
	public string Name;

	[ChoPropertyInfo("address", DefaultValue="10, River Road, Piscataway, NJ 08880 && New York")]
	public ChoCDATA Address;

	[ChoPropertyInfo("employer", DefaultValue = "<Sample>ABCD Inc.</Sample>")]
	public string Employer;

	[ChoPropertyInfo("department")]
	public Department Department;

	protected override void OnAfterConfigurationObjectLoaded()
	{
		Console.WriteLine(ToString());
	}
}

In the above sample configuration object, Department member is defined as Department type, it is defined as below

[ChoXmlSerializerConverter(typeof(Department))]
public class Department : INotifyPropertyChanged
{
	private int _deptCode;

	[XmlAttribute("deptCode")]
	public int DeptCode
	{
		get { return _deptCode; }
		set
		{
			_deptCode = value;
			RaisePropertyChanged("DeptCode");
		}
	}

	private string _deptName;

	[XmlAttribute("deptName")]
	public string DeptName
	{
		get { return _deptName; }
		set
		{
			_deptName = value;
			RaisePropertyChanged("DeptName");
		}
	}

	public event PropertyChangedEventHandler PropertyChanged;

	protected void RaisePropertyChanged(string propertyName)
	{
		if (propertyName.IsNullOrWhiteSpace()) return;

		PropertyChangedEventHandler propertyChanged = PropertyChanged;
		if (propertyChanged != null)
			propertyChanged(this, new PropertyChangedEventArgs(propertyName));
	}
}

Key things to remember when you define custom object in order to use in configuration object

  • Should be decorated as ChoXmlSerializerConverter which helps to serialize the object to Xml string.
  • Must implement INotifyPropertyChanged in order to notify member changes to Configuration Manager.
  • Decorate appropriate Xml serialization attribute to its members (ie. XmlAttribute, XmlElement etc)

Now lets try to create and consume the above configuration object. Below code shows how to consume and modify the object members

static void Main(string[] args)
{
	AppSettings appSettings = new AppSettings();

	//Modify the members
	appSettings.Address.Value = "11, Oak Road, Woodbridge, NJ 08827";
	appSettings.Employer = "<Sample1>ZZZ1 Inc.</Sample1>";
	if (appSettings.Department == null)
		appSettings.Department = new Department() { DeptCode = 10, DeptName = "Comp Sc." };
	else
		appSettings.Department.DeptCode = appSettings.Department.DeptCode + 20;

	ChoFramework.Shutdown();
}

If you compile and run this sample, the output of the configuration will be seen as below

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="appSettings" type="Cinchoo.Core.Configuration.ChoNameValueSectionHandler, Cinchoo.Core" />
  </configSections>
  <appSettings>
    <add key="name" value="Tom" />
    <add key="address">
      <value><![CDATA[11, Oak Road, Woodbridge, NJ 08827]]></value>
    </add>
    <add key="employer">
      <value>
        <Sample1>ZZZ1 Inc.</Sample1>
      </value>
    </add>
    <add key="department">
      <value>
        <Department deptCode="11" deptName="Comp Sc." />
      </value>
    </add>
  </appSettings>
</configuration>

That’s it folks, it is as easy as to store and consume custom user defined member in Configuration object.

Cinchoo – Configuration Framework, Part 28

Support of CDATA / Xml as configuration value

Download Sample (Zip)

Cinchoo is an application framework for .NET. One of the main functionality it provides to users is application configuration management. Application configuration is information that an application reads and/or writes at run-time from the source.

Please visit jump start article [Cinchoo – Simplified Configuration Manager] for more information about Cinchoo configuration manager.

In this section, I’m going to detail about new feature, using and saving CDATA / Xml  as configuration values. So far Cinchoo supports the configuration object member values as simple text values. It limits the configuration object member values from having any complex text.

In standard .NET framework, it is not supported in straight way. You will have to create custom configuration section and put a plumbing code to parse the values. In Cinchoo, it is simplified to handle CDATA/Xml values.

Here is how you can define configuration members to support CDATA / Xml values

[ChoNameValueConfigurationSection("appSettings")]
public class AppSettings : ChoConfigurableObject
{
	[ChoPropertyInfo("name", DefaultValue = "Tom")]
	public string Name;

	[ChoPropertyInfo("address", DefaultValue="10, River Road, Piscataway, NJ 08880 && New York")]
	public ChoCDATA Address;

	[ChoPropertyInfo("employer", DefaultValue = "<Sample>ABCD Inc.</Sample>")]
	public string Employer;
}

In the above sample configuration object, Address member defined as ChoCDATA type to accommodate CDATA value. Employer member is defined as string type, to hold the xml value. Cinchoo automatically checks the configuration member value for any xml tags present, if it does then the section value will be created as Xml element, otherwise it will be created as Xml attribute.

Now lets try to create and consume the above configuration object. Below code shows how to consume and modify the object members

static void Main(string[] args)
{
	AppSettings appSettings = new AppSettings();

	//Modify the members
	appSettings.Address = new ChoCDATA("11, Oak Road, Woodbridge, NJ 08827");
	appSettings.Employer = "<Sample1>ZZZ1 Inc.</Sample1>";

	Console.WriteLine(appSettings.ToString());
	ChoFramework.Shutdown();
}

Since Address is CDATA member, any modification must be done by creating new ChoCDATA object with new value and assign to it. And for the Employer member, it is straight forward to modify. Cinchoo framework will take care of it under the hood.

If you compile and run this sample, the output of the configuration will be seen as below

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="appSettings" type="Cinchoo.Core.Configuration.ChoNameValueSectionHandler, Cinchoo.Core" />
  </configSections>
  <appSettings>
    <add key="name" value="Tom" />
    <add key="address">
      <value><![CDATA[11, Oak Road, Woodbridge, NJ 08827]]></value>
    </add>
    <add key="employer">
      <value>
        <Sample1>ZZZ1 Inc.</Sample1>
      </value>
    </add>
  </appSettings>
</configuration>

That’s it folks, it is as easy as to store and consume CDATA / Xml values in Configuration object.