Getting Windows serial number (was: Getting MachineGuid from Registry)

As other people have already pointed out, you are not supposed to get that value directly from the registry (which is probably why it doesn't work reliably among different versions of Windows).

A little searching led me to the Win32_OperatingSystem WMI class. Using this class, you can actually get the Windows serial number. It took me some searching and experimenting to get it right, but this is how to use it in C#.

Make sure you have the System.Management.dll reference in your project:

using System.Management;

...

ManagementObject os = new ManagementObject("Win32_OperatingSystem=@");
string serial = (string)os["SerialNumber"];

Using the [] operator, you can get any property in the class.


I my humble opinion none of the answers satisfies the question; is pretty straight forward asking for a way to read the MachineGuid from the registry... so here is my answer: You will need to add a reference to "Microsoft.Win32". This code was written for demonstration purposes and should be adapted accordingly.

EDIT: Someone stated wrongly that the x64 code is useless.
In 64 bit OS that is where the correct key is found.

So this answer stands as the only one that satisfies the question.

private void buttonGetMachineGuid_Click(object sender, RoutedEventArgs e)
{
  try
  {
    string x64Result = string.Empty;
    string x86Result = string.Empty;
    RegistryKey keyBaseX64 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
    RegistryKey keyBaseX86 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32);
    RegistryKey keyX64 = keyBaseX64.OpenSubKey(@"SOFTWARE\Microsoft\Cryptography", RegistryKeyPermissionCheck.ReadSubTree);
    RegistryKey keyX86 = keyBaseX86.OpenSubKey(@"SOFTWARE\Microsoft\Cryptography", RegistryKeyPermissionCheck.ReadSubTree);
    object resultObjX64 = keyX64.GetValue("MachineGuid", (object)"default");
    object resultObjX86 = keyX86.GetValue("MachineGuid", (object)"default");
    keyX64.Close();
    keyX86.Close();
    keyBaseX64.Close();
    keyBaseX86.Close();
    keyX64.Dispose();
    keyX86.Dispose();
    keyBaseX64.Dispose();
    keyBaseX86.Dispose();
    keyX64 = null;
    keyX86 = null;
    keyBaseX64 = null;
    keyBaseX86 = null;
    if(resultObjX64 != null && resultObjX64.ToString() != "default")
    {
      MessageBox.Show(resultObjX64.ToString());
    }
    if(resultObjX86 != null && resultObjX86.ToString() != "default")
    {
      MessageBox.Show(resultObjX86.ToString());
    }
  }
  catch(Exception)
  {
  }
}

Hope this helps some one.


which is probably why it doesn't work reliably among different versions of Windows

No, that's not the reason. This problem is caused by the platform target selection for your EXE project. Project + Properties, Build tab, Platform target combobox. You have it set to x86 instead of AnyCPU. On VS2012, the "Prefer 32-bit" checkbox matters. This setting forces your program to run in 32-bit mode on a 64-bit version of Windows. Which has a number of side effects, the one that matters here is that access to registry keys are redirected. Your program is actually reading the value of HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Cryptography\MachineGuid. Which doesn't exist.

The x86 selection is the default for VS2010 and up, previously AnyCPU was the default. Microsoft prefers x86, Visual Studio works better with 32-bit mode processes. Particularly when debugging, VS is a 32-bit process itself so requires the remote debugger if your program executes in 64-bit mode. Which has a few limitations like not supported mixed-mode debugging. And the Edit + Continue feature only works for 32-bit code. Your program itself however works "better" if you have the setting at AnyCPU, including not getting bitten by the file system and registry redirection appcompat features built into Windows.

If you are really stuck with x86 mode, typically because you have a dependency on 32-bit native code that you can't update then the next workaround is to use the .NET 4+ RegistryKey.OpenBaseKey() method. Which allows you to pass RegistryView.Registry64, ensuring that you'll read the non-redirected keys.

Sure, using WMI is a workaround. Just keep in mind that you are not reading the same information when you use Win32_OperatingSystem.SerialNumber. To what degree that key is reliably random on different machines isn't that clear to me, let's just say that this value is a pretty attractive target for the kind of users that are not very interested in paying the license fee for your product either.

Last but not least, do consider that it is pretty easy to generate your own unique id that doesn't depend at all on Windows. With the considerable advantage that you won't piss off your customer when he updates Windows on his machine. Just use Guid.NewGuid() once and store the value in a file. That will be lost when the drive goes bad, but that usually takes out your product as well.