Retrieving Hardware Identifiers in C# with WMI
By Peter A. Bromberg, Ph.D.
Printer - Friendly Version
Peter Bromberg

Some time ago I wrote up a short article here on how to use P/Invoke to use one of the Kerne32 APIs to get the Volume Serial Number off the specified hard drive. It turns out that there has been some feedback on this and related subjects and so I've decided to revisit it.

The whole concept of "per machine" or "per seat" software licensing has tended to revolve around unique machine keys, dongles and other ways of tying a licensing scheme to a particular machine (or machines). Microsoft uses this in its "per seat" or "per processor" licensing scheme as do many other vendors. The Activation key used for Windows XP and Windows Server is also based on a composite of hardware - based "IDs".



Most all of these can be easily obtained through the use of the Windows Management Instrumentation classes in the System.Managment namespace, through instances of the ManagementObject class.

I'm not giving a tutorial on how to use these classes here; the MSDN documentation is fairly complete. We get lots of questions (usually but not always from newbies) from people who haven't yet learned Rule Number One of software development: RTFM. And of course, our advice, taking the meaning of the great Chinese proverb about teaching men to fish, is usually "RTFM!" (sometimes with a handy link to the actual place where the solution to the question or problem can be found). I say this seriously because it surprises me how many people have not invested the time to learn how to search on their own for answers. You have your Visual Studio.NET help built in where all you have to do is highlight the item and hit F1 to search the index, and you have a built-in search window too (which by the way accepts boolean modifiers). For those without Studio, you have the Framework SDK help which works the same way. Then, you have MSDN online with a huge searchable archive of Knowlege Base articles, help documentation and articles. Finally, you have Google, arguably the best web search engine ever invented, and all you need to do is learn the syntax for complex searches. Switch to the "Groups" tab and you can repeat your search on their 20 year history of all newsgroups. I think you get the picture.

Probably the most useful of all of the methods I've created below is the one to retrieve the CPU ID, since this is the one piece of hardware that almost never changes. If you reformat your hard drive, your Volume Serial Number will be changed. As well, there are a number of program utilities that allow you to change the HD volume serial without reformatting the drive. Same with NICs- the network card in my machine now is not the same one that I had in there a year ago. However, the CPU is the same.

I've put together a class library with what you see below, along with a nice Winforms - based test harness for it, and you can download the VS.NET 2003 solution for it at the bottom of this article. Enjoy!

using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Management;

namespace MachineInfo
{
    public class GetInfo
    {
        
        /// <summary>
        /// return Volume Serial Number from hard drive
        /// </summary>
        /// <param name="strDriveLetter">[optional] Drive letter</param>
        /// <returns>[string] VolumeSerialNumber</returns>
        public string GetVolumeSerial(string strDriveLetter)
        {
        if( strDriveLetter=="" || strDriveLetter==null) strDriveLetter="C";
        ManagementObject disk = 
            new ManagementObject("win32_logicaldisk.deviceid=\"" + strDriveLetter +":\"");
        disk.Get();
        return disk["VolumeSerialNumber"].ToString();
        }
        
        /// <summary>
        /// Returns MAC Address from first Network Card in Computer
        /// </summary>
        /// <returns>[string] MAC Address</returns>
        public string GetMACAddress()
            {
            ManagementClass mc = new ManagementClass("Win32_NetworkAdapterConfiguration");
            ManagementObjectCollection moc = mc.GetInstances();
            string MACAddress=String.Empty;
            foreach(ManagementObject mo in moc)
            {
                if(MACAddress==String.Empty)  // only return MAC Address from first card
                {
                    if((bool)mo["IPEnabled"] == true) MACAddress= mo["MacAddress"].ToString() ;
                }
                        mo.Dispose();
            }
            MACAddress=MACAddress.Replace(":","");
            return MACAddress;
            }
        /// <summary>
        /// Return processorId from first CPU in machine
        /// </summary>
        /// <returns>[string] ProcessorId</returns>
        public string GetCPUId()
        {
            string cpuInfo =  String.Empty;
            string temp=String.Empty;
            ManagementClass mc = new ManagementClass("Win32_Processor");
            ManagementObjectCollection moc = mc.GetInstances();
            foreach(ManagementObject mo in moc)
            {
                if(cpuInfo==String.Empty) 
                {// only return cpuInfo from first CPU
                    cpuInfo = mo.Properties["ProcessorId"].Value.ToString();
                }             
            }
            return cpuInfo;
        }
    }
}

I want to add before closing that you can gain access to these objects through plain old VBScript in a VBS file:

Function CpuID() 
  Dim oWMI, oCpu
  Set oWMI = GetObject("winmgmts:")
  For Each oCpu in oWMI.InstancesOf("Win32_Processor")
   wscript.echo "CPU: " & oCpu.ProcessorID     
  Next 
End Function 
CpuID

Download the code that accompanies this article


Peter Bromberg is a C# MVP, MCP, and .NET consultant who has worked in the banking and financial industry for 20 years. He has architected and developed web - based corporate distributed application solutions since 1995, and focuses exclusively on the .NET Platform.