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 ↑