Tag Archives: type-safe

Cinchoo – Turn your app to Windows Tray app

In this article, I’ll show you how to turn your console / winform application to Windows System Tray application. Cinchoo framework provides a single hosting infrastructure to turn your application either Windows Service or Windows Tray application through configuration.

Console Application

Here is how you can do it for console application

1. Create a new ‘Console Application‘ from VS.NET

2. Add reference to Cinchoo.Core.dll

3. Add namespace Cinchoo.Core

4. Create a class derived from ChoApplicationHost as below

[RunInstaller(true)]
public class AppHost : ChoApplicationHost
{
    protected override void OnStart(string[] args)
    {
        //TODO: Application Startup code goes here
        base.OnStart(args);
    }
}

Decorating the above class with RunInstallerAttribute will make the application to be run as Windows Service. And override OnStart method, where application start up code placed there.

5. In the main entry, do as below.

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

That’s all, you application is now ready to run as self installable windows service application or Windows Tray application.

Here is how to turn your application to Windows Tray application. In ChoCoreFrx.xml file, set ‘turnOn’ flag to ‘true’ in trayApplicationBehaviourSettings element.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <globalApplicationSettings applicationId="TestApplication.exe" eventLogSourceName="TestApplication.exe" turnOnConsoleOutput="false">
    <behaviourSettings hideWindow="false" bringWindowToTop="false" alwaysOnTop="false" runAtStartup="false" runOnceAtStartup="false" singleInstanceApp="false" activateFirstInstance="false" />
    <trayApplicationBehaviourSettings turnOn="true" showInTaskbar="true" hideMainWindowAtStartup="true" hideTrayIconWhenMainWindowShown="false" trayAppTurnOnMode="OnMinimize" />
    <appConfigPath />
  </globalApplicationSettings>
</configuration>

Other parameters

  • showInTaskbar – true, will show the application in Taskbar. false, otherwise.
  • hideMainWindowAtStartup – true, will hide the window at the application startup. Otherwise false.
  • hideTrayIconWhenMainWindowShown – true, tray application icon will hidden when the main windows shown. Otherwise false.
  • trayAppTurnOnMode – This option is applicable to WinForm application only. Possible options are OnMinimize, OnClose, OnMinimizeOrClose.

WinForm Application

Below are the steps to turn your winform application into Windows Tray application

1. Create a new ‘WinForm Application‘ from VS.NET

2. Add reference to Cinchoo.Core.dll

3. Add namespace Cinchoo.Core

4. Create a class derived from ChoApplicationHost and IChoWinFormApp as below

[RunInstaller(true)]
public class AppHost : ChoApplicationHost, IChoWinFormApp
{
    MainForm form = new MainForm();
    public AppHost()
    {
    }

    public Form MainFormWindow
    {
        get { return form; }
    }

    public ContextMenu GetContextMenu(ContextMenu contextMenu)
    {
        //Build the context menu items
        return contextMenu;
    }

    public string TooltipText
    {
        get { return null; }
    }

    public System.Drawing.Icon TrayIcon
    {
        get { return null; }
    }

    public string BalloonTipText
    {
        get { return null; }
    }

    protected override void OnStart(string[] args)
    {
        base.OnStart(args);
    }
}

Decorating the above class with RunInstallerAttribute will make the application to be run as Windows Service. And override OnStart method, where application start up code placed there.

5. In the main entry, do as below.

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

Thats all. Try it.


Cinchoo – Collections, NestedList, Part 7

GetContainedList

ChoNestedList<T> provides a helper method to find the list containing an item.

1. Add reference to Cinchoo.Core.dll assembly

2. Namespace Cinchoo.Core.Collections.Generic

Sample code,

static void Main(string[] args)
{
    ChoNestedList topList = new ChoNestedList();
    topList.Add("TopList-Tom");
    topList.Add("TopList-Mark");

    ChoNestedList nestedList1 = new ChoNestedList();
    nestedList1.Add("NestedList1-Raj");
    nestedList1.Add("NestedList1-Peter");
    nestedList1.Add("NestedList1-Samuel");
    topList.Add(nestedList1);

    topList.Add("TopList-Nancy");

    foreach (string name in topList.GetContainedList("NestedList1-Raj"))
        Console.WriteLine(name);
}

When you run the above code, the output will be

NestedList1-Raj
NestedList1-Peter
NestedList1-Samuel
Press any key to continue . . .

Cinchoo – Collections, NestedList, Part 6

EnumerateAllChildLists

ChoNestedList<T> provides a helper method to get all child list items.

1. Add reference to Cinchoo.Core.dll assembly

2. Namespace Cinchoo.Core.Collections.Generic

Sample code,

static void Main(string[] args)
{
    ChoNestedList<int> topList = new ChoNestedList<int>();
    topList.Add(1);
    topList.Add(2);

    ChoNestedList<int> nestedList1 = new ChoNestedList<int>();
    nestedList1.Add(3);
    nestedList1.Add(new List<int>() { 4, 5 });
    nestedList1.Add(6);
    nestedList1.Add(7);
    topList.Add(nestedList1);

    topList.Add(8);

    int count = 0;
    foreach (IList<int> list in topList.EnumerateAllChildLists())
    {
        Console.WriteLine("List {0} contains".FormatString(count++));
        foreach (int x in list)
            Console.WriteLine(x);
    }
}

When you run the above code, the output will be

List 0 contains
3
4
5
6
7
List 1 contains
4
5
Press any key to continue . . .

Cinchoo – Collections, NestedList, Part 5

GetListItemAt

ChoNestedList<T> provides one another helper method to get the underlying list object at a specified index

1. Add reference to Cinchoo.Core.dll assembly

2. Namespace Cinchoo.Core.Collections.Generic

Sample code,

static void Main(string[] args)
{
    ChoNestedList<int> topList = new ChoNestedList<int>();
    topList.Add(1);
    topList.Add(2);

    ChoNestedList<int> nestedList1 = new ChoNestedList<int>();
    nestedList1.Add(3);
    nestedList1.Add(4);
    nestedList1.Add(5);
    topList.Add(nestedList1);

    topList.Add(6);

    Console.WriteLine("Underlying list object at index 3 is");
    foreach (int x in topList.GetListItemAt(3))
        Console.WriteLine(x);

    Console.WriteLine("Underlying list object at index 5 is");
    foreach (int x in topList.GetListItemAt(5))
        Console.WriteLine(x);
}

When you run the above code, the output will be

Underlying list object at index 3 contains
3
4
5
Underlying list object at index 5 contains
1
2
3
4
5
6
Press any key to continue . . .

Cinchoo – Collections, NestedList, Part 4

Clone

ChoNestedList<T> provides deep cloning through ICloneable interface.

1. Add reference to Cinchoo.Core.dll assembly

2. Namespace Cinchoo.Core.Collections.Generic

Sample code,

static void Main(string[] args)
{
    ChoNestedList<string> topList = new ChoNestedList<string>();
    topList.Add("TopList-Tom");
    topList.Add("TopList-Mark");

    ChoNestedList<string> nestedList1 = new ChoNestedList<string>();
    nestedList1.Add("NestedList1-Raj");
    nestedList1.Add("NestedList1-Peter");
    nestedList1.Add("NestedList1-Samuel");
    topList.Add(nestedList1);

    topList.Add("TopList-Nancy");

    ChoNestedList<string> clonedTopList = topList.Clone<ChoNestedList<string>>();

    for (int index = 0; index < clonedTopList.Count; index++)
        Console.WriteLine(clonedTopList[index]);
}

When you run the above code, the output will be

TopList-Tom
TopList-Mark
NestedList1-Raj
NestedList1-Peter
NestedList1-Samuel
TopList-Nancy
Press any key to continue . . .

Cinchoo – Collections, NestedList, Part 3

BinarySerialization

Now you can serialize/deserialize a ChoNestedList<T> object in binary format. Here is how

1. Add reference to Cinchoo.Core.dll assembly

2. Namespace Cinchoo.Core.Collections.Generic

Sample code,

static void Main(string[] args)
{
    ChoNestedList topList = new ChoNestedList();
    topList.Add("TopList-Tom");
    topList.Add("TopList-Mark");

    ChoNestedList nestedList1 = new ChoNestedList();
    nestedList1.Add("NestedList1-Raj");
    nestedList1.Add("NestedList1-Peter");
    nestedList1.Add("NestedList1-Samuel");
    topList.Add(nestedList1);

    topList.Add("TopList-Nancy");

    ChoNestedList clonedTopList = null;
    using (MemoryStream buffer = new MemoryStream())
    {
        BinaryFormatter formatter = new BinaryFormatter();

        formatter.Serialize(buffer, topList);
        buffer.Seek(0, SeekOrigin.Begin);
        clonedTopList = (ChoNestedList)formatter.Deserialize(buffer);
    }

    for (int index = 0; index < clonedTopList.Count; index++)
        Console.WriteLine(clonedTopList[index]);
}

When you run the above code, the output will be

TopList-Tom
TopList-Mark
NestedList1-Raj
NestedList1-Peter
NestedList1-Samuel
TopList-Nancy
Press any key to continue . . .

Cinchoo – Collections, NestedList, Part 2

XmlSerialization

Now you can serialize/deserialize the ChoNestedList<T> to/from xml. Here is how

Serialize

Here you can see how to serialize ChoNestedList<T> object to xml string.

1. Add reference to Cinchoo.Core.dll assembly

2. Namespace Cinchoo.Core.Collections.Generic

Sample code,

static void Main(string[] args)
{
    ChoNestedList<string> topList = new ChoNestedList<string>();
    topList.Add("TopList-Tom");
    topList.Add("TopList-Mark");

    ChoNestedList<string> nestedList1 = new ChoNestedList<string>();
    nestedList1.Add("NestedList1-Raj");
    nestedList1.Add("NestedList1-Peter");
    nestedList1.Add("NestedList1-Samuel");
    topList.Add(nestedList1);

    topList.Add("TopList-Nancy");

    Console.WriteLine(topList.ToXml());
}

When you run the above code, the output will be

<ChoNestedList>
  <ChoTuple>
    <First><string>TopList-Tom</string></First>
    <Second />
  </ChoTuple>
  <ChoTuple>
    <First><string>TopList-Mark</string></First>
    <Second />
  </ChoTuple>
  <ChoTuple>
    <First />
    <Second><ChoNestedList>
  <ChoTuple>
    <First><string>NestedList1-Raj</string></First>
    <Second />
  </ChoTuple>
  <ChoTuple>
    <First><string>NestedList1-Peter</string></First>
    <Second />
  </ChoTuple>
  <ChoTuple>
    <First><string>NestedList1-Samuel</string></First>
    <Second />
  </ChoTuple>
</ChoNestedList></Second>
  </ChoTuple>
  <ChoTuple>
    <First><string>TopList-Nancy</string></First>
    <Second />
  </ChoTuple>
</ChoNestedList>
Press any key to continue . . .

Deserialize

In here, you can see how to deserialize a xml string to ChoNestedList<T> object.

1. Add reference to Cinchoo.Core.dll assembly

2. Namespace Cinchoo.Core.Collections.Generic

Sample code,

static void Main(string[] args)
{
    ChoNestedList<string> topList = new ChoNestedList<string>();
    topList.Add("TopList-Tom");
    topList.Add("TopList-Mark");

    ChoNestedList<string> nestedList1 = new ChoNestedList<string>();
    nestedList1.Add("NestedList1-Raj");
    nestedList1.Add("NestedList1-Peter");
    nestedList1.Add("NestedList1-Samuel");
    topList.Add(nestedList1);

    topList.Add("TopList-Nancy");

    string xml = topList.ToXml();
    ChoNestedList<string> deserializedList = xml.ToObjectFromXml<ChoNestedList<string>>();

    for (int index = 0; index < deserializedList.Count; index++)
        Console.WriteLine(deserializedList[index]);
}

When you run the above code, the output will be

TopList-Tom
TopList-Mark
NestedList1-Raj
NestedList1-Peter
NestedList1-Samuel
TopList-Nancy
Press any key to continue . . .

Cinchoo – Windows Service made easy

Cinchoo framework simplifies the Windows service development. An application developed in this approach is ready to run as Console application as well as self install-able Service application. In this article, I’m going to walk over the steps of creating service development.

1. Create a new ‘Console Application‘ from VS.NET

2. Add reference to Cinchoo.Core.dll

3. Add namespace Cinchoo.Core

4. Create a class derived from ChoApplicationHost as below

[RunInstaller(true)]
public class AppHost : ChoApplicationHost
{
    protected override void OnStart(string[] args)
    {
        //TODO: Application Startup code goes here
    }
}

Make sure the type is decorated with RunInstallerAttribute. And override OnStart method, where application start up code placed there. Implement any other service related (OnStop, OnShutdown etc) methods by overriding them.

5. In the main entry, do as below.

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

That’s all, you application is self install ready windows service application.

You can run it as console application or install it as Windows service also.

To Install as windows service, pass /i command line argument

[AppExeName].exe /i

To uninstall as windows service, pass /u command line argument

[AppExeName].exe /u

To start the service, pass /s command line argument

[AppExeName].exe /s

To stop the service, pass /t command line argument

[AppExeName].exe /t

To pause the service, pass /p command line argument

[AppExeName].exe /p

To continue the service, pass /c command line argument

[AppExeName].exe /c

To execute a command in the service, pass /e:{command_id} command line argument

[AppExeName].exe /e:2

Controlling Service Installation

There are couple of settings classes which controls the service process behavior during installation. It can configured through configuration file or can be overridden programmatically. They are

  • ChoServiceInstallerSettings – service process parameters used during installation of service.
  • ChoServiceProcessInstallerSettings – service credential parameters used during installation of the service.

First, let take a look at the way configuring them through configuration file.

ChoServiceProcessInstallerSettings

These setting can be maintained in ChoServiceProcessInstallerSettings.xml file. If the file not exists, it will created by Cinchoo framework in application configuration directory. The file look as below

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <serviceProcessInstallerSettings account="LocalSystem" userName="" password="">
    <helpText />
  </serviceProcessInstallerSettings>
</configuration>

If you want more information about each attribute/element in the above xml section, please visit ServiceProcessInstaller Class in MSDN.

ChoServiceInstallerSettings

These setting can be maintained in ChoServiceInstallerSettings.xml file. If the file not exists, it will created by Cinchoo framework in application configuration directory. The file look as below

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <serviceInstallerSettings delayedAutoStart="false" displayName="ChoServiceHost.Test" serviceName="ChoServiceHost.Test" serviceStartMode="Automatic" />
</configuration>

If you want more information about each attribute/element in the above xml section, please visit ServiceInstaller Class in MSDN.

Next, let take a look at the way of overriding them programmatically.

Override ApplyServiceInstallParamsOverrides method in your ApplicationHost class, where you can changes service installation parameters.

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

    protected override void ApplyServiceInstallParamsOverrides(ChoServiceProcessInstallerSettings serviceProcessInstallerSettings, ChoServiceInstallerSettings serviceInstallerSettings)
    {
        serviceInstallerSettings.DisplayName = "Test";
        serviceInstallerSettings.ServiceName = "Test";
    }
}

In the above sample code, we are trying to override the ServiceName and DisplayName as ‘Test’. When you install the service, it will be created as ‘Test’ service.


Cinchoo – Framework Tips

Here I’m going to talk about couple of tips on using Cinchoo framework in your project.

At the start of the application, you must call ChoFramework.Initialize() to initialize the framework and start necessary services. Usually you must place this statement in application Main entry point as below.

Calling ChoFramework.Shutdown() method is not mandatory to call. But it is safe to make this call for graceful shutdown of all the background services.

static void Main(string[] args)
{
    ChoFramework.Initialize();
    try
    {
        ChoApplication.ApplyFrxParamsOverrides += new EventHandler<ChoFrxParamsEventArgs>(ChoApplication_ApplyFrxParamsOverrides);

        SampleConfigSection ApplicationSettings = new SampleConfigSection();
    }
    catch (Exception ex)
    {
        Console.WriteLine("ERROR: " + ex.Message);
    }
    finally
    {
        ChoFramework.Shutdown();
    }
}

ChoAppDomain.


Cinchoo – Configuration framework, part 17

Configuration Object Validation

This section is the continuation of previous articles. In here, I’ll go over seamless automatic configuration member validations,  another feature provided by Cinchoo configuration framework.

At present, Cinchoo framework supports the following family of Validation attributes

Look below the sample, where the Name property decorated with ChoStringValidator, It restricts the maximum length of the Name is 5.

[ChoSingleTagConfigurationSection("sample")]
public class SampleConfigSection : ChoConfigurableObject
{
	#region Instance Data Members (Public)

	[ChoPropertyInfo("name", DefaultValue = "Mark")]
    [ChoStringValidator(MaxLength=5)]
	public string Name;

	[ChoPropertyInfo("message", DefaultValue = "Hello World!")]
	public string Message;

	#endregion

	[ChoAfterConfigurationObjectLoadedHandler]
	void OnAfterConfigurationObjectLoaded(object sender, ChoConfigurationObjectEventArgs e)
	{
		Console.WriteLine(sender.ToString());
	}
}

During the configuration object initialization or when there is explicit assignment to Name member, if the length of value over 5, it will silently (default behavior) report the error in the log file as below. Framework will try to assign the value either DefaultValue or FallbackValue to Name.

Below code trying to assign Name value to ‘123456’

class Program
{
	static void Main(string[] args)
	{
		SampleConfigSection sampleConfigSection = new SampleConfigSection();
        sampleConfigSection.Name = "123456";

		//Shutdown the framework to stop the background services...
		//otherwise the application will not terminate
		ChoFramework.Shutdown();
	}
}

If you look at the configuration object log file (\\Logs\Settings\HelloWorld.SampleConfigSection.log), it reports the error


2012-03-29 04:53:08.0241140
  -- HelloWorld.SampleConfigSection State --
	Name: Mark
	Message: Hello World!

-- MetaData Information --
	BindingMode: TwoWay
	Defaultable: True
	Silent: True
	ConfigurationMetaDataLogInfo:
		-- Log Information --
			LogCondition: True
			LogTimeStampFormat:
			LogDirectory: Settings
			LogFileName: HelloWorld.SampleConfigSection.log

	ConfigStorageType: Cinchoo.Core.Configuration.ChoFileSingleTagConfigStorage, Cinchoo.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b7dacd80ff3e33de

2012-03-29 04:53:08.0397395
* -- HelloWorld.SampleConfigSection State --
	Name: Mark
	Message: Hello World!

	-- Following errors produced while construction --
		Name: Failed to assign `123456` value. The string must be no more than 5 characters long.

-- MetaData Information --
	BindingMode: TwoWay
	Defaultable: True
	Silent: True
	ConfigurationMetaDataLogInfo:
		-- Log Information --
			LogCondition: True
			LogTimeStampFormat:
			LogDirectory: Settings
			LogFileName: HelloWorld.SampleConfigSection.log

	ConfigStorageType: Cinchoo.Core.Configuration.ChoFileSingleTagConfigStorage, Cinchoo.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b7dacd80ff3e33de


Follow

Get every new post delivered to your Inbox.