C# .NET Custom ConfigurationSection, ConfigurationElementCollection, and ConfigurationElement

The built in configuration classes for .NET are powerful and incredibly useful but can be quite confusing due to their flexibility. This is a simple example of a custom ConfigurationSection with custom element names and nested ConfigurationElementCollection objects.

FIRST: Add a reference to System.Configuration in your project and make sure to add “using System.Configuration;” to the top of your class files.

This is the App.Config file the example with comments to explain each important line.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <!-- 
  configSections must be immediately after <configuration>
  type = the full namespace to your custom configuration section then a comma then the file name of the assembly it is in without the extension (no .exe or .dll)
  The default namespace for this example is 'ConfigTesting' and the assembly name is 'ConfigTesting.exe' or "DefaultNamespace.ClassName, AssemblyName"
  which gives ConfigTesting.MyConfigSection, ConfigTesting
  -->
  <configSections>
    <section name="MyConfig" type="ConfigTesting.MyPetsConfigSection, ConfigTesting"/>
  </configSections>
  
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>

  <!-- The name of the section is whatever is in the 'name' attribute in the above section definition -->
  <MyConfig>
    <!-- The word Pet replaces the standard 'add' keyword to add an element to the collection -->
    <Pet name="FirstElement" personality="FirstPersonality">
      <!-- 
      The 'Pet' element contains a default collection in the same way as the configuration section. 
      The Toy collection is the one accessed without any extra tags around it.
      Here the 'Toy' word replaces the standard 'add' keyword
      -->
      <Toy name="Toy1" description="desc1" />
      <!-- 
      This is an additional collection that must have extra tags around it to show .NET that it is a collection.
      In this case the collection is called 'Food'
      -->
      <Food>
        <!-- The word 'Product' replaces the standard 'add' keyword -->
        <Product name="Collards Dry Food" source="Kennelgate" />
      </Food>
    </Pet>
    
    <!-- Second 'Pet' element without the per-line comments -->
    <Pet name="SecondElement" personality="SecondPersonality">
      <Toy name="Toy2" description="desc2" />
      <Toy name="Toy3" description="desc3" />
      <Food>
        <Product name="Lamb Liver" source="Butchers" />
        <Product name="Carrots" source="Garden" />
        <Product name="Cashews" source="Tesco" />
      </Food>
    </Pet>
    
  </MyConfig>
</configuration>

Defining the section itself is relatively easy.

using System.Configuration;

namespace ConfigTesting
{
    public class MyPetsConfigSection : ConfigurationSection
    {
        //This makes the collection the default one that is accessed.
        //Look at the App.config and see that the collection's elements are immediately within the section instead of 
        //being enclosed by another layer of tags
        [ConfigurationProperty("", IsDefaultCollection = true, IsRequired = true)]
        public MyPetConfigCollection Pets
        {
            get { return base[""] as MyPetConfigCollection; }
            set { base[""] = value; }
        }
    }
}

This is the class referenced in the configSection tag in App.Config. You can see that it has one property which is of type MyPetConfigCollection.

using System.Configuration;

namespace ConfigTesting
{
    public class MyPetConfigCollection : ConfigurationElementCollection
    {
        public override ConfigurationElementCollectionType CollectionType => ConfigurationElementCollectionType.BasicMap;
        protected override string ElementName => "Pet";

        protected override ConfigurationElement CreateNewElement()
        {
            return new MyPetConfigElement();
        }

        protected override object GetElementKey(ConfigurationElement element)
        {
            return ((MyPetConfigElement)element).Name;
        }
    }
}

The MyPetConfigCollection is a simple class that is mostly generated for you. The CreateNewElement and GetElementKey methods are compulsory and Visual Studio offers to generate them for you but you have to fill in the content. CreateNewElement should simply return a new object of whatever type the collection holds while GetElementKey should return the key of the given object (it’s like a primary key, if you’re familiar with databases) which is defined in the element class.

NOTE: Overriding ElementName is how you replace the default ‘add’ keyword with your own string but you must also override CollectionType.

 

using System.Configuration;

namespace ConfigTesting
{
    public class MyPetConfigElement : ConfigurationElement
    {
        //This is the key for the element, it's how to find this particular one in the
        //collection
        [ConfigurationProperty("name", IsKey = true, IsRequired = true)]
        public string Name
        {
            get { return base["name"] as string; }
            set { base["name"] = value; }
        }

        [ConfigurationProperty("personality", IsRequired = true)]
        public string Personality
        {
            get { return base["personality"] as string; }
            set { base["personality"] = value; }
        }

        //This is the default collection for this object and can be accessed without looking for extra tags
        [ConfigurationProperty("", IsDefaultCollection = true, IsRequired = true)]
        public PetToyCollection Toys
        {
            get { return base[""] as PetToyCollection; }
            set { base[""] = value; }
        }

        //This is another collection and must be accessed by using its enclosing tag. In this case <Food>
        [ConfigurationProperty("Food", IsRequired = false)]
        public PetFoodCollection Food
        {
            get { return base["Food"] as PetFoodCollection; }
            set { base["Food"] = value; }
        }
    }
}

This element contains two collections, one is the default collection and the other is specifically named Food. It’s important to note that the in-code name of the property does not need to match the tags/attributes in App.Config. The code names are used in code, the ConfigurationProperty names are used in App.Config.

using System.Configuration;

namespace ConfigTesting
{
    public class PetToyCollection : ConfigurationElementCollection
    {

        public override ConfigurationElementCollectionType CollectionType => ConfigurationElementCollectionType.BasicMap;
        protected override string ElementName => "Toy";

        protected override ConfigurationElement CreateNewElement()
        {
            return new ToyElement();
        }

        protected override object GetElementKey(ConfigurationElement element)
        {
            return ((ToyElement)element).Name;
        }
    }
}

This is the toys collection, which is the default collection (you don’t have to have a default).

using System.Configuration;

namespace ConfigTesting
{
    public class ToyElement : ConfigurationElement
    {
        [ConfigurationProperty("name", IsKey = true, IsRequired = true)]
        public string Name
        {
            get { return base["name"] as string; }
            set { base["name"] = value; }
        }

        [ConfigurationProperty("description", IsRequired = true)]
        public string Description
        {
            get { return base["description"] as string; }
            set { base["description"] = value; }
        }
    }
}

The ToyElement does not contain any collections but it could if you wanted to add some here.

 

The below two blocks are the Food collection and elements.

using System.Configuration;

namespace ConfigTesting
{
    public class PetFoodCollection : ConfigurationElementCollection
    {

        public override ConfigurationElementCollectionType CollectionType => ConfigurationElementCollectionType.BasicMap;
        protected override string ElementName => "Product";

        protected override ConfigurationElement CreateNewElement()
        {
            return new PetFoodElement();
        }

        protected override object GetElementKey(ConfigurationElement element)
        {
            return ((PetFoodElement)element).Name;
        }
    }
}

 

using System.Configuration;

namespace ConfigTesting
{
    public class PetFoodElement : ConfigurationElement
    {
        //Name of pet food product
        [ConfigurationProperty("name", IsKey = true, IsRequired = true)]
        public string Name
        {
            get { return base["name"] as string; }
            set { base["name"] = value; }
        }

        //Shop to buy the pet food from
        [ConfigurationProperty("source", IsRequired = true)]
        public string Source
        {
            get { return base["source"] as string; }
            set { base["source"] = value; }
        }
    }
}

Finally, here is the test I used in a Windows Forms application to make sure each element was usable. This iterates over each element and I had a breakpoint to pause so I could hover over it to read the contents.

private void button1_Click(object sender, EventArgs e)
        {
            MyPetsConfigSection section = ConfigurationManager.GetSection("MyConfig") as MyPetsConfigSection;

            var c = section.Pets.Count;
            foreach (MyPetConfigElement i in section.Pets)
            {
                StringBuilder sb = new StringBuilder();
                sb.AppendLine($"{i.Name} has {i.Toys.Count} toy(s)");
                if (i.Food != null)
                    sb.AppendLine($"Foods: {i.Food.Count}");

                MessageBox.Show(sb.ToString(), i.Personality);
            }
        }

 

Which .NET Version is Installed?

I’ve found myself needing to know which version(s) of the .NET are installed on a server and the easiest way I’ve discovered is to browse to the directory and check the version on mscor*.dll files.

Browse to this directory which is the same on all versions of Windows currently in use.

C:\Windows\Microsoft.NET\

Go into the Framework or Framework64 directories and you’ll see the overview of versions installed. For example, you might have a directory called v4.0.30319 and you would be forgiven for thinking that is the version installed. But what if you know you have .NET 4.5 or even 4.7 installed?

You can go into the directory and find the DLLs whose name begins mscor then right-click -> Properties -> Details tab and see the file version. This matches the version of .NET.

WMI Windows Management Instrumentation

I am running a VPS with only 50Gb disk space so have to regularly clear log files and such which, as an unimportant server only personal stuff, I wasn’t too fussed about automating. This VPS was purchased originally so I could code things and try them out online so I decided to look into building a tool to easily visualise the state of a Windows system.

There’s so much information about WMI that it can be difficult to know where and how to start so my first stop is to begin getting to grips with what exactly WMI is in terms of what the relationship is between it and the things it is telling you about and what the data model looks like.

WMI uses the Common Information Model (CIM) to interact with, and even control, system data. The Wikipedia article explains that this model is updated regularly and provides a helpful link to the Distributed Management Task Force site where the specifications are publically available. For example, this is the current (from June 2015) complete CIM specification: DMTF Schema Documentation 2.44.1.

At this point I now have the information available to know exactly what is possible through WMI but this is entirely different to what Windows will actually let me do. The next step is to decide on how I’m going to interact with WMI.

The MSDN has a helpful article which gives an overview of what providers can be used and how they work. From the WMI Architecture article I know that anything which can query COM will work and that .NET is by design the best choice.

It’s at this point where things get tricky because there are several ways of working with WMI and it seems you, as the programmer, kind of need to know all of them to get anything useful in .NET because you’re simply provided some objects like ManagementObject and ManagementClass and left to your own devices. There is no nice enum of every piece of information you might want to pull from WMI and while you can search online it’s very likely the ‘name’ of something that looks interesting simply won’t work through .NET. Also, the DMTF CIM scheme documentation gives you their name for everything but Microsoft obscures this by modifying their implementation of the scheme. For example, CIM_PROCESS is now WIN32_PROCESS. This does actually make sense in one way as CIM is a method of describing data and it can be used on far more than just Windows but Microsoft want to give their users only information relevant to Windows and, of course, put their own spin on things.

Thankfully Microsoft have listed all of their WMI classes on the MSDN and that is exactly what I’ve been searching for: Performance Counter Classes.

using System;
using System.Windows.Forms;
using System.Management;
 
namespace WMITesting
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
 
        private void Form1_Load(object sender, EventArgs e)
        {
            ManagementClass mc = new ManagementClass("Win32_PerfFormattedData_PerfOS_Processor");
            mc.Get();
            foreach (var p in mc.Properties)
                textBox1.AppendText(string.Format("{0}={1}\r\n", p.Name, p.Value));
        }
    }
}

This works in as much as it gives some information back to me but, oddly, the collection it returns does not contain any values.

As it turns out, all this code does is create a new instsance of Win32_PerfFormattedData_PerfOS_Processor instead of requesting a proper one from the system. Getting it to actually get any values from the system is confusing at first and I learned that WQL is used for ManagementObjectSearcher while a ‘standard’ ManagementPath will use something like \\.\root\CIMV2: Win32_LogicalDisk.DeviceID=”C:” as the example shows in MSDN. I know what information I want so I don’t want to do a search every time the application updates so I need to find how to get a current instance of Win32_PerfFormattedData_PerfOS_Processor which means finding what that ‘path’ should be. Clearly the application needs to be told where to look to get the data and this is done using ManagementScope.

private void Form1_Load(object sender, EventArgs e)
        {
            ManagementScope wmiScope = new ManagementScope(@"\\.\root\cimv2");
            ManagementPath wmiPath = new ManagementPath("Win32_PerfFormattedData_PerfOS_Processor.Name=\"_TOTAL\"");
            ObjectGetOptions wmiOptions = new ObjectGetOptions(null, TimeSpan.MaxValue, true);
            ManagementObject wmiObject = new ManagementObject(wmiScope, wmiPath, wmiOptions);
            wmiObject.Get();
            foreach (var p in wmiObject.Properties)
                textBox1.AppendText(string.Format("{0}={1}\r\n", p.Name, p.Value));
        }

What if I want more granular information instead of just looking at _TOTAL for the process class or C: for the disk. I might want to list everything that’s available and let the user decide. After a lot of playing around and trying to avoid using the searcher it became clear that ManagementObject is for single instances while ManagementClass lets you get all instances of the class.

private void Form1_Load(object sender, EventArgs e)
{
    ManagementScope wmiScope = new ManagementScope(@"\\.\root\cimv2");
    ManagementPath wmiPath = new ManagementPath("Win32_PerfFormattedData_PerfOS_Processor");
    ObjectGetOptions wmiOptions = new ObjectGetOptions(null, TimeSpan.MaxValue, true);
    ManagementClass wmiClass = new ManagementClass(wmiScope, wmiPath, wmiOptions);
    var wmiInstances = wmiClass.GetInstances();
 
    foreach (var i in wmiInstances)
        foreach(var p in i.Properties)
            textBox1.AppendText(string.Format("{0}={1}\r\n", p.Name, p.Value));
}

This code gives the same output as before but with data and includes every instance of the Win32_PerfFormattedData_PerfOS_Processor class.

 

That’s it for this post. I’ve gone from using WMI only through the command line to knowing where to find all the class names & their descriptions then pulling back individual or all instances of that class in any application I want. The next obvious step is to query remote computers but I’m thinking of looking at how to display the data in a different way and may do another post about it at some point.

C#.NET Scripting Support In Your Applications

This is a technical blog post today that I hope will be fairly useful to everyone out there. I have wondered how to actually include scripting abilities within my applications and today I decided to figure it out. The process is actually pretty simple. You need to include two namespaces which are System.CodeDom.Compiler and System.Reflection to do this (the first compiles your ‘script’ while the second lets you use the result) and then you can do whatever you want from there. Here’s one I made earlier…

private void btnRun_Click(object sender, EventArgs e) 
{ 
  CodeDomProvider codeProvider = CodeDomProvider.CreateProvider("C#"); 
  CompilerParameters codeParams = new CompilerParameters(); 
  CompilerResults codeResults = null; 
  codeParams.GenerateExecutable = false; 
  codeParams.GenerateInMemory = true; 
  codeParams.IncludeDebugInformation = false; 
  codeParams.ReferencedAssemblies.Add("System.dll"); 
  codeParams.ReferencedAssemblies.Add("System.Windows.Forms.dll"); 
  codeResults = codeProvider.CompileAssemblyFromSource( codeParams, new string[] { txtSourceInput.Text } ); 
  CompilerErrorCollection codeErrors = cResults.Errors; 
  Type[] resultTypes = cResults.CompiledAssembly.GetTypes(); 
  object o = codeResults.CompiledAssembly.CreateInstance( resultTypes[0].FullName ); 
  MethodInfo[] m = resultTypes[0].GetMethods(); 
  m[0].Invoke(o, null); 
}

There’s a lot to note here:

 

  1. Lines 9 and 10 are adding referenced assemblies programmatically but this is the same as right clicking “References” in your project and clicking “Add Reference” as this makes the methods within those assemblies available to the dynamically compiled code (the user’s script). I was testing this by popping up a messagebox so needed System.Windows.Forms.dll but you could add anything here that would work using the “Add Reference” option of a normal project.
  2. Line 11 uses an array of strings, I’m not sure why but for a standard user script you can just do what I’ve done and enter the whole text box as the first element.
  3. Line 15 shows the retrieval of the compile errors collection which is IMPORTANT! If you don’t check this for errors then your application will likely crash due to bad end-user code. I was just doing this as testing so I haven’t added anything to actually check the collection but if “codeErrors.HasErrors” is false then it’s good to go.
  4. Line 17 creates the type by using the FULL NAME If you do not do this you will get the exception “Non-static method requires a target” unless you execute static methods.
  5. The remaining lines are NOT good code and you shouldn’t do things this way in your live applications but this is fine as an example. I’m showing the way to get the types, create an instance, get the methods of that type and then how to invoke the first method of the instance of that type. You can easily modify this code to check for an expected type and method and provide parameters as needed.
When you come to test this you must feed it fully correct C# or the CompilerErrorCollection will have errors. So, for example, you cannot try to compile “MessageBox.Show(“HI!!”);” but you can compile:
namespace MyDynamicNamespace 
{ 
  public class MyDynamicClass 
  { 
    public void MyDynamicMethod() 
    { 
      MessageBox.Show("This is a test"); 
    } 
  } 
}

 

A small class to take screen shots in C# NET

This is very simple but I wanted to test the Syntax highlighter I just installed on the blog.

using System;
using System.IO;
using System.IO.Compression;
using System.Drawing;
using System.Drawing.Imaging
using System.Collections.Generic;
using System.Windows.Forms;

public class Screenshot {
List _screenImages = new List();
Graphics _gfx; ///

/// Creates a new Screenshot object for taking, saving and compressing screenshots
public Screenshot() { }
/// Get a stream for the selected image zero-based index of the image use compression or not
public Stream this[int index, bool compress]
{
get {
if (index >= 0 && index <= _screenImages.Count)
{
MemoryStream[] ms = new MemoryStream[] { new MemoryStream(), new MemoryStream() };
_screenImages[index].Save(ms[0], ImageFormat.Bmp);
if (compress)
{
compressOutput(ms[0], ms[1]); ms[0].Close();
ms[0] = null;
ms[1].Seek(0, SeekOrigin.Begin); return ms[1];
}
else
{
ms[0].Seek(0, SeekOrigin.Begin);
return ms[0];
}
}
return null;
}
}
/// Get a stream for the selected image zero-based index of the image use compression or not type of image to return
public Stream this[int index, bool compress, ImageFormat format]
{
get
{
if (index >= 0 && _screenImages.Count < index)
{
MemoryStream[] ms = new MemoryStream[1];
_screenImages[index].Save(ms[0], format);
if (compress)
{
compressOutput(ms[0], ms[1]);
ms[0].Close();
ms[0] = null;
ms[1].Seek(0, SeekOrigin.Begin);
return ms[1];
}
else
{
ms[0].Seek(0, SeekOrigin.Begin);
return ms[0];
}
}
return null;
}
}
public int Count { get { return _screenImages.Count; } }

/// Capture all screens to memory
public void CaptureAllScreens()
{
_screenImages.Clear();
foreach (Screen x in Screen.AllScreens)
{
Bitmap tmp = new Bitmap(x.Bounds.Width, x.Bounds.Height, PixelFormat.Format32bppArgb);
_gfx = Graphics.FromImage(tmp);
_gfx.CopyFromScreen(0, 0, 0, 0, x.Bounds.Size, CopyPixelOperation.SourceCopy);
_screenImages.Add(tmp);
}
}
/// Capture only the primary screen
public void CapturePrimary()
{
_screenImages.Clear();
Bitmap tmp = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb);
_gfx = Graphics.FromImage(tmp);
_gfx.CopyFromScreen(0, 0, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
_screenImages.Add(tmp);
}
/// Capture a specific screen from the Screen collection Screen object
public void CapureScreen(Screen screen)
{
_screenImages.Clear();
Bitmap tmp = new Bitmap(screen.Bounds.Width, screen.Bounds.Height, PixelFormat.Format32bppArgb);
_gfx = Graphics.FromImage(tmp);
_gfx.CopyFromScreen(0, 0, 0, 0, screen.Bounds.Size, CopyPixelOperation.SourceCopy);
_screenImages.Add(tmp);
}

/// Compress the input stream to the output stream using DeflateStream
///source stream destination stream
void compressOutput(Stream source, Stream destination)
{
DeflateStream ds = new DeflateStream(destination, CompressionMode.Compress);
byte[] buffer = new byte[2048];
int bytesRead = 0;
source.Seek(0, SeekOrigin.Begin);
bytesRead = source.Read(buffer, 0, buffer.Length);
while (bytesRead > 0)
{
ds.Write(buffer, 0, bytesRead);
bytesRead = source.Read(buffer, 0, buffer.Length);
}
ds.Flush();
}
}

 

Proudly powered by WordPress | Theme: Baskerville 2 by Anders Noren.

Up ↑