Singleton Application With NotifyIcon

I recently had to implement 2 changes to a winforms application I’m working on, here’s the general requirements:

  • Can only have 1 instance of the application running – make it a singleton application
  • The user can close (hide) the application but it remains running until exited – utilize the notify icon (tray icon)

I searched google and found a bunch of helpful sources for both, this is a melding of several of them to fit my needs.

Here are the guts of the winform that deals with the notifyIcon:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
using System;
using System.Windows.Forms;
 
namespace SingletonAppWithNotifyIcon
{
    public partial class MainForm2 : Form
    {
        private bool IsQuit { get; set; }
 
        public MainForm2()
        {
            InitializeComponent();
        }
 
        private void MyFormClosing(object sender, FormClosingEventArgs e)
        {
            if (IsQuit)
                return;
 
            HideMe();
            e.Cancel = true;
        }
 
        private void HideMe()
        {
            Hide();
        }
 
        public void ShowMe()
        {
            WindowState = FormWindowState.Normal;
            Visible = true;
            Activate();
        }
 
        private void QuitMe()
        {
            IsQuit = true;
            Application.Exit();
        }
 
        private void MainForm2_Load(object sender, EventArgs e)
        {
            notifyIcon1.ContextMenu = new ContextMenu();
            notifyIcon1.ContextMenu.MenuItems.Add(new MenuItem("Quit My Fancy App", NotifyIconQuit));
        }
 
        private void NotifyIconQuit(object sender, EventArgs e)
        {
            QuitMe();
        }
 
        private void NotifyClick(object sender, EventArgs e)
        {
            ShowMe();
        }
 
        private void CloseClick(object sender, EventArgs e)
        {
            HideMe();
        }
 
        private void QuitClick(object sender, EventArgs e)
        {
            QuitMe();
        }
    }
}

Here the highlights for the singleton application:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
using System;
using System.Windows.Forms;
using Microsoft.VisualBasic.ApplicationServices;
 
namespace SingletonAppWithNotifyIcon
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
 
            SingleInstanceAppStarter.Start(new MainForm2(), StartNewInstance);
        }
 
        static void StartNewInstance(object sender, StartupNextInstanceEventArgs e)
        {
            var forms = Application.OpenForms;
            var frm = forms["MainForm2"] as MainForm2;
            if (frm != null)
            {
                frm.ShowMe();
            }
            else
            {
                var f = new MainForm2();
                f.ShowDialog();
            }
        }
    }
 
    class SingleInstanceApp : WindowsFormsApplicationBase
    {
        public SingleInstanceApp() { }
 
        public SingleInstanceApp(Form f)
        {
            IsSingleInstance = true;
            MainForm = f;
        }
    }
 
    public class SingleInstanceAppStarter
    {
        static SingleInstanceApp _app;
 
        public static void Start(Form f, StartupNextInstanceEventHandler handler)
        {
            if (_app == null && f != null)
            {
                _app = new SingleInstanceApp(f);
            }
 
            if (_app == null) return;
 
            _app.StartupNextInstance += handler;
            _app.Run(Environment.GetCommandLineArgs());
        }
    }	
}

Here’s a sample solution (nice and simple) for you to download and experiment with…
SingletonAppWithNotifyIcon.zip

Custom List In app.config File

I recently had the need to define a custom list in an application. There are many ways to make this happen but I wanted it in the app config file.

After a few searches, I pieced together the following example.

The app.config below has a custom section, AppConfigCollection.CustomDictionary. This section is declared in the configSections and the type attribute has my handler class and assembly name.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
 
  <configSections>
    <section name="AppConfigCollection.CustomDictionary" type="AppConfigCollection.CustomDictionary, AppConfigCollection"/>
  </configSections>
 
  <AppConfigCollection.CustomDictionary>
 
    <NFL>
      <AFC>
        <AFC_WEST>
          <Item code="DEN" team="Denver Broncos" />
          <Item code="MCI" team="Kansas City Chiefs" />
          <Item code="SAN" team="San Diego Chargers" />
          <Item code="OAK" team="Oakland Raiders" />
        </AFC_WEST>
      </AFC>
 
      <NFC>
        <NFC_EAST>
          <Item code="DFW" team="Dallas Cowboys" />
          <Item code="PHL" team="Philadelphia Eagles" />
          <Item code="EWR" team="New York Giants" />
          <Item code="IAD" team="Washington Redskins" />
        </NFC_EAST>
      </NFC>
    </NFL>
 
  </AppConfigCollection.CustomDictionary>
 
</configuration>

The handler class CustomDictionary inherits from IConfigurationSectionHandler. This interface has been deprecated but it’s still available and easy to implement and make it work.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Xml.Linq;
 
namespace AppConfigCollection
{
 
    class Program
    {
        static void Main(string[] args)
        {
            var teamDictionary = (Dictionary<string, string>)ConfigurationManager.GetSection("AppConfigCollection.CustomDictionary");
 
            Console.WriteLine();
            Console.WriteLine("NFL TEAMS");
            foreach (var key in teamDictionary.Keys)
            {
                Console.WriteLine("{0} : {1}", key, teamDictionary[key]);
            }
 
            Console.WriteLine();
            Console.WriteLine("press any key to continue ...");
            Console.ReadLine();
        }
    }
 
    public class CustomDictionary : IConfigurationSectionHandler
    {
        public object Create(object parent, object configContext, System.Xml.XmlNode section)
        {
            var rList = new Dictionary<string, string>();
 
            var doc = XDocument.Parse(section.OuterXml);
            var root = (XElement)doc.FirstNode;
 
            var rootElement = (XElement)root.FirstNode;
            if (rootElement.Name != "NFL")
                throw new ConfigurationErrorsException(
                    "AppConfigCollection.CustomDictionary section only accepts 'NFL' elements.");
 
            try
            {
                foreach (XElement conference in rootElement.Nodes())
                {
                    Console.WriteLine();
                    Console.WriteLine("Conference : {0}", conference.Name);
 
                    foreach (XElement division in conference.Nodes())
                    {
                        Console.WriteLine("  Division : {0}", division.Name);
 
                        var teamList = from div in division.Elements("Item") select div;
                        foreach (var item in teamList)
                        {
                            var code = item.Attribute("code").Value;
                            var team = item.Attribute("team").Value;
 
                            Console.WriteLine("    Team ({0}) : {1}", code, team);
                            rList.Add(code, team);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw new ConfigurationErrorsException(
                    "Error reading element.", ex);
            }
 
            return rList;
        }
    }
 
}

This is the output.
AppConfigCollection

Feel free to download the application solution – experiment and adapt it for your needs.

Logging Serializable Objects

Here’s a handy method to log your object serialized as XML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//
public void LogObject(object obj)
{
	var msg = "";
	try
	{
		using (var w = new StringWriter())
		{
			var s = new XmlSerializer(obj.GetType());
			s.Serialize(w, obj);
			w.Flush();
			msg = w.ToString();
		}
	}
	catch (Exception e)
	{
		msg = e.Message.ToString();
	}
 
	msg = string.Format("{0}\n{1}", obj.GetType(), msg);
	Log.Debug(msg);
}
 
LogObject(msgHeader);
//

This is what my logger spit out

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
OTA_TravelItineraryReadNS2_2_0.MessageHeader
<?xml version="1.0" encoding="utf-16"?>
<MessageHeader xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.ebxml.org/namespaces/messageHeader">
  <From>
    <PartyId>55555</PartyId>
  </From>
  <To>
    <PartyId>789789789</PartyId>
  </To>
  <CPAId>2WE2</CPAId>
  <ConversationId>130615-120130.2844</ConversationId>
  <Action>TravelItineraryReadLLSRQ</Action>
  <MessageData>
    <MessageId>mid:200051209-18808-23933@clientofsabre.com</MessageId>
  </MessageData>
</MessageHeader>

Frustration …

Frustrated with Sabre web services, grrrrr!

The version of the TravelItineraryRead web service I’m using is “2003A.TsabreXML1.15.1”. Yes, I know its a bit dated but it fits the needs .. until recently. I’m seeing more Rail segments come across and this version does not support them. So, I wanted to test the latest TravelItineraryRead version to see how or if Rail is supported. 

So I built my proxy classes using 
wsdl.exe  http://webservices.sabre.com/wsdl/tpfc/TravelItineraryReadLLS2.2.0RQ.wsdl  /namespace:OTA_TravelItineraryReadNS2_2_0
And setup my code to call the new service  

1
2
3
4
5
. . .
var serviceObj = new TravelItineraryReadService();
serviceObj.MessageHeaderValue = msgHeader;
serviceObj.Security = security;
TravelItineraryReadRS response = serviceObj.TravelItineraryReadRQ(req);

… and I get a null response. No errors or exceptions just a null response. So, I tweak my objects and try again – same null response. Make more changes – same null response. I go through several cycles of this and I’m stuck, I need help – another set of eyes to see what I’m doing wrong. (BTW, Sabre’s documentation on this web service is horrible. This is the root of my problem)

Next I contact Sabre’s web services support through email. This is my frustration … The email conversation goes on for 6 days. Maybe their support group is outside the USA or their policy it to email a maximum of a few times per day. Whichever the case, it is painfully slow dealing with them. The end result of this painfully slow email conversation is nothing, nada, zip, still getting null. The support group doesn’t have aqueduct documentation, no code samples, no code ownership and certainly no expertise.

 Long story shortened, this was my problem:

1
2
3
4
5
6
7
8
9
10
11
12
13
MessageHeader msgHeader = new MessageHeader();
 
// If the msgHeader.Action is not exactly 
// "TravelItineraryReadLLSRQ"
// your response will be null
msgHeader.Action = "TravelItineraryReadLLSRQ";
 
Service service = new Service();
service.Value = "TravelItineraryReadLLSRQ"; // THIS IS NOT USED!!!
service.type = "this object can be null";
msgHeader.Service = service; // THIS CAN BE NULL, IT'S NOT USED
 
. . .

Now I can finally start my research on Rail segments!!

There are not many “open” resources available for Sabre web services. I’ve written a lot of code to consume them…. contact me if you are stuck, maybe I can help.

Blogging C# Source in WordPress

1
2
3
4
5
6
7
8
9
10
11
12
13
using System;
namespace HelloWorld
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
            Console.WriteLine(Environment.NewLine + "Press Enter to exit.");
            Console.ReadLine();
        }
    }
}

New to WordPress so I fumbled around a bit figuring out a way to write about C#. I’m sure there are lots of ways to skin this cat but the first method I got working was with the WP-Syntax plugin. After installed, I copied my source from VisualStudio and stuffed it inside here :

<pre lang=”csharp” line=”1″> PASTE CODE HERE </pre>.