Archive for the ‘Development’ Category.

WIX: Creating an MSI and Deploying Your First File

Windows Install XML (WIX) is a nice simple way to create an MSI installation file.  Some of the items it automatically handles, such as registering with Add / Remove Programs, are very nice features.

In this example we are going to create a simple MSI that deploys a single file.

Prequisites

Step 1 – Create a WIX project in Visual Studio

Creating a new project is a very simple task once you have done it a few times.  However, I try to make my walk-thrus newbie proof, so even some one who has never done this feels comfortable. So if you need help with this step, use my instructions below.  If you don’t, skip them.

  1. Open Visual Studio if it is not already open.
  2. Got to File | New | Project.
  3. You should have an option under Installed Templates for Windows Install XML. Select it.
  4. Now you should see the option for Setup Project.  Select it.
  5. Enter a Name for the project. I called the project I made for this walk-thru DeployOneFile.
  6. Change the directory to store the project if you want.  It doesn’t matter what directory you choose, but it is nice to keep your learning projects organized.
  7. Click OK.

Your project should now be created.  In solution explorer you should now have a solution, a project, a reference and the Product.wxs.  See the image below.

Ok, I hope that was easy for you.  Let move on.

Step 2 – Add a file to the project

These steps are preformed in Visual Studio on the project you just created in the above step.

  1. Right-click on the project name, DeployOneFile, and from the drop down, choose Add | New Item.
  2. Select Text File.
  3. Name it whatever you want. I  used Install.conf for this example.
  4. Click Add.

Your file is now added to the Visual Studio project.  However, it is not automatically added into the Product.wxs as a file to be installed. This is done manually in the next step.

Step 3 – Take a moment to learn

We are going to change the Product.wxs file to include the file we just created.

The Product.wxs file has the following XML text in it.  Take a moment to look at the XML nodes and their elements so are familiar with the syntax it is using.

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
	<Product Id="fae97512-4eca-4271-821d-75b7a8861557" Name="DeployOneFile" Language="1033" Version="1.0.0.0" Manufacturer="DeployOneFile" UpgradeCode="fec17f7b-060e-466f-8bd2-7895eb2b92ce">
		<Package InstallerVersion="200" Compressed="yes" />

		<Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />

		<Directory Id="TARGETDIR" Name="SourceDir">
			<Directory Id="ProgramFilesFolder">
				<Directory Id="INSTALLLOCATION" Name="DeployOneFile">
					<!-- TODO: Remove the comments around this Component element and the ComponentRef below in order to add resources to this installer. -->
					<!-- <Component Id="ProductComponent" Guid="927a42ea-a6df-45f6-ac52-4b979194bc10"> -->
						<!-- TODO: Insert files, registry keys, and other resources here. -->
					<!-- </Component> -->
				</Directory>
			</Directory>
		</Directory>

		<Feature Id="ProductFeature" Title="DeployOneFile" Level="1">
			<!-- TODO: Remove the comments around this ComponentRef element and the Component above in order to add resources to this installer. -->
			<!-- <ComponentRef Id="ProductComponent" /> -->

			<!-- Note: The following ComponentGroupRef is required to pull in generated authoring from project references. -->
			<ComponentGroupRef Id="Product.Generated" />
		</Feature>
	</Product>
</Wix>

The text of the Xml gives us hints as to what we are supposed to do in its comments.  Take a moment and read the comments.

Step 4 – Configure the WIX Xml file to deploy that file

Lets start editing that XML file.

Note: I am going to use the term “node’ to indicate and XML section. So and everything it contains is a node. If a node is inside it, I might call it a subnode.

  1. Uncomment the Component node the Directory nodes.
  2. Remove the two TODO: comments.
  3. Uncomment the ComponentRef node that is inside the Feature node.
  4. Remove the TODO: comment and the Note comment.
  5. Inside the Component node, at a File node as follows:
    <File Id='Install.conf_id' Name='Install.conf' DiskId='1' Source='Install.conf' KeyPath='yes' />
    

Ok, you are done.  Yes, that was all there is too it.

You XML syntax should now look as follows:

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <Product Id="fae97512-4eca-4271-821d-75b7a8861557" Name="DeployOneFile" Language="1033" Version="1.0.0.0" Manufacturer="DeployOneFile" UpgradeCode="fec17f7b-060e-466f-8bd2-7895eb2b92ce">
    <Package InstallerVersion="200" Compressed="yes" />

    <Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />

    <Directory Id="TARGETDIR" Name="SourceDir">
      <Directory Id="ProgramFilesFolder">
        <Directory Id="INSTALLLOCATION" Name="DeployOneFile">
          <Component Id="ProductComponent" Guid="927a42ea-a6df-45f6-ac52-4b979194bc10">
            <File Id='Install.conf_id' Name='Install.conf' DiskId='1' Source='Install.conf' KeyPath='yes' />
          </Component>
        </Directory>
      </Directory>
    </Directory>

    <Feature Id="ProductFeature" Title="DeployOneFile" Level="1">
      <ComponentRef Id="ProductComponent" />
      <ComponentGroupRef Id="Product.Generated" />
    </Feature>
  </Product>
</Wix>

Ok, you are ready to build.

Step 4 – Build the project

Well, there really isn’t much to debug, so we are only going to build a release version here.

  1. In the Visual Studio 2010 tool bar, there should be a drop down box that either says Debug or Release.  Change it to Release if it is not already at Release.
  2. Select Build | Build Solution or use the shortcut key.

You should now have an MSI built in the project’s bin\release directory.

Step 5 – Test the MSI

Let’s go get the MSI and test it.

  1. Right-click on the project, DeployOneFile, and choose, Open Folder in Windows Explorer.
  2. In Explorer, navigate into the bin\release directory.
  3. You will see two files:
    DeployOneFile.msi – This is the MSI and is all you need.
    DeployOneFile.wixpdb – This is a file for debugging only. You may never use it unless you need to debug.
  4. You may or may not want to test the MSI on you development box. If  you do, just double-click the MSI.  Otherwise, copy it to a test box and run the MSI there.
  5. Verify that the file installed.
    Note: If on a 64 bit system, it will by default install as an x86 app, so look in c:\program files (x86)\ for a folder called DeployOneFile.
  6. Check Add / Remove Programs to make sure the install shows up there and that the uninstall works.

Congratulations. You just use WIX to create and deploy your first MSI.

 

Bonus Information – Learning about Repair

Repairing an installation is an important feature of  to understand.

What you are going to learn here, is that if you delete a file in a component that has the KeyPath=’yes’ tag, the component will reinstall.

  1. Make sure the MSI you just created above is installed. If you uninstalled it doing steps above, reinstall it.
  2. On the machine where you installed the MSI, Browse to the installation directory.My directory is here.
    C:\Program Files (x86)\DeployOneFile
  3. The only file in that directory is the file you installed:
    Install.conf
  4. Delete the Install.conf file.
  5. Still on the machine where you installed the MSI, go to Add / Remove Programs.
  6. Right-click on the DeployOneFile instance and choose Repair.
  7. The file you deleted should be restored.

Ok, so you probably have questions about repair.  This was just a basic introduction.  Hopefully we will learn more about repair as we go on.

Running Unit Tests in Visual Studio 2010: DebugTestsInClass

Problem

So, I am trying to run Unit tests in Visual Studio 2010.  However, it isn’t working.

I am getting this message in the status bar.

The key combination (Ctrl + R, Ctrl + C) is bound to command (DebugTestsInClass) which is currently not available.

I do have Visual Studio 2010 Ultimate so I expected everything to be available.

I did a Google and Bing and MSDN search for “DebugTestsInClass” and got a big fat nothing, which is pretty difficult to do these days. (Of course, that won’t happen for the next guy now that I have made this post!)

Anybody know what the DebugTestsInClass is and how to make it available?

Cause

Ok, so I found the issue.  My test project was created a long time ago in Visual Studio 2008 and using MBUnit.

I had changed the test to use no longer use MBUnit, but to use Microsoft.VisualStudio.QualityTools.UnitTestFramework.

I had create a new test project by click Test | New Test.

Then migrate my test classes to the new project.

I was unaware that the test project must be .NET 4, but yes it does.  Creating a new Test project allows me to use my Ctrl + R, Ctrl + C  functionality.

At first, I thought that using .NET 4 might be a problem because everything else we are doing is currently in .NET 3.5.  However, even though we are developing in .NET 3.5 currently, our test projects can be .NET 4 as they only run on dev and build machines.

Resolution

So it looks like multiple steps were required to move my test project from one testing library to another.

  1. Create a new Test project by going to Test | New Test.
  2. Move the classes to the new test project.
  3. Change the class syntax to match the syntax specified by Microsoft.VisualStudio.QualityTools.UnitTestFramework.
  4. Add any references needed.

 

Avoiding the MSVCR100.dll, MSVCP100D.dll, or MSVCR100D.dll is missing error (Also MSVCR110.dll, MSVCR120.dll, MSVCR140.dll)

Note: This also applies to newer MSVCR110.dll, MSVCR120.dll and MSVCR140.dll.

MSVCR100.dll

This msvcr100.dll is the Microsoft Visual C++ Redistributable dll that is needed for projects built with Visual Studio 2010. The dll letters spell this out.

MS = Microsoft
V = Visual
C = C program language
R = Run-time
100 = Version

If you create a C++ project in Visual Studio 2010, this file is probably needed.

MSVCP100.dll

This msvcp100.dll is the Microsoft Visual C++ Redistributable dll that is needed for projects built with Visual Studio 2010. The dll letters spell this out.

MS = Microsoft
V = Visual
CP = C++
100 = version

If you create a C++ project in Visual Studio 2010, this file is probably needed.

MSVCR100D.dll

The MSVCR100D.dll is almost the exact same file only the D at the end stands for Debug. This file has debugging enabled and is not considered redistributable.

Why the error?

Ok, so recently I switched to Visual Studio 2010.  I had a C++ application that worked perfectly in Visual Studio 2008.  Once I compiled it with Visual Studio 2010 and ran it on a clean 2008 server (fully patched but otherwise clean), it failed to run with the following error.

TestWin32.exe – System Error

The program can’t start because MSVCR100.dll is missing from your computer. Try reinstalling the program to fix this problem.

Here is the screen shot:

msvcr100.dll

The same things happens with the debug version of the file, only it is a the debug version of the same DLL as noted by the fact that the DLL name ends with D.

Autorun – System Error

The program can’t start because MSVCR100.dll is missing from your computer. Try reinstalling the program to fix this problem.

The screen shot is identical except for the D in the dll name.

msvcr100d.dll

I create a new project in Visual Studio 2010 using the project type of C++ Win32 Project and without making a single change to the default project, I built the file and tested it on my clean machine and the same issue occurred.

So obviously that is not acceptable.  It seems like this should just not happen by default, but unfortunately it does.

Solution

It was actually really easy to resolve for my one project.

Here is what I did.

You can solve this any of the following ways:

  1. Statically link to the dll files so they are compiled into my executable instead of referenced as separate dll files.
  2. Included the dll in the same directory as the exe (I actually didn’t try this but I assume it would work).
  3. Forced everyone to install the VC++ Runtime Redistributable before running the app.

The first option seems the most stable and robust and easiest for a single executable. So that is the one I am going to use.

The second option doesn’t really make sense to me and I would probably never do it. Maybe if I had dozens of executable files that all required the same DLL and I didn’t have an installer, and I wanted to conserve size, which probably wouldn’t happen for me since I am pretty good at creating a quick installer. Though you might be in this a situation.

The third option would make sense if I was planning on running my executable after an install. During the install I could include the VC++ Runtime Redistributable and all would be fine.

Statically Linking the DLLs

Make sure you resolve it for both Release and Debug. The steps are slightly different.

Release

  1. In Visual Studio, I went to the project Properties.
  2. I changed my Configuration to Release.
  3. I went under Configuration Properties | C/C++ | Code Generation
  4. Look at the Runtime Library setting.  It is set to this: Multi-threaded DLL (/MD)
    Change it to this: Multi-threaded (/MT)
  5. Rebuild.

Debug

Almost exactly the same as release.

  1. In Visual Studio, I went to the project Properties.
  2. I changed my Configuration to Debug.
  3. I went under Configuration Properties | C/C++ | Code Generation
  4. Look at the Runtime Library setting. It is set to this: Multi-threaded Debug DLL (/MDd)
    Change it to this: Multi-threaded Debug (/MTd)
  5. Rebuild the debug

It might be a good idea for me to figure out how to change the project so when I create a new project of this type, those settings are the default.

Install the VC++ Runtime Redistributable

Release

Download the appropriate version of VC++ Runtime Redistributable:

File Version VC++ Runtime Version
MSVCR100.dll Microsoft Visual C++ 2010 Redistributable Package (x86)
MSVCR110.dll Microsoft Visual C++ 2012 Redistributable Package (x86)
MSVCR120.dll Microsoft Visual C++ 2013 Redistributable Package (x86)
MSVCR140.dll Microsoft Visual C++ 2015 Redistributable Package (x86)

Just google these and you will find the download links. Install the correct version and you are good to go.

Debug

If you are missing MSVCR100D.dll, then that is a debug dll and is not part of the free to download Visual C++ Redistributable package. So you pretty much are stuck with copying it from your dev box. Copy it to the C:\Windows\System32 directory. You need admin privileges to do this.

Note: You would never want to redistribute the debug dll anyway.

How to document a function so Visual Studio's Intellisense displays it?

So, when I code, I am usually in Visual Studio and I am used to writing documentation above my functions as follows:

        /*
         * The is SomeFunction that does some action.
         */
        private void SomeFunction(int inSomeValue)
        {
                // write code here
        }

However, it annoys me that this information doesn’t show up in Visual Studio’s Intellisense. So I took time to look up the proper way to make function documentation show up in Intellisense.

It turns out that you can type /// above a function and Visual Studio will automagically populate the markup needed to have your comments show up in intellisense.

        /// <summary>
        ///  The is SomeFunction that does some action.
        /// </summary>
        /// <param name="inSomeValue">Enter an integer as some value here.</param>
        private void SomeFunction(int inSomeValue)
        {
                // write code here
        }

So it seems if you use this syntax, the function documentation will now show up in Visual Studio’s Intellisense.

Why my categories span such broad topics?

Hey all,

I recently noticed I was added to another blogs blogroll.  It was funny what he said:

Rhyous’s 127.0.0.1 or ::1 covers an interesting mix of FreeBSD, PC-BSD, C# and even Windows 7 issues. An unusual mix…

Well, here is the deal.

  1. One of my hobbies is learning about open source.  My operating system of choice is FreeBSD and I am partial to PC-BSD for desktop use as well.  So naturally, I am going to post about that.
  2. However, I work for LANDesk as a Developer (previously I was a Level III support engineer), using primarily C#.  We have a lot of legacy C++ code as well.  LANDesk is mostly a Microsoft shop.  Our server software runs on Windows Servers only and we support MS SQL and Oracle back ends.  Our agent goes to Windows, Mac, Linux, and some Unix devices.  Yes, I have had the Linux version of the LANDesk agent running on FreeBSD using Linux Emulation.  I am probably going to post more on WIX now as I am on a development team that deals with our product installation.
  3. I am also just a techie in the first place so I have lots of tech interests outside my career and my hobbies.
  4. I now realize the importance of experience and I want to increase my experience and make my experience more meaningful by retaining it.  I used to do support for Nortel Networks equipment before coming to LANDesk so I have intense networking skills that are uncommon among developers. I also have 10+ years of support and troubleshooting which gives my a unique perspective.  The more you do something the easier it is.  I have a lot of experiences where some support guys were working on something for days and I walk over and solve the problem in five minutes.  It is something that comes with 10+ years experience or troubleshooting. No, that is not unique to me.  LANDesk tech support engineers are some of the best out there and there are a number of guys that can do that here.  But all of them have about 10+ years experience.
  5. I help others with their computer issues which often leads to figuring things out.

My one regret is that I didn’t start my blog in 10+ years ago.  Think of the content I would have had.

So anytime I have to look something up or research a problem and the first or second Google link is not an exact answer, I am going to blog about it, whatever the topic may be.

A DottedDecimal Object

Ok, so there are a lot of objects that are represented in dotted decimal notation.  The most common are versions and IP addresses.

Version 3.1.9.27

IP Address: 192.168.0.1

I have to wonder why I have never found in the Standard C++ Library, or in the C# libraries an object for these?  Are they there and I just don’t know how to find them.  It seems they are always just treated as Strings and this makes no sense to me.  Also these seem common enough that they should be standard objects in all languages.

Which IP Address is greater?

192.168.0.2
192.168.0.100

Well, since these are usually treated as strings, then .2 is greater than .100.  Unfortunately that is not correct.  We all know that .100 is greater.

So I created some objects that overload the >,>=,<=,<,==,!= functions.  Maybe these are completely finished, but hey, they are a start. I created C# and C++ versions.  The C# is first, scroll down if you are looking for C++ versions. This is really great for versions that can be different.  However, I think that for an IP address object, that because it is limited to three characters and each section is one byte and only can be seen as 0-255, that a very efficient object could be created, but for now, a more generic DottedDecimal object is fine, though if you had a large list of IP addresses, you may want that efficiency. Also, this is only tested with digits 0-9, not hex, so there is plenty more work to do, but usually version are just 0-9, though sometimes people throw in an "a" or "b" build such as 1.0.0.1a.  That is not handled yet.  So again, much more work to do.  But for my needs these are more than enough for now. If there are already objects like this that are awesome, efficient, tested, and free, let me know.

C# DottedDecimal object for IP address and Versions

For C#, I implement a lot of interfaces too as you can see in the object.

DottedDecimal.cs

using System;
using System.Collections.Generic;
using System.Text;

namespace DottedDecimalProject
{
    public class DottedDecimal
        : IComparable, IComparable<DottedDecimal>, IComparable<String>, ICloneable, IEquatable<string>, IEquatable<DottedDecimal>
    {
        #region Member Variables
        List<long> _DecimalValues;
        CompareDirection _CompareDirection = CompareDirection.LeftToRight;
        #endregion

        #region Constructors
        /*
         * The default constuctor.
         * The default compare direction is left to right
         * No values are added by default.
         */
        public DottedDecimal()
        {
        }

        /*
         * This constructor takes a string in this regex format
         * [0-9]+(\.[0-9])*
         */
        public DottedDecimal(String inDottedDecimalString)
        {
            _DecimalValues = null;
            _DecimalValues = StringToDottedDecimalList(inDottedDecimalString);
        }

        /*
         * This constructor takes two parameters.
         * Parameter 1 is a string in this regex format: [0-9]+(\.[0-9])*
         * Parameter 2 sets the compare direction. See this.Direction.
         */
        public DottedDecimal(String inDottedDecimalString, CompareDirection inCompareDirection)
        {
            _CompareDirection = inCompareDirection;
            _DecimalValues = null;
            _DecimalValues = StringToDottedDecimalList(inDottedDecimalString);
        }
        #endregion

        #region Properties
        /*
         * Returns the dotted decimal object in string format.
         */
        public String DottedDecimalString
        {
            get { return DottedDecimalListToString(); }
            set { _DecimalValues = StringToDottedDecimalList(value); }
        }

        /*
         * The decimal values are stored in order.  If LeftToRight, the left
         * most value is first.  If RightToLeft, the right most value is first.
         */
        public List<long> DecimalValues
        {
            get { return _DecimalValues; }
            set { _DecimalValues = value; }
        }

        /*
         * Determines whether to compare left to right or right to left.
         *
         * LeftToRight - 1.0.0.1 is greater than 1.0.0.0
         *               1.0.10 is greater than 1.0.0.27
         *
         * RightToLeft - 1.0.0.1 is less than 1.0.0.0
         *             - 1.0.10 is less than 1.0.0.27
         */
        public CompareDirection Direction
        {
            get { return _CompareDirection; }
            set
            {
                if (!(this.Direction == value))
                {
                    this._DecimalValues.Reverse();
                }
                _CompareDirection = value;
            }
        }
        #endregion

        #region Functions
        /*
         * Verifies that the CompareDirection values match between two DottedDecimal objects.
         */
        private static bool CompareDirectionsMatch(DottedDecimal left, DottedDecimal right)
        {
            if (left.Direction == right.Direction)
                return true;
            else
                return false;
        }

        /*
         * Overloads the greater than operator (>) to allow for a syntax as follows:
         *
         *      bool b = dd1 > dd2;
         */
        public static bool operator >(DottedDecimal left, DottedDecimal right)
        {
            int count = (left.DecimalValues.Count > right.DecimalValues.Count) ? right.DecimalValues.Count : left.DecimalValues.Count;

            for (int i = 0; i < count; i++)
            {
                // If left side is greater then true;
                if (left.DecimalValues&#91;i&#93; > right.DecimalValues[i])
                {
                    return true;
                }
                // If right side is greater then false;
                if (left.DecimalValues[i] < right.DecimalValues&#91;i&#93;)
                {
                    return false;
                }
                // If it is equal, check the next decimal values over
                if (left.DecimalValues&#91;i&#93; == right.DecimalValues&#91;i&#93;)
                {
                    continue;
                }
            }

            if (left.DecimalValues.Count > right.DecimalValues.Count)
            {
                // If the left side has the same values as the right,
                // but then has more values, true.
                return true;
            }

            if (left.DecimalValues.Count < right.DecimalValues.Count)
            {
                // If the left side has the same values as the right,
                // but then the right side has more values, false.
                return false;
            }
            // If we get here both sides are equals, so false
            return false;
        }

        /*
         * Overloads the less than operator (<) to allow for a syntax as follows:
         *
         *      bool b = dd1 < dd2;
         */
        public static bool operator <(DottedDecimal left, DottedDecimal right)
        {
            int count = (left.DecimalValues.Count > right.DecimalValues.Count) ? right.DecimalValues.Count : left.DecimalValues.Count;
            for (int i = 0; i < count; i++)
            {
                // If right side is greater then true;
                if (left.DecimalValues&#91;i&#93; < right.DecimalValues&#91;i&#93;)
                {
                    return true;
                }
                // If the left is greater then false;
                if (left.DecimalValues&#91;i&#93; > right.DecimalValues[i])
                {
                    return false;
                }
                // If it is equal, check the next decimal values over
                if (left.DecimalValues[i] == right.DecimalValues[i])
                {
                    continue;
                }
            }

            if (left.DecimalValues.Count > right.DecimalValues.Count)
            {
                // If the left side has the same values as the right,
                // but then has more values, false.
                return false;
            }

            if (left.DecimalValues.Count < right.DecimalValues.Count)
            {
                // If the left side has the same values as the right,
                // but then the right side has more values, true.
                return true;
            }
            // If we get here both sides are equals, so false
            return false;
        }

        /*
         * Overloads the equals operator (==) to allow for a syntax as follows:
         *
         *      bool b = dd1 == dd2;
         */
        public static bool operator ==(DottedDecimal left, DottedDecimal right)
        {
            // If there are more values in either side, they aren't equal
            if (!(left.DecimalValues.Count == right.DecimalValues.Count))
            {
                return false;
            }
            for (int i = 0; i < left.DecimalValues.Count; i++)
            {
                // If any one value is not equal, then false
                if (left.DecimalValues&#91;i&#93; != right.DecimalValues&#91;i&#93;)
                {
                    return false;
                }
            }
            // If you get here they are all equal so true
            return true;
        }

        /*
         * Overloads the not equals operator (!=) to allow for a syntax as follows:
         *
         *      bool b = dd1 != dd2;
         */
        public static bool operator !=(DottedDecimal left, DottedDecimal right)
        {
            // If there are more values in either side, they aren't equal
            if (!(left.DecimalValues.Count == right.DecimalValues.Count))
            {
                return true;
            }
            for (int i = 0; i < left.DecimalValues.Count; i++)
            {
                // If any one value is not equal, then true
                if (left.DecimalValues&#91;i&#93; != right.DecimalValues&#91;i&#93;)
                {
                    return true;
                }
            }
            // If you get here they are all equal so false
            return false;
        }

        public override bool Equals(object obj)
        {
            return base.Equals(obj);
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public override string ToString()
        {
            return DottedDecimalString;
        }

        /*
         * Appends a new string value to the left side of a DottedDecimal object.  Adding "12" to
         * 1.0.0 makes it 1.0.0.12.  Because it is a string, you can also add multiple values at
         * a time so adding the string "12.24" makes 1.0.0.12.24.
         */
        void AddToLeftSide(String inVal)
        {
            foreach (string decimalString in inVal.Split('.'))
            {
                _DecimalValues.Insert(0, Convert.ToInt64(inVal));
            }
        }

        /*
         * Appends a new value to the left side of a DottedDecimal object.  Adding 12 to
         * 1.0.0 makes it 1.0.0.12
         */
        public void AddToLeftSide(long inVal)
        {
            _DecimalValues.Insert(0, inVal);
        }

        /*
         * Appends a new string value to the right side of a DottedDecimal object.  Adding "12" to
         * 1.0.0 makes it 12.1.0.0.  Because it is a string, you can also add multiple values at
         * a time so adding the string "12.24" makes 12.24.1.0.0.
         */
        public void AddToRightSide(String inVal)
        {
            foreach (string decimalString in inVal.Split('.'))
            {
                _DecimalValues.Add(Convert.ToInt64(decimalString));
            }
        }

        /*
         * Appends a new value to the right side of a DottedDecimal object.  Adding 12 to
         * 1.0.0 makes it 12.1.0.0
         */
        public void AddToRightSide(long inVal)
        {
            _DecimalValues.Add(inVal);
        }

        private string DottedDecimalListToString()
        {
            string retVal = "";
            if (this.Direction == CompareDirection.LeftToRight)
            {
                foreach (long l in _DecimalValues)
                {
                    if (!retVal.Equals(""))
                    {
                        retVal += ".";
                    }
                    retVal += l;
                }
            }
            else
            {
                for (int i = _DecimalValues.Count - 1; i >= 0; i--)
                {
                    if (!retVal.Equals(""))
                    {
                        retVal += ".";
                    }
                    retVal += _DecimalValues[i];
                }
            }
            return retVal;
        }

        private List<long> StringToDottedDecimalList(String inString)
        {
            List<long> retList = new List<long>();
            foreach (string decimalString in inString.Split('.'))
            {
                retList.Add(Convert.ToInt64(decimalString));
            }
            if (this.Direction == CompareDirection.RightToLeft)
            {
                retList.Reverse();
            }
            return retList;
        }

        #endregion

        #region Interface Functions

        #region IComparable Members
        public int CompareTo(object inOjbect)
        {
            DottedDecimal dd = (DottedDecimal)inOjbect;
            return CompareTo(dd);
        }
        #endregion

        #region IComparable<DottedDecimal> Members
        public int CompareTo(DottedDecimal inDottedDecimal)
        {
            if (this < inDottedDecimal)
                return -1;
            if (this == inDottedDecimal)
                return 0;
            if (this > inDottedDecimal)
                return 1;
            return -2; // Should never get here.
        }
        #endregion

        #region IComparable<string> Members
        public int CompareTo(string inString)
        {
            DottedDecimal dd = new DottedDecimal(inString);
            return CompareTo(dd);
        }
        #endregion

        #region ICloneable Members
        public object Clone()
        {
            return new DottedDecimal(this.DottedDecimalString, this.Direction);
        }

        #endregion

        #region IEquatable<string> Members
        public bool Equals(string inString)
        {
            DottedDecimal dd = new DottedDecimal(inString);
            return this == dd;
        }

        #endregion
        #region IEquatable<DottedDecimal> Members
        public bool Equals(DottedDecimal inDottedDecimal)
        {
            return this == inDottedDecimal;
        }
        #endregion

        #endregion

        #region Enums
        public enum CompareDirection
        {
            LeftToRight,
            RightToLeft
        }
        #endregion
    }
}

C++ DottedDecimal object for IP address and Versions

I tried to overload the common operators, if there is one you would like overloaded, let me know.

DottedDecimal.h

#pragma once
#include <vector>
#include <iostream>
#include "windows.h"

using namespace std;

class DottedDecimal
{
public:
	// Constructors
	DottedDecimal(); // Default constructor
	DottedDecimal(DottedDecimal & inDottedDecimal); // Copy constructor
	DottedDecimal(string inString);
	DottedDecimal(char * inString);
	DottedDecimal(LPTSTR inString);

	// Destructors
	~DottedDecimal();

	// Public functions
	string GetDottedDecimal();

	template <class T>
	void SetDottedDecimal(const T& t);

	vector<long> GetDecimals();

	// Functions Overloading Operators
	friend ostream &operator<<(ostream & dataStream, DottedDecimal & dd);

	friend bool operator==(DottedDecimal & left, DottedDecimal & right);
	friend bool operator!=(DottedDecimal & left, DottedDecimal & right);

	friend bool operator>(DottedDecimal & left, DottedDecimal & right);
	friend bool operator>=(DottedDecimal & left, DottedDecimal & right);

	friend bool operator<(DottedDecimal & left, DottedDecimal & right);
	friend bool operator<=(DottedDecimal & left, DottedDecimal & right);

private:
	// Member Variables
	vector<long> _decimals;

	// Private Functions
	void StringSplit(string inString, string inDelim, vector<string> * outResults);

	template <class T>
	string AnyTypeToString(const T& t);

	template <class T>
	void StringToAnyType(T& t, std::string inString);
};

DottedDecimal.cpp

#include "StdAfx.h"
#include "DottedDecimal.h"
#include <iostream>
#include <sstream>

using namespace std;

DottedDecimal::DottedDecimal()
{
}

DottedDecimal::DottedDecimal(DottedDecimal & inDottedDecimal)
{
	SetDottedDecimal(inDottedDecimal.GetDottedDecimal());
}

DottedDecimal::DottedDecimal(string inString)
{
	SetDottedDecimal(inString);
}

DottedDecimal::DottedDecimal(LPTSTR inString)
{
	wstring ws = wstring(inString);
	string s;
	s.assign(ws.begin(), ws.end());
	SetDottedDecimal(s);
}

DottedDecimal::DottedDecimal(char * inString)
{
	SetDottedDecimal(inString);
}

DottedDecimal::~DottedDecimal()
{
}

string DottedDecimal::GetDottedDecimal()
{
	string retVal = "";
	for (unsigned short i = 0; i < _decimals.size(); i++)
	{
		if (retVal.compare("") != 0)
		{
			retVal += ".";
		}
		retVal += AnyTypeToString(_decimals.at(i));
	}
	return retVal;
}

template <class T>
void DottedDecimal::SetDottedDecimal(const T& t)
{
	_decimals.clear();
	string valueString = AnyTypeToString(t);
	vector<string> * values = new vector<string>();
	StringSplit(valueString, ".", values);
	for (unsigned short i = 0; i < values->size(); i++)
	{
		long l;
		StringToAnyType(l, values->at(i));
		_decimals.push_back(l);
	}
	delete values;
}

vector<long> DottedDecimal::GetDecimals()
{
	return _decimals;
}

ostream &operator<<(ostream & dataStream, DottedDecimal & dd)
{
	dataStream << dd.GetDottedDecimal();
	return dataStream;
}

bool operator==(DottedDecimal & left, DottedDecimal & right)
{
	// If the value count isn't the same, then false
	if (left.GetDecimals().size() != right.GetDecimals().size())
		return false;

	for (unsigned short i = 0; i < left.GetDecimals().size(); i++)
	{
		// If at any time values don't match, return false
		if (left.GetDecimals().at(i) != right.GetDecimals().at(i))
			return false;
	}

	// If you get here they are the same.
	return true;
}

bool operator!=(DottedDecimal & left, DottedDecimal & right)
{
	// If the value count isn't the same, then true
	if (left.GetDecimals().size() != right.GetDecimals().size())
		return true;

	for (unsigned short i = 0; i < left.GetDecimals().size(); i++)
	{
		// If at any time values don't match, return true
		if (left.GetDecimals().at(i) != right.GetDecimals().at(i))
			return true;
	}

	// If you get here they are the same.
	return false;
}

bool operator>(DottedDecimal & left, DottedDecimal & right)
{
	// If one has three values and the other has four, only check three
	short count = (left.GetDecimals().size() <= right.GetDecimals().size() ? left.GetDecimals().size() : right.GetDecimals().size());
	for (unsigned short i = 0; i < count; i++)
	{
		if (left.GetDecimals().at(i) > right.GetDecimals().at(i))
			return true;
	}

	// If you get here, then the checked values were the same.
	// Return true if the left side has more values than the right side.
	if (left.GetDecimals().size() > right.GetDecimals().size() )
		return true;
	else
		return false;
}

bool operator>=(DottedDecimal & left, DottedDecimal & right)
{
	// If one has three values and the other has four, only check three
	short count = (left.GetDecimals().size() <= right.GetDecimals().size() ? left.GetDecimals().size() : right.GetDecimals().size());
	for (unsigned short i = 0; i < count; i++)
	{
		// If any compared value is greater, return true;
		if (left.GetDecimals().at(i) > right.GetDecimals().at(i))
			return true;
	}

	// If you get here, then the checked values were the same.
	// Return true if the left side has more values than or the same values as the right side.
	return left.GetDecimals().size() >= right.GetDecimals().size();
}

bool operator<(DottedDecimal & left, DottedDecimal & right)
{
// If one has three values and the other has four, only check three
	short count = (left.GetDecimals().size() <= right.GetDecimals().size() ? left.GetDecimals().size() : right.GetDecimals().size());
	for (unsigned short i = 0; i < count; i++)
	{
		// If any compared value is less, return true;
		if (left.GetDecimals().at(i) < right.GetDecimals().at(i))
			return true;
	}

	// If you get here, then the checked values were the same.
	// Return true if the left side has less values than the right side.
	return left.GetDecimals().size() < right.GetDecimals().size();
}

bool operator<=(DottedDecimal & left, DottedDecimal & right)
{
	// If one has three values and the other has four, only check three
	short count = (left.GetDecimals().size() <= right.GetDecimals().size() ? left.GetDecimals().size() : right.GetDecimals().size());
	for (unsigned short i = 0; i < count; i++)
	{
		// If any compared value is greater, return true;
		if (left.GetDecimals().at(i) > right.GetDecimals().at(i))
			return true;
	}

	// If you get here, then the checked values were the same.
	// Return true if the left side has less values than or the same values as the right side.
	return left.GetDecimals().size() <= right.GetDecimals().size();
}

// Private

template <class T>
string DottedDecimal::AnyTypeToString(const T& t)
{
	std::stringstream ss;
	ss << t;
	return ss.str();
}

template <class T>
void DottedDecimal::StringToAnyType(T& t, std::string inString)
{
	std::stringstream ss(inString);
	ss >> t;
}

void DottedDecimal::StringSplit(string inString, string inDelim, vector<string> * outResults)
{
	int cutAt;
	while( (cutAt = inString.find_first_of(inDelim)) != inString.npos )
	{
		if(cutAt > 0)
		{
			outResults->push_back(inString.substr(0,cutAt));
		}
		inString = inString.substr(cutAt+1);
	}
	if(inString.length() > 0)
	{
		outResults->push_back(inString);
	}
}

Copyright ® Rhyous.com – Linking to this article is allowed without permission and as many as ten lines of this article can be used along with this link. Any other use of this article is allowed only by permission of Rhyous.com.

How to enumerate installed MSI Products and their MSP Patches using C#

Hey all,

I already have a C++ example of doing this, but that isn’t much help when you have to do it in C#. So it is good to know how to do this in both languages.

Here is what I did:

  1. Created a new C# Console Application project in Visual Studio and named it WindowsInstallerTest.
  2. I added a reference to Microsoft.Deployment.WindowsInstaller. I found this by installing Windows Installer XMl and looking here:
    C:\Program Files (x86)\Windows Installer XML v3.6\bin\Microsoft.Deployment.WindowsInstaller.dll
  3. I added a using statement to the same Microsoft.Deployment.WindowsInstaller.
  4. I enumerated the installed MSI products and then with a foreach loop output data on each.
  5. I enumerated the MSP patches for each product and used another foreach loop to output data on each product.

Here is the simple code.  Hopefully this is enough to get your started with writing code to work with installed MSI products.

using System;
using System.Collections.Generic;
using System.Linq;
// 
// Step 1 - Add a reference to Microsoft.Deployment.WindowsInstaller
//          and then add this using statement.
using Microsoft.Deployment.WindowsInstaller;

namespace WindowsInstallerTest
{
    class Program
    {
        static void Main(string[] args)
        {
            // Step 2 - Get the installed MSI Products
            IEnumerable<ProductInstallation> installations = Microsoft.Deployment.WindowsInstaller.ProductInstallation.GetProducts(null, "s-1-1-0", UserContexts.All);
            int i = 0;

            // Step 3 - Loop through the installed MSI Products and output information

            foreach (ProductInstallation installation in installations)
            {
                Console.WriteLine("Id: " + ++i);
                Console.WriteLine("Name: " + installation.ProductName);
                Console.WriteLine("ProductVersion: " + installation.ProductVersion);
                Console.WriteLine("Install Source: " + installation.InstallSource);
                Console.WriteLine("Patches: ");

                // Step 4 - Get the installed MSP Patches for the current installation
                IEnumerable<PatchInstallation> patches = PatchInstallation.GetPatches(null, installation.ProductCode, "s-1-1-0", UserContexts.All, PatchStates.All);

                // Step 5 - Loop through the installed MSP Patches and output information
                int j = 0;
                foreach (PatchInstallation patch in patches)
                {
                    Console.WriteLine("  " + ++j + ": " + patch.DisplayName);
                    Console.WriteLine("  Cache: " + patch.LocalPackage);
                }
                Console.WriteLine();
            }
        }
    }
}

How do you know all software programmers are crazy?

Question: How do you know all software programmers are crazy?

Answer: All their code has to be committed.

A Laffy Taffy style joke by Mark Minson

A simple example of starting and stopping a service in windows using C++

Hey all,

Here is a simple example of starting and stopping a service in C++. Just pass the service name as a parameter and the service will switch states. If stopped, it will start. If started, it will stop.

// StartStopService.cpp : Defines the entry point for the console application.
//

#include “stdafx.h”
#include
#include

int _tmain(int argc, _TCHAR* argv[])
{
SC_HANDLE serviceDbHandle = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
SC_HANDLE serviceHandle = OpenService(serviceDbHandle, argv[1], SC_MANAGER_ALL_ACCESS);

SERVICE_STATUS_PROCESS status;
DWORD bytesNeeded;
QueryServiceStatusEx(serviceHandle, SC_STATUS_PROCESS_INFO,(LPBYTE) &status,sizeof(SERVICE_STATUS_PROCESS), &bytesNeeded);

if (status.dwCurrentState == SERVICE_RUNNING)
{// Stop it
BOOL b = ControlService(serviceHandle, SERVICE_CONTROL_STOP, (LPSERVICE_STATUS) &status);
if (b)
{
std::cout << "Service stopped." << std::endl; } else { std::cout << "Service failed to stop." << std::endl; } } else {// Start it BOOL b = StartService(serviceHandle, NULL, NULL); if (b) { std::cout << "Service started." << std::endl; } else { std::cout << "Service failed to start." << std::endl; } } CloseServiceHandle(serviceHandle); CloseServiceHandle(serviceDbHandle); return 0; } [/sourcecode]

A simple example of creating or deleting a windows share using C++

Hey all,

Here is a simple example of creating or deleting a windows share in C++.

// CreateShare.cpp : Defines the entry point for the console application.
//

#include “stdafx.h”
#include
#include
#include “lm.h”

int _tmain(int argc, _TCHAR* argv[])
{
// Create share
if (0 == _tcscmp(argv[1], _T(“create”)))
{
SHARE_INFO_2 si =
{
L”ShareName”,
STYPE_DISKTREE,
L”Any nice comment or remark”,
ACCESS_READ,
DWORD(-1),
0,
L”C:\\Users\\jbarneck\\Desktop\\Share”,
L””
};

DWORD parameterError = 0;
DWORD status = NetShareAdd(NULL, 2, (BYTE *) &si, &parameterError);
if (status != ERROR_SUCCESS)
{
std::cout << "Error: " << status << std::endl; if (status == ERROR_ACCESS_DENIED) // 5L in WinError.h { std::cout << "Access denied." << std::endl; } if (status == NERR_ServerNotStarted) // 2114 in LMErr.h { std::cout << "The Server service is stopped." << std::endl; } if (status == NERR_DuplicateShare) // 2118 in LMErr.h { std::cout << "The share already exists." << std::endl; } } // End program return 0; } // Delete share if (0 == _tcscmp(argv[1], _T("delete"))) { DWORD status = NetShareDel(NULL, L"ShareName", 0); if (status != ERROR_SUCCESS) { std::cout << "Could not delete share: " << status << std::endl; } // End program return 0; } } [/sourcecode] References: NetShareAdd - http://msdn.microsoft.com/en-us/library/bb525384%28VS.85%29.aspx NetShareDel - http://msdn.microsoft.com/en-us/library/bb525386%28v=VS.85%29.aspx

How to determine the project type of an existing Visual Studio project?

Ok, so I have an existing C++ Visual Studio project (at my new position here as a Developer at LANDesk) and who knows who created it or when it was created.  Anyway, I wanted to start a new project and use the same project type.

So how do I find out the project type.

In Visual Studio, I opened the ProjectName.vcproj file and found this information near the top:

<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
	ProjectType="Visual C++"
	Version="9.00"
	Name="MSICheckForPatch"
	ProjectGUID="{0793FB88-8BED-4297-8615-9408EA2FBE74}"
	Keyword="AtlProj"
	TargetFrameworkVersion="196613"
	>
	<Platforms>
		<Platform
			Name="Win32"
		/>
	</Platforms>
	...

So I noticed the Keyword was AtlProj, so that clued me in.

Looks like it was as easy as opening a text file and looking.

If there is a better or easier way, please comment and let me know.

How to get the current running executable name as a string in C#?

Ok, there are multiple options.

Here is the code, you choose the option you want.

It is best to use Option 1 or Option 2. Read this blog at MSDN for a better understanding:
Assembly.CodeBase vs. Assembly.Location

using System;

namespace GetCurrentProcessName
{
    class Program
    {
        static void Main(string[] args)
        {
            // It is best to use Option 1 or Option 2.  Read this:
            // http://blogs.msdn.com/b/suzcook/archive/2003/06/26/assembly-codebase-vs-assembly-location.aspx

            // Option 1 - Using Location (Recommended)
            String fullExeNameAndPath = System.Reflection.Assembly.GetExecutingAssembly().Location;
            String ExeName = System.IO.Path.GetFileName(fullExeNameAndPath);

            // Option 2 - Using CodeBase instead of location
            String fullExeNameAndPathUrl = System.Reflection.Assembly.GetExecutingAssembly().CodeBase;
            String codeBase = System.IO.Path.GetFileName(fullExeNameAndPathUrl);

            // Option 3 - Usable but during debugging in Visual Studio it retuns ExeName.vhost.exe
            String fullVhostNameAndPath = Environment.GetCommandLineArgs()[0];
            String vhostName = System.IO.Path.GetFileName(fullVhostNameAndPath);

            // Option 4 - Also usable but doesn't include the extension .exe and also returns ExeName.vhost
            // during debuggin in visual studio
            String prcessName = System.Diagnostics.Process.GetCurrentProcess().ProcessName;
        }
    }
}

Copyright ® Rhyous.com – Linking to this page is allowed without permission and as many as ten lines of this page can be used along with this link. Any other use of this page is allowed only by permission of Rhyous.com.

How to limit or prevent characters in a TextBox in C#? Or How to create a NumberTextBox or DigitBox object?

Let say you want to have a TextBox in which you only want to allow integers (0-9) or maybe you only want to allow strings, A-Za-z.

Well, lets play around with this for a second and see what we can do.

To get started do this:

  1. Create a new WPF Application project.
  2. In the designer, add a TextBox from the Toolbox.
  3. In the properties field for the TextBox click the icon that look like a lightening bolt to bring up events.
  4. Find the KeyDown event and double-click on it.

Ok, so you should now have the following function:

        private void textBox1_KeyDown(object sender, KeyEventArgs e)
        {
        }

The key that was pressed is accessible from the KeyEventArgs variable, e. Specifically e.Key.

Key just happens to be an enum, which means they can basically be treated as integers.

The key press can be ignored by telling setting e.Handled=true. This way it is already marked as handled and will not be added to the TextBox.

Allow only number keys 0-9 in a TextBox

Here is a simple function to allow only natural numbers or number keys 0-9 in a TextBox. Be aware that the keys may be different in other languages.

        private void textBox1_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key < Key.D0 || e.Key > Key.D9)
            {
                e.Handled = true;
            }
        }

Wow, that was pretty simple, right?  WRONG! It is not that easy.

You realize that there are two sets of numbers on a keyboard right? You have numbers in row above your QWERTY keys and you likely have a Number pad on the right of your keyboard as well.  That is not all either.

What else did we forget, you might ask?  Well, of the seven requirements we need to handle, we only handled one.  For an application to be considered release quality or enterprise ready or stable, all seven of these should be handled.

  1. Numbers 1234567890 above QWERTY keys.
  2. Numpad numbers.
  3. You may want to allow pressing of the Delete, Backspace, and Tab keys.
  4. What about pasting?
  5. What about drag and drop?
  6. What about someone else calling your code and changing the text?
  7. Mnemonics should work.

Requirements

Here are the six requirements in clear statements.

  1. Allow pressing Numbers 1234567890 above QWERTY keys.
  2. Allow pressing Numpad numbers.
  3. Allow pressing of the Delete, Backspace, and Tab keys.
  4. Allow pasting so that only numbers in a string are added: A1B2C3 becomes 123.
  5. Allow drag and drop so that only numbers in a string are added: A1B2C3 becomes 123.
  6. Allow change in code at runtime so that only numbers in a string are added: A1B2C3 becomes 123.
  7. When another control has a mnemonic, such as Alt + S, pressing Alt + S, should property change the focus and place the cursor in the Alt + S control.

Remembering lists like this is something that comes with experience.  If you thought of these on your own, good work.  If you didn’t think of them on your own, don’t worry, experience comes with time.

So lets enhance this to handle each of these.

Handling both Number keys and Numpad keys

        private void textBox1_KeyDown(object sender, KeyEventArgs e)
        {
                e.Handled = !IsNumberKey(e.Key);
        }

        private bool IsNumberKey(Key inKey)
        {
            if (inKey < Key.D0 || inKey > Key.D9)
            {
                if (inKey < Key.NumPad0 || inKey > Key.NumPad9)
                {
                    return false;
                }
            }
            return true;
        }

All right, we now have two of the six requirements down.

Handling Delete, Backspace, and Tab, and Mnemonics

You can probably already guess how easy it will be to do something similar to handle these two keys.

        protected void OnKeyDown(object sender, KeyEventArgs e)
        {
            e.Handled = !IsNumberKey(e.Key) && !IsActionKey(e.Key);
        }

        private bool IsNumberKey(Key inKey)
        {
            if (inKey < Key.D0 || inKey > Key.D9)
            {
                if (inKey < Key.NumPad0 || inKey > Key.NumPad9)
                {
                    return false;
                }
            }
            return true;
        }

        private bool IsActionKey(Key inKey)
        {
            return inKey == Key.Delete || inKey == Key.Back || inKey == Key.Tab || inKey == Key.Return || Keyboard.Modifiers.HasFlag(ModifierKeys.Alt);
        }

Ok, now we have four of six requirements handled.

Handling Paste (Ctrl + V) and Drag and Drop

Yes, I can handle both at the same time with a new event TextChanged.

This is setup so that if someone pastes both letters and number, only the numbers are pasted: A1B2C3 becomes 123.

This event is not configured so we have to set it up.

  1. In the designer, click the TextBox.
  2. In the properties field for the TextBox click the icon that look like a lightening bolt to bring up events.
  3. Find the TextChanged event and double-click on it.

You should now have this stub code for the event function.


        private void textBox1_TextChanged(object sender, TextChangedEventArgs e)
        {
        }

Here is some easy code to make sure each character is actually a digit.

        protected void OnTextChanged(object sender, TextChangedEventArgs e)
        {
            base.Text = LeaveOnlyNumbers(Text);
        }

        private string LeaveOnlyNumbers(String inString)
        {
            String tmp = inString;
            foreach (char c in inString.ToCharArray())
            {
                if (!IsDigit(c))
                {
                    tmp = tmp.Replace(c.ToString(), "");
                }
            }
            return tmp;
        }

        public bool IsDigit(char c)
        {
            return (c >= '0' && c <= '9');
        }

Guess what else? This last function actual handles the first five requirements all by itself. But it is less efficient so we will leave the previous requirements as they are.

Handling Direct Code Change

Ok, so some somehow your TextBox is passed inside code during runtime a string that contains more than just numbers.  How are you going to handle it.

This is setup so that if someone pastes both letters and number, only the numbers are pasted: A1B2C3 becomes 123.  Well, we need to run the same function as for Drag and Drop, so to not duplicate code, it is time to create a class or object.

Creating a NumberTextBox object

Now we need to make our code reusable. Lets create a class called NumberTextBox and it can do everything automagically.

NumberTextBox

using System;
using System.Windows.Controls;
using System.Windows.Input;

namespace System.Windows.Controls
{
    public class DigitBox : TextBox
    {
        #region Constructors
        /// <summary> 
        /// The default constructor
        /// </summary>
        public DigitBox()
        {
            TextChanged += new TextChangedEventHandler(OnTextChanged);
            KeyDown += new KeyEventHandler(OnKeyDown);
        }
        #endregion

        #region Properties
        new public String Text
        {
            get { return base.Text; }
            set
            {
                base.Text = LeaveOnlyNumbers(value);
            }
        }

        #endregion

        #region Functions
        private bool IsNumberKey(Key inKey)
        {
            if (inKey < Key.D0 || inKey > Key.D9)
            {
                if (inKey < Key.NumPad0 || inKey > Key.NumPad9)
                {
                    return false;
                }
            }
            return true;
        }

        private bool IsActionKey(Key inKey)
        {
            return inKey == Key.Delete || inKey == Key.Back || inKey == Key.Tab || inKey == Key.Return || Keyboard.Modifiers.HasFlag(ModifierKeys.Alt);
        }

        private string LeaveOnlyNumbers(String inString)
        {
            String tmp = inString;
            foreach (char c in inString.ToCharArray())
            {
                if (!IsDigit(c))
                {
                    tmp = tmp.Replace(c.ToString(), "");
                }
            }
            return tmp;
        }

        public bool IsDigit(char c)
        {
            return (c >= '0' && c <= '9');
        }
        #endregion

        #region Event Functions
        protected void OnKeyDown(object sender, KeyEventArgs e)
        {
            e.Handled = !IsNumberKey(e.Key) && !IsActionKey(e.Key);
        }

        protected void OnTextChanged(object sender, TextChangedEventArgs e)
        {
            base.Text = LeaveOnlyNumbers(Text);
        }
        #endregion
    }
}

Now I can delete the events and functions from the Window1.xaml.cs file. I don’t have to add any code to the Window1.xaml.cs. Instead I need to reference my local namespace in the Window1.xaml and then change the TextBox to a local:NumberTextBox. Here is the XAML.

<Window x:Class="TextBoxIntsOnly.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:TextBoxIntsOnly"
    Title="Window1" Height="300" Width="300">
    <Grid>
        <local:DigitBox Margin="87,27,71,0" VerticalAlignment="Top" x:Name="textBox1" />
        <Label Height="28" HorizontalAlignment="Left" Margin="9,25,0,0" Name="label1" VerticalAlignment="Top" Width="72">Integers:</Label>
        <TextBox Height="23" Margin="87,56,71,0" Name="textBox2" VerticalAlignment="Top" />
        <Label Height="28" HorizontalAlignment="Left" Margin="9,54,0,0" Name="label2" VerticalAlignment="Top" Width="72">Alphabet:</Label>
    </Grid>
</Window>

And now all seven requirements are met.

Common Regular Expression Patterns for C#

The following are code snippets for common regular expressions in C#.

If you have a regular expression that you think is common or a correction/improvement to one of mine, please submit it.

IP address pattern or expression

The expression:

^[0-9]{1,3}\.){3}[0-9]{1,3}$

In CSharp code:

String theIpAddressPattern = @"^[0-9]{1,3}\.){3}[0-9]{1,3}$";

Domain name pattern or expression

The expression:

^[\-\w]+\.)+[a-zA-Z]{2,4}$

In CSharp code:

String theDoainNamePattern = @"^[\-\w]+\.)+[a-zA-Z]{2,4}$";

Email address pattern or expression

The expression:

^[\w!#$%&'*+\-/=?\^_`{|}~]+(\.[\w!#$%&'*+\-/=?\^_`{|}~]+)*@((([\-\w]+\.)+[a-zA-Z]{2,4}$)|(([0-9]{1,3}\.){3}[0-9]{1,3}))

In CSharp code:

String theEmailPattern = @"^[\w!#$%&'*+\-/=?\^_`{|}~]+(\.[\w!#$%&'*+\-/=?\^_`{|}~]+)*"
                                   + "@"
                                   + @"((([\-\w]+\.)+[a-zA-Z]{2,4})|(([0-9]{1,3}\.){3}[0-9]{1,3}))$";

a

The URL Pattern

The expression:

((^http(s)*://(([\-\w]+\.)+[a-zA-Z]{2,4}.*)))$|(^ftp://([\w](:[\w]))*(([\-\w]+\.)+[a-zA-Z]{2,4}[/\w]*))$

In CSharp code:

            String theURLPattern = @"((^http(s)*://(([\-\w]+\.)+[a-zA-Z]{2,4}.*)))$"
                                 + @"|(^ftp://([\w](:[\w]))*(([\-\w]+\.)+[a-zA-Z]{2,4}[/\w]*))$";

Regular Expressions in C# (including a new comprehensive email pattern)

Of course C# supports regular expressions. I happen to have learned regular expressions in my dealings with FreeBSD, shell scripting, php, and other open source work. So naturally I would want to add this as a skill as I develop in C#.

What is a Regular Expression?

This is a method in code or script to describe the format or pattern of a string. For example, look at an email address:

someuser@somedomain.tld

It is important to understand that we are not trying to compare the email string against another string, we are trying to compare the string against a pattern.

To verify the email was in the correct format using String functions, it would take dozens of different functions running one after another.  However, with a regular expression, a proper email address can be verified in one single function.

So instead regular expression is a language, almost like a scripting language in itself, for defining character patterns.

Most characters represent themselves.  However, some characters don’t represent themselves without escaping them with a backslash because they represent something else.  Here is a table of those characters.

Expression Meaning
* Any number of the previous character or character group.
+ One of more of the previous character or character group.
^ Beginning of line or string.
$ End of line or string.
? Pretty much any single character.
. Pretty much any character, zero characters, one character, or any number of characters
[ … ] This forms a character class expression
( … ) This forms a group of items

You should look up more regular expression rules. I don’t explain them all here. This is just to give you an idea.

Example 1 – Parameter=Value

Here is a quick example of a regular expression that matches String=String. At first you might think this is easy and you can use this expression:

.*=.*

While that might work, it is very open. And it allows for zero characters before and after the equals, which should not be allowed.

This next pattern is at least correct but still very open.

.+=.+

What if the first value is limited to only alphanumeric characters?

[a-zA-z0-9]=.+

What if the second value has to be a valid windows file path or URL? And we will make sure we cover start to finish as well.

^[0-9a-zA-Z]+=[^<>|?*\”]+$

See how the more restrictions you put in place, the more complex the expression gets?

Example 2 – The email address

The pattern of an email is as follows: (Reference: wikipedia)

See updates here: C# – Email Regular Expression

  1. It will always have a single @ sign
  2. 1 to 64 characters before the @ sign called the local-part. Can contain characters a–z, A–Z, 0-9, ! # $ % & ‘ * + – / = ? ^ _ ` { | } ~, and . if it is not at the first or end of the local-part.
  3. Some characters after the @ sign that have a pattern as follows called the domain.
    1. It will always have a period “.”.
    2. One or more character before the period.
    3. Two to four characters after the period.

So a simple patterns of an email address should be something like these:

  1. This one just makes sure there are characters before and after the @
    .+@.+
  2. This one makes sure the are characters before and after the @ as well as a character before and after the . in the domain.
    .+@.*+\..+
  3. This one makes sure that there is only one @ symbol.
    [^@]+@[^@]+\.

This are all quick an easy examples and will not work in every instance but are usually accurate enough for casual programs.

But a comprehensive example is much more complex.

  1. I wrote one myself that is the shortest and gets the best results of any I have found:
    ^[\w!#$%&'*+\-/=?\^_`{|}~]+(\.[\w!#$%&'*+\-/=?\^_`{|}~]+)*@((([\-\w]+\.)+[a-zA-Z]{2,4})|(([0-9]{1,3}\.){3}[0-9]{1,3}))$
    
    
  2. Here is another complex one I found: [reference]
    ^(([^<>()[\]\\.,;:\s@\""]+(\.[^<>()[\]\\.,;:\s@\""]+)*)|(\"".+\""))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$
    

So let me explain the first one that I wrote as it passes my unit tests below:

The start
[\w!#$%&’*+\-/=?\^_`{|}~]+ At least one valid local-part character not including a period.
(\.[\w!#$%&’*+\-/=?\^_`{|}~]+)* Any number (including zero) of a group that starts with a single period and has at least one valid local-part character after the period.
@ The @ character
( Start group 1
( Start group 2
([\-\w]+\.)+ At least one group of at least one valid word character or hyphen followed by a period
[\w]{2,4} Any two to four valid top level domain characters.
) End group 2
| an OR statement
( Start group 3
([0-9]{1,3}\.){3}[0-9]{1,3} A regular expression for an IP Address.
) End group 3
) End group 1

Code for both examples

Here is code for both examples. My email regular expression is enabled and the one I found on line is commented out. To see how they work differently, just comment out mine, and uncomment the one I found online.

using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace RegularExpressionsTest
{
    class Program
    {
        static void Main(string[] args)
        {
            // Example 1 - Parameter=value
            // Match any character before and after the =
            // String thePattern = @"^.+=.+$";

            // Match only Upper and Lowercase letters and numbers before
            // the = as a parameter name and after the equal match the
            // any character that is allowed in a file's full path
            //
            // ^[0-9a-zA-Z]+    This is any number characters upper or lower
            //                  case or 0 thru 9 at the string's beginning.
            //
            // =                Matches the = character exactly
            //
            // [^<>|?*\"]+$     This is any character except < > | ? * "
            //                  as they are not valid in a file path or URL

            String theNameEqualsValue = @"abcd=http://";

            String theParameterEqualsValuePattern = "^[0-9a-zA-Z]+=[^<>|?*\"]+$";
            bool isParameterEqualsValueMatch = Regex.IsMatch(theNameEqualsValue, theParameterEqualsValuePattern);
            Log(isParameterEqualsValueMatch);

            // Example 2 - Email address formats

            String theEmailPattern = @"^[\w!#$%&'*+\-/=?\^_`{|}~]+(\.[\w!#$%&'*+\-/=?\^_`{|}~]+)*"
                                   + "@"
                                   + @"((([\-\w]+\.)+[a-zA-Z]{2,4})|(([0-9]{1,3}\.){3}[0-9]{1,3}))$";

            // The string pattern from here doesn't not work in all instances.
            // http://www.cambiaresearch.com/c4/bf974b23-484b-41c3-b331-0bd8121d5177/Parsing-Email-Addresses-with-Regular-Expressions.aspx
            //String theEmailPattern = @"^(([^<>()[\]\\.,;:\s@\""]+(\.[^<>()[\]\\.,;:\s@\""]+)*)|(\"".+\""))"
            //                       + "@"
            //                       + @"((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])"
            //                       + "|"
            //                       + @"(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$";

            Console.WriteLine("Bad emails");
            foreach (String email in GetBadEmails())
            {
                Log(Regex.IsMatch(email, theEmailPattern));
            }

            Console.WriteLine("Good emails");
            foreach (String email in GetGoodEmails())
            {
                Log(Regex.IsMatch(email, theEmailPattern));
            }
        }

        private static void Log(bool inValue)
        {
            if (inValue)
            {
                Console.WriteLine("It matches the pattern");
            }
            else
            {
                Console.WriteLine("It doesn't match the pattern");
            }
        }

        private static List GetBadEmails()
        {
            List emails = new List();
            emails.Add("joe"); // should fail
            emails.Add("joe@home"); // should fail
            emails.Add("a@b.c"); // should fail because .c is only one character but must be 2-4 characters
            emails.Add("joe-bob[at]home.com"); // should fail because [at] is not valid
            emails.Add("joe@his.home.place"); // should fail because place is 5 characters but must be 2-4 characters
            emails.Add("joe.@bob.com"); // should fail because there is a dot at the end of the local-part
            emails.Add(".joe@bob.com"); // should fail because there is a dot at the beginning of the local-part
            emails.Add("john..doe@bob.com"); // should fail because there are two dots in the local-part
            emails.Add("john.doe@bob..com"); // should fail because there are two dots in the domain
            emails.Add("joe<>bob@bob.come"); // should fail because <> are not valid
            emails.Add("joe@his.home.com."); // should fail because it can't end with a period
            emails.Add("a@10.1.100.1a");  // Should fail because of the extra character
            return emails;
        }

        private static List GetGoodEmails()
        {
            List emails = new List();
            emails.Add("joe@home.org");
            emails.Add("joe@joebob.name");
            emails.Add("joe&bob@bob.com");
            emails.Add("~joe@bob.com");
            emails.Add("joe$@bob.com");
            emails.Add("joe+bob@bob.com");
            emails.Add("o'reilly@there.com");
            emails.Add("joe@home.com");
            emails.Add("joe.bob@home.com");
            emails.Add("joe@his.home.com");
            emails.Add("a@abc.org");
            emails.Add("a@192.168.0.1");
            emails.Add("a@10.1.100.1");
            return emails;
        }
    }
}