Category Archives: Development - Page 8

QuickMon on CodePlex

I’ve finally decided to place the whole project on CodePlex (quickmon.codeplex.com)

Go and see it for yourself 🙂

 

TreeViewEx – a few enhancements

Sometimes I wonder why some functionality that should be common is simply left out of general controls.. like the WinForms TreeView (and ListView) controls that don’t have specific events for the enter key pressed. I’ve been taught long ago to always provide functionality so keyboard events also can be used to navigate around applications – like when the mouse is unavailable, broken or not your first choice (like for disabled people). Yes, I know the controls have general events for key pressing but it requires some hectic coding each time to do something that should be simple.

Anyway, adding it isn’t too difficult (but I still wished I didn’t have to do it each time I need to use it).

public class TreeViewEx : TreeView
{

public TreeViewEx() : base()
{

DoubleBuffered = true;

}
public event MethodInvoker EnterKeyPressed;
public event MethodInvoker DeleteKeyPressed;
protected override void OnKeyPress(KeyPressEventArgs e)
{

if (e.KeyChar == ‘\r’)

if (EnterKeyPressed != null)
{

EnterKeyPressed();
e.Handled = true;

}

base.OnKeyPress(e);

}

protected override void OnKeyDown(KeyEventArgs e)
{

if (e.KeyCode == Keys.Delete)

if (DeleteKeyPressed != null)
{

DeleteKeyPressed();
e.Handled = true;

}

base.OnKeyDown(e);

}

}

The code for the ListView control is basically (exactly) the same.

QuickMon update

In the past couple of weeks I’ve been busy part time working on this little project to build a simple monitoring system for myself. At the moment the basic ‘bits’ are all there working reasonably well I might add. On the one side I’ve been contemplating putting the project up on CodePlex like many of my other pet projects but this one has some potential to ‘grow’ into something that could generate some thing more (like income or so…) The trouble with that idea is it may be even more powerful if ‘others’ can extend it but then the main source is available to all. Also, this little tool is not in the same league as proper enterprise monitoring tools like Microsoft SCOM and so on.

Anyway, as far as progress goes, the ‘tool’ or set of tools now can do the following (all tested and working)

  • Main shared components for editing, management and configuration of agents plus both a Windows UI and Windows service for hosting the agents (monitor pack).
  • Collector agents for
    (General)
    Disk space – local drive space available
    File count and size – file/directory size
    Performance counters – monitor performance counters
    Ping – ping machines
    Windows service state – are specified services running or not
    WMI query – specify some WMI query and compare the return value
    (Specialized)
    BizTalk ports and orchestrations – check if ports/orchestrations are running
    BizTalk suspended count – check the suspended instance count
    SQL server database size – check database size
    SQL server table size – check row count
    Httpping – like ping but for http
  • Notifier agents for:
    Log file – a simple text log file
    RSS feed – actually really just an xml file that you can host on a web server that is exposed as an RSS feed
    SMTP – Sending out alerts as emails
    SQL database – storing alert details in a sql server
  • A special type of ‘Collector’ that is just a folder to contain others (so you can enable/disable all or set a service window for that branch)
  • Collector agents support the following general properties and methods:
    Enabled
    Check dependent collectors even after warning
    Repeat alert after x minutes
    Only alert once in x minutes
    Service windows (yes, even multiple service windows e.g. 5:00 to 12:00 and 13:00 to 17:00 etc.)
    Launch a detail view window
  • Notifier agents support the following properties:
    Enabled
    Alert level (Info, Warning, Error)
    Detail level (Detail, Summary or both)
    List of collectors to ‘service’ (default = all)
    If implemented – launch a viewer for the notifier

Development

I’m using the .Net 4 client profile framework so download footprint should be fairly small – and I’d like to keep it that way. It is developed using C# and is extendable because of the add-in type of architecture. To add an agent is as simple as implementing a provided interface.

Download example

Soon I’ll add a download link for (the current) test version. It is already fully working as is but I’m continuing to enhance and add things.

Download a first test version here.

Extension method to create CSV from List collection

A quick one to share:

I needed to a method to quickly convert a List<string> to a plain comma delimited string. So I created this little Extension class:

public static class ListEx
{

public static string ToCSVString(this List<string> l)
{

StringBuilder sb = new StringBuilder();
if (l != null)
{

l.ForEach(li => sb.Append(li + “, “));

}
return sb.ToString().Trim(‘ ‘, ‘,’);

}

}

Of course this is still very specific to string collections only. I could have created something that does the same to say a List<T> list that is more generic but that I’ll leave for another day.

[Update]

While I’m at it – here is the opposite method:

public static List<string> ToListFromCSVString(this string s, bool ordered = false)
{

List<string> list = new List<string>();
if (s != null && s.Trim().Length > 0)
{

if (ordered)

list.AddRange((from sitem in s.Split(new char[] { ‘,’ }, StringSplitOptions.RemoveEmptyEntries)
orderby sitem
select sitem.Trim()).ToArray());

else

list.AddRange((from sitem in s.Split(new char[] { ‘,’ }, StringSplitOptions.RemoveEmptyEntries)
select sitem.Trim()).ToArray());

}

return list;

}

XmlElementEx – a few XmlElement extension methods

I thought I’d just share a few extension methods I created for myself to handle a hew things with the XmlElement, XmlNode and XmlDocument classes of System.Xml namespace.

These include:

  • ReadXmlElementAttr
  • CreateAttributeWithValue
  • SetAttributeValue
  • AppendElementWithText
  • AppendElementWithCDATA
using System.Xml;

    public static class XmlElementEx
    {
        public static string ReadXmlElementAttr(this XmlElement e, string attrName, string defaultValue = "")
        {
            try
            {
                if (e.HasAttribute(attrName))
                    return e.Attributes.GetNamedItem(attrName).Value;
            }
            catch { }
            return defaultValue;
        }
        public static string ReadXmlElementAttr(this XmlNode e, string attrName, string defaultValue = "")
        {
            return ReadXmlElementAttr((XmlElement)e, attrName, defaultValue);
        }
        public static XmlAttribute CreateAttributeWithValue(this XmlDocument xml, string attrName, bool value)
        {
            return xml.CreateAttributeWithValue(attrName, value.ToString());
        }
        public static XmlAttribute CreateAttributeWithValue(this XmlDocument xml, string attrName, int value)
        {
            return xml.CreateAttributeWithValue(attrName, value.ToString());
        }
        public static XmlAttribute CreateAttributeWithValue(this XmlDocument xml, string attrName, long value)
        {
            return xml.CreateAttributeWithValue(attrName, value.ToString());
        }

        public static XmlAttribute CreateAttributeWithValue(this XmlDocument xml, string attrName, string value)
        {
            XmlAttribute attr;
            attr = xml.CreateAttribute(attrName);
            attr.Value = value;
            return attr;
        }
        public static XmlAttribute SetAttributeValue(this XmlNode e, string attrName, string value)
        {
            return SetAttributeValue((XmlElement)e, attrName, value);
        }
        public static XmlAttribute SetAttributeValue(this XmlElement e, string attrName, string value)
        {
            if (e.HasAttribute(attrName))
                e.Attributes[attrName].Value = value;
            else
                e.Attributes.Append(CreateAttributeWithValue(e.OwnerDocument, attrName, value));
            return e.Attributes[attrName];
        }

        public static XmlElement AppendElementWithText(this XmlElement e, string elementName, string text)
        {
            XmlElement newElement = e.OwnerDocument.CreateElement(elementName);
            newElement.InnerText = text;
            e.AppendChild(newElement);
            return newElement;
        }
        public static XmlElement AppendElementWithCDATA(this XmlElement e, string elementName, string cdataText)
        {
            XmlElement newElement = e.OwnerDocument.CreateElement(elementName);
            XmlCDataSection commentNode = e.OwnerDocument.CreateCDataSection(cdataText);
            newElement.AppendChild(commentNode);
            e.AppendChild(newElement);
            return newElement;
        }
    }

 

Is it my time or not?

Sounds like another philosophical question but no, you can relax. This is another developmental (yes almost mental) posting.

Say you need to create a piece of functionality that must only operate within a predefined time period of a day – call it a ‘service window’. It could be that you may even need several of these ‘windows’ per day – i.e. if there is a pause period inside the day and work must be processed before (up until) and after the pause.

For this kind of functionality I created a ‘ServiceWindows’ class. It basically host a list of ‘from’ and ‘to’ times plus methods that makes it possible to find out if a specified time is somewhere ‘inside’ or ‘outside’ any ‘service window’. Additionally the class can be ‘populated’ from an xml config string so it can be used in applications that operates from xml config files.

So without much further chit-chatting here is the guts of the class (not for sensitive readers 😉

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;

    public class ServiceWindow
    {
        public ServiceWindow(DateTime from, DateTime to)
        {
            From = from;
            To = to;
        }
        public DateTime From { get; set; }
        public DateTime To { get; set; }
    }

    public class ServiceWindows
    {
        private List<ServiceWindow> times = new List<ServiceWindow>();
        public List<ServiceWindow> Times { get { return times; } }

        public void AddTimeWindow(DateTime fromTime, DateTime toTime)
        {
            times.Add(new ServiceWindow(fromTime, toTime));
        }
        public bool IsInTimeWindow()
        {
            return IsInTimeWindow(DateTime.Now);
        }
        public bool IsInTimeWindow(DateTime nowTime)
        {
            if (times.Count == 0)
                return true;
            else
                return (from t in times
                        where t.From.TimeOfDay <= nowTime.TimeOfDay &&
                              t.To.TimeOfDay >= nowTime.TimeOfDay
                        select t).Count() > 0;
        }

        public void CreateFromConfig(string config)
        {
            if (config.Length > 0)
            {
                times.Clear();
                XmlDocument configXml = new XmlDocument();
                configXml.LoadXml(config);
                XmlElement root = configXml.DocumentElement;
                foreach (XmlNode windowItem in root.SelectNodes("window"))
                {
                    try
                    {
                        DateTime fromTime = DateTime.Parse(windowItem.ReadXmlElementAttr("from", "00:00:01"));
                        DateTime toTime = DateTime.Parse(windowItem.ReadXmlElementAttr("to", "23:59:59"));
                        times.Add(new ServiceWindow(fromTime, toTime));
                    }
                    catch { }
                }
            }
        }
        public string ToConfig()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("<serviceWindows>");
            if (times.Count > 0)
                foreach (ServiceWindow timeWindow in times)
                    sb.AppendLine(string.Format("<window from=\"{0}\" to=\"{1}\" />",
                              timeWindow.From.ToString("HH:mm:ss"),
                              timeWindow.To.ToString("HH:mm:ss")));

            sb.AppendLine("</serviceWindows>");
            return sb.ToString();
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            if (times.Count > 0)
                foreach (ServiceWindow timeWindow in times)
                    sb.Append(string.Format("{0} to {1} and ",
                           timeWindow.From.ToString("HH:mm:ss"),
                           timeWindow.To.ToString("HH:mm:ss")));
            else
                sb.Append("None");
            return sb.ToString().Trim(' ', 'a', 'n', 'd');
        }
    }

I noticed that I included an XmlNode extension method (ReadXmlElementAttr) that I defined elsewhere but all it does is to read an xml element attribute or provide a default value if not found. I’ll list that class here on my blog too soon.

Waiting for your exit

With a heading like this the post could be mistaken for something more philosophical but fortunately this is a plain developmental posting 🙂

Suppose you have some program that does some work and then have to ‘sleep’ for a while before doing some more work – like a service checking or polling stuff. You can simply use something like a Thread.Sleep() but that also means the thread cannot do or respond to anything else during that time – like the program or service stopping. That would mean the stopping process has to wait until the ‘sleeping’ thread becomes active again.

A solution to this is by having a variable or property that indicates if the program or service is still running and having the ‘waiting’ routine wait in little ‘jumps’ checking the ‘IsRunning’ variable each time and then waiting a little bit more until the full ‘sleeping’ period time is done.

private void BackgroundWaitIsPolling(int nextWaitInterval)
{
   int waitTimeRemaining;
   int decrementBy = 2000;
   if (IsRunning)
   {
       try
       {
           if ((nextWaitInterval <= decrementBy) && (nextWaitInterval > 0))
           {
               Thread.Sleep(nextWaitInterval);
           }
           else
           {
               waitTimeRemaining = nextWaitInterval;
               while (IsRunning && (waitTimeRemaining > 0))
               {
                  if (waitTimeRemaining <= decrementBy)
                  {
                      waitTimeRemaining = 0;
                  }
                  else
                  {
                      waitTimeRemaining -= decrementBy;
                  }
                  if (decrementBy > 0)
                  {
                      Thread.Sleep(decrementBy);
                  }
               }
           }
        }
        catch { }
   }
}

 

Extending the TreeView control

Here is a little Extension class to the TreeView control. I created it to add some new functionality that is not available in the base control. It is based on the System.Windows.Forms namespace TreeView but probably can be use relatively easy without too much modification on WPF as well (haven’t tried it yet)

public static class TreeViewEx
    {
        public static int CheckedCount(this TreeView tvw)
        {
            int count = 0;
            count = (from TreeNode n in tvw.Nodes
                     where n.Checked
                     select n).Count();
            foreach (TreeNode childNode in tvw.Nodes)
            {
                count += CheckedCount(childNode);
            }

            return count;
        }
        public static int CheckedCount(this TreeNode parentNode)
        {
            int count = 0;
            count = (from TreeNode n in parentNode.Nodes
                     where n.Checked
                     select n).Count();
            foreach (TreeNode childNode in parentNode.Nodes)
            {
                count += CheckedCount(childNode);
            }
            return count;
        }
        public static List<TreeNode> CheckedNodes(this TreeView tvw)
        {
            List<TreeNode> checkedNodes = new List<TreeNode>();
            foreach(TreeNode node in (from TreeNode n in tvw.Nodes
                         select n))
            {
                if (node.Checked)
                    checkedNodes.Add(node);
                checkedNodes.AddRange(node.CheckedNodes().ToArray());
            }
            return checkedNodes;
        }
        public static List<TreeNode> CheckedNodes(this TreeNode parentNode)
        {
            List<TreeNode> checkedNodes = new List<TreeNode>();
            foreach (TreeNode node in (from TreeNode n in parentNode.Nodes
                                           select n))
            {
                if (node.Checked)
                    checkedNodes.Add(node);
                checkedNodes.AddRange(node.CheckedNodes().ToArray());
            }
            return checkedNodes;
        }
        public static void ExpandAllParents(this TreeNode node)
        {
            TreeNode parent = node.Parent;
            while (parent != null)
            {
                parent.Expand();
                parent = parent.Parent;
            }
        }
    }

Microsoft Access Database Engine for 64-bit

Wow I must have missed this one when it was released but it (has the potential to) solves some problems with accessing Access databases (*.mdb) from a 64-bit process. I haven’t tested it yet and the description mention that you can only use the 32-bit driver on Windows XP (sp3) but at least it should work for Windows 7 (and yes that other one…Vista as well).

Download link: Microsoft Access Database Engine 2010 Redistributable

http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=13255

 

QuickMon 2

A quick follow-up on my QuickMon application. I’ve now managed to get a basic working application that performs some simple monitoring of 4 different types of agents (ping, file count, services and BizTalk suspended instance count). Also it includes the first notifier agent that simply logs to a plain text file.

The full architecture is a bit complicated to explain in a simple post so I’ll have to start working on a much more detail blog posting to explain it all.

Other than the documentation the next steps are to test its stability, improve error handling and adding the other basic agents I planned to make it useful.