S h o r t S t o r i e s

// Tales from software development

Archive for the ‘Development’ Category

C# StorageValue type part 2: Struct

leave a comment »

Almost as soon as I started on a struct implementation I decided to change the type used to hold the storage value from the long (System.Int64) type used in the class implementation to a ulong (System.UInt64). I’d considered this previously and decided that a signed value might be useful for storage/memory calculations but, the more I thought about it the more I realised this didn’t make much sense and it would be better to use an unsigned value.

The class implementation of the StorageClass type in the previous post was useful to developing the code required to parse and format storage values. However, I knew that I really wanted the semantics and behaviour associated with the built in value types such as int.

Another change I considered was to use the implicit operator to allow conversion from a string value without an explicit cast.

With the current implementation, assignment looks like this:

StorageValue s1 = new StorageValue(100 * 1024);
StorageValue s2 = new StorageValue("1.5MB");

but I really wanted to be able to do this:

StorageValue s1 = 100 * 1024;
StorageValue s2 = "1.5MB";

In fact, it’s very easy to do this, whether the type is a class or struct, by implementing the implicit operator:

 public static implicit operator StorageValue(int value)
 {
    return new StorageValue(value);
 }
 
 public static implicit operator StorageValue(string value)
 {
    return StorageValue.Parse(value);
 }

Unfortunately, this is represents an abuse of what the implicit operator is designed for. To explain why, it’s worth considering the intent of the explicit operator first. Typically, when a conversion might fail or will potentially result in data loss, the compiler requires an explicit cast, e.g.

long gb = 1024 * 1024 * 1024;
int i = (int)gb // cast required

This conversion using a cast is implemented using the explicit operator. The point of this is to allow the conversion but only when the programmer uses a cast, i.e. it ensures that the programmer is aware of the potential for failure or data loss.

The implicit operator implements the conversion without requiring the cast, i.e. the programmer is not made aware of the possible failure or data loss when the conversion is performed.

So, while it would be nice to allow syntax such as

StorageValue s2 = "1.5GB";

it is not correct use of the implicit operator as this conversion could easily fail if the string is not correctly formatted, e.g.

StorageValue s2 = "Hello world!";

There is a much bigger issue with a struct implementation rather than a class implementation which is simply that it will be passed by value rather than by reference. For this implementation this is probably the correct behaviour but, again, it really comes down to what feels right.

Advertisements

Written by Sea Monkey

January 30, 2017 at 6:18 pm

Posted in Development

Tagged with ,

C# StorageValue type part 1: Class

leave a comment »

I’ve been meaning to write a class for the past few years for dealing with memory storage values.

The clinical data interfaces that I write often need to read and write disk and memory storage values. For example, the Vitality HL7 MLLP software that receives and sends HL7 messages via TCP/IP, writes incoming messages to disk and so it monitors disk free space to ensure that there is always sufficient disk space available. The required disk free space is held as a configuration file value. I could use a long value for bytes, or an int value for MB, but it would be more convenient to have a datatype that allowed me to express the value using the most appropriate units, e.g. as “500MB” or “0.5GB” rather than 524288000.

Conversely, these interfaces typically log the process working set size before and after processing to aid diagnosing memory problems. Again, it’s possible just to log the number of bytes but it’s easier to read and understand a working set size of, for example, 18.56MB rather than 19464932.

So, the class must implement parsing methods to take values such as “1234”, “123.4KB”, “12.34MB”, “1.234GB”, “1.234GB” and to provide formatting methods to display storage values according to a format string or default to an appropriate formatted value.

Most of the code already existed as discrete utility methods in my code. I just needed to bring them all together into a single class and implement the IFormattable interface so that an instance of the class can be formatted in composite formatting methods such as string.Format().

The formatting supported for parsing is a numeric value optionally suffixed with B, KB, MB, GB, or TB. For example:

StorageValue s1 = StorageValue.Parse("150MB");

The IFormattable formatting supported for conversion to string values is any of the suffixes above, optionally with a leading +/- to enable or disable the display of the suffix, and an optional space in front of the suffix to indicate that a space should be placed between the numeric value and the suffix. If not format is specified then an appropriate unit will be used. Examples:

StorageValue workingSetSize = Process.GetCurrentProcess().WorkingSet64;

// Let the StorageValue class choose the most appropriate units:
applicationLog.WriteInfo(string.Format("Working set size: {0}", workingSetSize));

// Use KB:
applicationLog.WriteInfo(string.Format("Working set size: {0:KB}", workingSetSize));

// Use MB with a space between the value and the suffix:
applicationLog.WriteInfo(string.Format("Working set size: {0: MB}", workingSetSize));

// Use MB but don't display the units:
applicationLog.WriteInfo(string.Format("Working set size: {0:-MB} megabytes", workingSetSize));

One of the big design decisions that needs to be made is whether this type should be a class or a struct. A class is the easy option as its behaviour is likely to be more intuitive as it avoids some of the issues like immutability that structs raise. However, because this type is a thin wrapper around the System.Int64 type, a struct might offer more intuitive syntax and semantics. I decided to write the implementation as a class and then re-work it as a struct to see how the two implementations compared.

The initial implementation as a class is below and the struct implementation will follow in a second post. (Hint: Wait for the struct version, I think it’s better…)

(StorageClass.cs):

namespace Vitality.Common.Types
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Text;

    /// <summary>
    /// Represents a memory or disk storage value.
    /// </summary>
    [Serializable]
    public class StorageValue : IFormattable
    {
        #region Private Constants

        /// <summary>
        /// Constant for one terabyte.
        /// </summary>
        private const long TeraByte = 1024 * 1024 * 1024 * 1024L;

        /// <summary>
        /// Constant for one gigabyte.
        /// </summary>
        private const long GigaByte = 1024 * 1024 * 1024L;

        /// <summary>
        /// Constant for one megabyte.
        /// </summary>
        private const long MegaByte = 1024 * 1024L;

        /// <summary>
        /// Constant for one kilobyte.
        /// </summary>
        private const long KiloByte = 1024L;

        /// <summary>
        /// Constant for one byte.
        /// </summary>
        private const long Byte = 1L;

        #endregion Private Constants

        #region Private Members

        /// <summary>
        /// The storage value in bytes.
        /// </summary>
        private long bytes;

        #endregion Private Members

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the StorageValue class.
        /// </summary>
        /// <param name="bytes">The number of bytes for the storage value.</param>
        public StorageValue(long bytes)
        {
            this.bytes = bytes;
        }

        #endregion Constructors

        #region Public Properties

        /// <summary>
        /// Gets or sets the number of bytes in the StorageValue.
        /// </summary>
        public long Bytes
        {
            get { return this.bytes; }
            set { this.bytes = value; }
        }

        #endregion Public Properties

        #region Public Static Methods

        /// <summary>
        /// Converts the string representation of a storage value to an instance of the StorageValue class.
        /// </summary>
        /// <param name="s">The string representing a storage value and optionally specifying B, KB, MB, GB, or TB as a suffix.</param>
        /// <returns>An instance of the StorageValue class.</returns>
        public static StorageValue Parse(string s)
        {
            long conversionFactor = 1;

            if (s.EndsWith("TB", StringComparison.OrdinalIgnoreCase))
            {
                conversionFactor = StorageValue.TeraByte;
                s = s.Substring(0, s.Length - 2);
            }
            else if (s.EndsWith("GB", StringComparison.OrdinalIgnoreCase))
            {
                conversionFactor = StorageValue.GigaByte;
                s = s.Substring(0, s.Length - 2);
            }
            else if (s.EndsWith("MB", StringComparison.OrdinalIgnoreCase))
            {
                conversionFactor = StorageValue.MegaByte;
                s = s.Substring(0, s.Length - 2);
            }
            else if (s.EndsWith("KB", StringComparison.OrdinalIgnoreCase))
            {
                conversionFactor = StorageValue.KiloByte;
                s = s.Substring(0, s.Length - 2);
            }
            else if (s.EndsWith("B", StringComparison.OrdinalIgnoreCase))
            {
                conversionFactor = StorageValue.Byte;
                s = s.Substring(0, s.Length - 1);
            }
            else
            {
                conversionFactor = StorageValue.Byte;
            }

            double value = double.Parse(s.Trim());

            return new StorageValue((long)(value * conversionFactor));
        }

        /// <summary>
        /// Returns a string representation of the storage value.
        /// </summary>
        /// <param name="bytes">The storage value in bytes.</param>
        /// <returns>A formatted string representing the storage value.</returns>
        public static string ToString(long bytes)
        {
            return StorageValue.ToString(bytes, string.Empty, null);
        }

        /// <summary>
        /// Returns a string representation of the storage value.
        /// </summary>
        /// <param name="bytes">The storage value in bytes.</param>
        /// <param name="format">The format string to use.</param>
        /// <returns>A formatted string representing the storage value.</returns>
        public static string ToString(long bytes, string format)
        {
            return StorageValue.ToString(bytes, format, null);
        }

        /// <summary>
        /// Returns a string representation of the storage value.
        /// </summary>
        /// <param name="bytes">The storage value in bytes.</param>
        /// <param name="format">The format string to use.</param>
        /// <param name="formatProvider">The IFormatProvider to use.</param>
        /// <returns>A formatted string representing the storage value.</returns>
        public static string ToString(long bytes, string format, IFormatProvider formatProvider)
        {
            long conversionFactor = 1L;
            string suffix = string.Empty;
            bool includeSuffix = true;
            bool includeSpace = false;
            bool isUnitsSpecified = false;

            // Format: [+/-][ ]units
            // + : include factor (this is the default if not specified), e.g. "10MB"
            // - : omit factor, e.g. "10"
            // : a space before the units indicates that the value and units should be separated by a space, e.g. "10 MB" rather than "10MB"
            // units : TB, GB, MB, KB, B for, respectively, terabytes, gigabytes, megabytes, kilobytes, and bytes.
            //
            // Examples:
            //
            // "GB" --> "10GB"
            // "+GB" --> "10GB"
            // "+ GB" --> "10 GB"
            // "-GB" --> "10"
            //
            // Commas and decimal places will be used when necessary, e.g. "0.1TB", "102,400KB", etc/
            //
            // The factor argument is not case sensitive, e.g. "10mb", "10MB", "10Mb", and "10mB" are all valid.
            if (!string.IsNullOrEmpty(format))
            {
                if (format.StartsWith("+", StringComparison.OrdinalIgnoreCase))
                {
                    includeSuffix = true;
                    format = format.Substring(1);
                }
                else if (format.StartsWith("-", StringComparison.OrdinalIgnoreCase))
                {
                    includeSuffix = false;
                    format = format.Substring(1);
                }

                if (format.StartsWith(" ", StringComparison.OrdinalIgnoreCase))
                {
                    includeSpace = true;
                    format = format.Substring(1);
                }

                if (string.Compare(format, "TB", true) == 0)
                {
                    conversionFactor = StorageValue.TeraByte;
                    suffix = "TB";
                    isUnitsSpecified = true;
                }
                else if (string.Compare(format, "GB", true) == 0)
                {
                    conversionFactor = StorageValue.GigaByte;
                    suffix = "GB";
                    isUnitsSpecified = true;
                }
                else if (string.Compare(format, "MB", true) == 0)
                {
                    conversionFactor = StorageValue.MegaByte;
                    suffix = "MB";
                    isUnitsSpecified = true;
                }
                else if (string.Compare(format, "KB", true) == 0)
                {
                    conversionFactor = StorageValue.KiloByte;
                    suffix = "KB";
                    isUnitsSpecified = true;
                }
                else if (string.Compare(format, "B", true) == 0)
                {
                    conversionFactor = StorageValue.Byte;
                    suffix = "B";
                    isUnitsSpecified = true;
                }
            }

            if (!isUnitsSpecified)
            {
                // No valid explicit units given so attempt to determine the most suitable to use:
                if (bytes > StorageValue.TeraByte)
                {
                    conversionFactor = StorageValue.TeraByte;
                    suffix = "TB";
                }
                else if (bytes > StorageValue.GigaByte)
                {
                    conversionFactor = StorageValue.GigaByte;
                    suffix = "GB";
                }
                else if (bytes > StorageValue.MegaByte)
                {
                    conversionFactor = StorageValue.MegaByte;
                    suffix = "MB";
                }
                else if (bytes > StorageValue.KiloByte)
                {
                    conversionFactor = StorageValue.KiloByte;
                    suffix = "KB";
                }
                else
                {
                    conversionFactor = StorageValue.Byte;
                    suffix = "B";
                }
            }

            // Format the string according to the IFormatProvider the caller specified, otherwise; use the InvariantCulture:
            NumberFormatInfo numberFormatInfo = null;

            if (formatProvider != null)
            {
                NumberFormatInfo formatProviderNumberFormatInfo = formatProvider.GetFormat(typeof(NumberFormatInfo)) as NumberFormatInfo;

                if (formatProviderNumberFormatInfo != null)
                {
                    numberFormatInfo = formatProviderNumberFormatInfo;
                }
            }

            if (numberFormatInfo == null)
            {
                numberFormatInfo = CultureInfo.InvariantCulture.NumberFormat;
            }

            // Use the culture specific thousands and decimal characters:
            string numberFormatString = string.Format("{{0:#{0}##0{1}##}}{{1}}{{2}}", numberFormatInfo.NumberGroupSeparator, numberFormatInfo.NumberDecimalSeparator);

            return string.Format(numberFormatString, (double)(bytes / (conversionFactor * 1.0d)), includeSpace ? " " : string.Empty, includeSuffix ? suffix : string.Empty);
        }

        #endregion Public Static Methods

        #region Public Methods

        /// <summary>
        /// Returns a string representation of the storage value.
        /// </summary>
        /// <returns>A formatted string representing the storage value.</returns>
        public override string ToString()
        {
            return StorageValue.ToString(this.bytes);
        }

        /// <summary>
        /// Returns a string representation of the storage value.
        /// </summary>
        /// <param name="format">The format string to use.</param>
        /// <returns>A formatted string representing the storage value.</returns>
        public string ToString(string format)
        {
            return StorageValue.ToString(this.bytes, format);
        }

        /// <summary>
        /// Returns a string representation of the storage value.
        /// </summary>
        /// <param name="format">The format string to use.</param>
        /// <param name="formatProvider">The IFormatProvider to use.</param>
        /// <returns>A formatted string representing the storage value.</returns>
        public string ToString(string format, IFormatProvider formatProvider)
        {
            return StorageValue.ToString(this.bytes, format, formatProvider);
        }

        #endregion Public Methods
    }
}



Written by Sea Monkey

January 25, 2017 at 5:08 pm

Posted in Development

Tagged with ,

Creating a tray icon application using NotifyIcon

leave a comment »

There are numerous code examples of how to create a tray icon application using the .NET NotifyIcon class. Many claim to show the ‘proper’ way to do this by using a hidden Windows Form as the main component of the application.

It’s easy to see why some might believe this to be true as the Form class implements IDispose methods and an IContainer for controls such as the System.Windows.Forms Timer control (as opposed to other Timer controls such as System.Timers namespace) that might be useful in a tray application.

However, there really isn’t any need to use a Form and, arguably, it adds problems of its own.

All that’s required is that you implement IDisposable and, if you want, an IContainer:

public class MonitorTrayApp : IDisposable
{
  /// <summary>
  /// Container for the form's components.
  /// </summary>
  private IContainer components = null;

  /// <summary>
  /// The NotifyIcon instance.
  /// </summary>
  private NotifyIcon notifyIcon = null;

  /// <summary>
  /// A timer control to drive the polling of the Vitalpulse Monitor service.
  /// </summary>
  private Timer intervalTimer = null;
 
  /// <summary>
  /// Initializes a new instance of the MonitorTrayApp class.
  /// </summary>
  public MonitorTrayApp()
  {
    this.InitializeComponent();
  }
 
  /// <summary>
  /// Initialises the system tray icon.
  /// </summary>
  private void InitializeComponent()
  {
    this.components = new Container();

    this.notifyIcon = new NotifyIcon(this.components);

    this.intervalTimer = new Timer(this.components);
    this.intervalTimer.Tick += new EventHandler(this.IntervalTimerTick);
    this.intervalTimer.Interval = Settings.Default.PollInterval * 1000;
    this.intervalTimer.Enabled = true;

    this.notifyIcon.Icon = Resources.AppIcon;
    this.notifyIcon.Text = Application.ProductName;
    this.notifyIcon.Visible = true;
  }
 
  /// <summary>
  /// Tick event handler for the interval timer control.
  /// </summary>
  /// <param name="sender">The object raising the event.</param>
  /// <param name="e">The event args.</param>
  private void IntervalTimerTick(object sender, EventArgs e)
  {
    // Do some processing here...
  } 
 
  /// <summary>
  /// Performs application IDisposable processing.
  /// </summary>
  public void Dispose()
  {
    this.Dispose(true);
  }

  /// <summary>
  /// Performs application IDisposable processing.
  /// </summary>
  /// <param name="disposing"></param>
  protected virtual void Dispose(bool disposing)
  {
    if (disposing)
    {
      if (this.components != null)
      {
        this.components.Dispose();
      }

      this.notifyIcon.Dispose();
    }
  }
}

The application’s entry point is a Main() method:

public static class Program
{
  [STAThread]
  public static void Main()
  {
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);

    using (MonitorTrayApp notifierTrayIcon = new MonitorTrayApp())
    {
      Application.Run();
    }
  }
}

Written by Sea Monkey

January 18, 2017 at 8:00 pm

Posted in Development

Tagged with ,

Determining the size required to display text: TextRenderer.MeasureText()

leave a comment »

One of the limitations of the .NET NotifyIcon class’s ShowBalloonTip() method is that there is a limit to the length of text that can be displayed. It’s great for a short warning message but not suitable for a long list of errors.

I’ve just written a tray application that connects to a Windows Service and polls for error messages. Once there are more than four or five error messages, using the ShowBalloonTip() method isn’t appropriate to display these messages so I wrote created a form that mimics the BalloonTip window to some extent but allows more text to be displayed.

As I didn’t want to use scrollbars, I needed to determine how much space is required to display the text and then resize the form and its controls appropriately.

After a bit of searching I found that the System.Windows.Forms namespace contains the TextRenderer class that implements the MeasureText() method. This returns a Size instance indicating the size of the rendered text for the specified Graphics context, text, and Font.

It took a bit of testing to realise that you also need to indicate that text lines can be broken if this is required. Otherwise, regardless of whether the specified Graphics context is not wide enough, the MeasureText() will not wrap or break lines. It will however correctly return the required width even though this is wider than the specific Graphics context.

As an example, my form uses a Label control called MessagesLabel to display the text and this is the call to MeasureText() used to determine the height that the control needs to be to display the text without changing the width of the control:

// Determine how much space the message text requires.
Size textSize = TextRenderer.MeasureText(this.MessagesLabel.CreateGraphics(), this.MessagesLabel.Text, this.MessagesLabel.Font, this.MessagesLabel.Size, TextFormatFlags.WordBreak);

// Adjust the height of the form to accommodate the text to be displayed.
int topMargin = this.MessagesLabel.Top;
int bottomMargin = this.ClientRectangle.Size.Height - this.MessagesLabel.Top - this.MessagesLabel.Height;
int requiredHeight = topMargin + bottomMargin + textSize.Height;

 

 

Written by Sea Monkey

January 16, 2017 at 8:00 pm

Posted in Development

Tagged with ,

Detecting when a program is executing from the Visual Studio IDE

leave a comment »

When developing small ad hoc command line utilities in the Visual Studio IDE I often add a Console.ReadKey() method call so that I can see the console output before the program terminates and returns control the IDE. This call is removed when development is complete but I’ve often thought that it would be useful to detect if the program was executing from the IDE and, if so, pause before exiting.

I finally got round to looking at how to do this yesterday. I’d assumed that simply looking at Assembly.GetEntryAssembly() would be enough to determine that the execution was initiated by the IDE but of course Visual Studio is a lot more sophisticated than this. It appears to create an application Domain and launches the program within this domain. However, as the IDE sets itself as the Domain’s DomainManager this can be used to detect whether the program is executing from the IDE:

    // If we're running from the Visual Studio IDE then pause before exiting:
    if (AppDomain.CurrentDomain.DomainManager != null && AppDomain.CurrentDomain.DomainManager.GetType().FullName == "Microsoft.VisualStudio.HostingProcess.VSHostAppDomainManager")
    {
        Console.WriteLine();
        Console.WriteLine("Press any key to exit and return to Visual Studio...");
        Console.ReadKey();
    }

This works for Visual Studio 2010 but I haven’t tested it against other versions yet.

Written by Sea Monkey

October 20, 2016 at 7:00 pm

Posted in Development

Tagged with ,

Line endings in HL7 messages

leave a comment »

Despite working with HL7 messages for several years I got caught out by what type of line endings to use when constructing an HL7 message.

I should point out that this is usually handled by the API or integration engine being used but I just needed a message file that I could throw at an MLLP Sender to test it. I remembered that the line endings were not Windows style CR/LF (0x0D/0x0A) and assumed it was UNIX style LF (0x0A) only to find that the message processor at the other end of the MLLP connection failed to parse the message correctly.

I checked the HL7 specifications and was surprised to find that the segment separator character is not 0x0A as you might expect but 0x0D.

So, if you want to think of segment separators as line ends, this corresponds to the old Mac line end style rather than UNIX.

Written by Sea Monkey

February 3, 2014 at 7:00 pm

Posted in Development, HL7

Tagged with ,

MySQL Error Code: 1034. 2 when fixing table

leave a comment »

After restoring a database and applying some changes to it, I started getting this error when I tried to run an INSERT statement:

Error Code: 1034. 2 when fixing table

The server error log showed the same error but with different text:

130912 9:27:08 [Warning] Warning: Enabling keys got errno 2 on vitality.cached_information_schema_columns, retrying

The INSERT statement seemed simple enough:

INSERT INTO cached_information_schema_columns(TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME)
SELECT DATABASE(), C.TABLE_NAME, C.COLUMN_NAME FROM information_schema.columns AS C
LEFT JOIN information_schema.tables AS T ON C.TABLE_NAME=T.TABLE_NAME
WHERE C.TABLE_SCHEMA=DATABASE()
AND T.TABLE_SCHEMA=DATABASE()
AND T.TABLE_TYPE='Base Table';

and, curiously, the SELECT statement executed successfully on its own.

Fortunately Rob had seen something similar before and suggested that I checked the Views tab of the schema display in MySQL Administrator. There were five views that had a red message in the Description column indicating that the view referenced invalid tables or columns or functions.
After dropping these views and recreating them with corrections where required, the INSERT statement executed successfully.

We’re not quite sure what the problem is but it appears that the invalid views are causing a problem with the schema query (SELECT statement) when it’s used in the INSERT statement. The schema query executes successfully on its own and I can also execute INSERT statements with VALUE clauses but the combination of INSERT INTO and SELECT always fails until the problem with the views is resolved.

Written by Sea Monkey

September 12, 2013 at 8:00 pm

Posted in Development

Tagged with