All posts tagged registry

Getting the Windows Key

Categories: .Net, C#, Console, WPF
Comments: No

In my last post I went over how to retrieve the Windows Product Activation information from Windows Vista / 7.0 / 2008. Today, we are going to do something on the same thread but very much more desirable. And that is retrieving the Windows Key information.

For as long as Windows has had a key, admins have been looking to be able to retrieve the Windows key information on the fly: especially when they need to do a re-install and lost their original media casing.

So, let’s get right to it with a console application.


static void Main(string[] args)
{
    string computername = Environment.MachineName;
    const RegistryHive registryRoot = RegistryHive.LocalMachine;
    const string sSubKeyName = @"SOFTWAREMicrosoftWindows NTCurrentVersion";
    const string sValueName = "DigitalProductId";
    string windowsKey = "";

    // Get product Id
    RegistryKey regoutput = RegistryKey.OpenRemoteBaseKey(
        registryRoot, computername, RegistryView.Default).
        OpenSubKey(sSubKeyName);

    // Convert to byte array
    byte[] digitalProductId;
    if (regoutput != null)
    {
        digitalProductId = regoutput.GetValue(sValueName) as byte[];

        // Get Windows Product Key
        windowsKey = (digitalProductId != null)
            ? DecodeProductKey(digitalProductId)
            : "The product key was not accessible";
    }

    else
    {
        windowsKey = "The product key was not accessible";
    }

    // Output 
    Console.WriteLine(windowsKey);
    Console.Read();
}

In this example, I am choosing my local machine as the target. The location of the value we want is located in the registry here:
HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows NTCurrentVersionDigitalProductId.

We then get the byte array representing the key and decode it with the below method.


/// <summary>
/// Decodes a Microsoft product key based on the provided digital product id.
/// </summary>
/// <param name="digitalProductId">The digital product id.</param>
/// <returns></returns>
private static string DecodeProductKey(byte[] digitalProductId)
{
    // Offset of first byte of encoded product key in 
    //  'DigitalProductIdxxx" REG_BINARY value. Offset = 34H.
    const int keyStartIndex = 52;

    // Offset of last byte of encoded product key in 
    //  'DigitalProductIdxxx" REG_BINARY value. Offset = 43H.
    const int keyEndIndex = keyStartIndex + 15;

    // Possible alpha-numeric characters in product key.
    var digits = new[]
            {
                'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'M', 'P', 'Q', 'R',
                'T', 'V', 'W', 'X', 'Y', '2', '3', '4', '6', '7', '8', '9'
            };

    // Length of decoded product key
    const int decodeLength = 29;

    // Length of decoded product key in byte-form.
    // Each byte represents 2 chars.
    const int decodeStringLength = 15;

    // Array of containing the decoded product key.
    var decodedChars = new char[decodeLength];

    // Extract byte 52 to 67 inclusive.
    var hexPid = new ArrayList();
    for (int i = keyStartIndex; i <= keyEndIndex; i++)
    {
        hexPid.Add(digitalProductId[i]);
    }
    for (int i = decodeLength - 1; i >= 0; i--)
    {
        // Every sixth char is a separator.
        if ((i + 1) % 6 == 0)
        {
            decodedChars[i] = '-';
        }
        else
        {
            // Do the actual decoding.
            int digitMapIndex = 0;
            for (int j = decodeStringLength - 1; j >= 0; j--)
            {
                int byteValue = (digitMapIndex << 8) | (byte)hexPid[j];
                hexPid[j] = (byte)(byteValue / 24);
                digitMapIndex = byteValue % 24;
                decodedChars[i] = digits[digitMapIndex];
            }
        }
    }
    return new string(decodedChars);
}

Now, if you are running 64-bit Windows (and most of us are), you may be running into problems getting your byte array. Why is that? Well, the short answer is that it has to do with your Configuration Manager settings. The default platform target is x86: which does not have access to several 64-bit spaces including certain registry values.

You can change that by doing the following in Visual Studio:

  1. Open your Project Properties.
  2. Go to the Build tab.
  3. For each Configuration set the Platorm target to Any CPU.
  4. For Visual Studio 2012, make sure to uncheck Prefer 32-bit.

That’s all there is to it.

Until next time.

In today’s blog I am going to give you the method I use to obtain the Windows Product Activation information in Astronomy.

Q: So, where does this information come from?
A: Well, as you may know, you can obtain this information from the following script:
C:\Windows\System32\slmgr.vbs

I always felt this needed to be modified for use in .Net, so I refactored this a bit and created my own replacement method:


/// <summary>
/// Retrieves licensing information.
/// </summary>
/// <param name="managementScope">The management scope.</param>
/// <returns></returns>
public static string GetLicensingInfo(ManagementScope managementScope)
{
	// Output
	string result = "Information Unavailable";
	const string windowsAppId = "55c92734-d682-4d71-983e-d6ec3f16059f";

	// Default values
	const uint HR_SL_E_GRACE_TIME_EXPIRED = 0xC004F009;
	const uint HR_SL_E_NOT_GENUINE = 0xC004F200;

	// Set WMI query
	var query =
		new WqlObjectQuery(
			"SELECT LicenseStatus, GracePeriodRemaining " +
			"FROM SoftwareLicensingProduct " +
			"WHERE (PartialProductKey <> NULL) AND " +
			"(ApplicationId='" + windowsAppId + "')");

	// Get items
	using (var searcher = new ManagementObjectSearcher(managementScope, query))
	
	// Get data
	using (var colItems = searcher.Get())
	{
		foreach (ManagementBaseObject obj in colItems)
		{
			string subline;

			// License Status
			int licenseStatus = Convert.ToInt32(obj.GetPropertyValue("LicenseStatus"));

			// Time remaining: minutes / days
			int gpMin = Convert.ToInt32(obj.GetPropertyValue("GracePeriodRemaining"));
			int gpDay = gpMin / (24 * 60);

			// Evaluate
			switch (licenseStatus)
			{
				case 0:
					result = "Unlicensed";
					break;

				case 1:
					result = "Licensed";
					if (gpMin > 0)
					{
						subline =
							String.Format(
								"Activation expiration: " +
								"{0} minute(s) ({1} day(s))", gpMin, gpDay);
						result += "\n" + subline;
					}
					break;

				case 2:
					result = "Initial grace period";
					subline =
						String.Format("Time remaining: {0} minute(s) " +
										"({1} day(s))", gpMin, gpDay);
					result += "\n" + subline;
					break;

				case 3:
					result =
						"Additional grace period (KMS license expired or " +
						"hardware out of tolerance)";
					subline =
						String.Format("Time remaining: {0} minute(s) " +
										"({1} day(s))", gpMin, gpDay);
					result += "\n" + subline;
					break;

				case 4:
					result = "Non-genuine grace period.";
					subline =
						String.Format("Time remaining: {0} minute(s) " +
										"({1} day(s))", gpMin, gpDay);
					result += "\n" + subline;
					break;

				case 5:
					result = "Notification";

					uint licenseStatusReason;
					uint.TryParse(obj.GetPropertyValue("GracePeriodRemaining").ToString(), 
						out licenseStatusReason);
						
					// Evaluate
					switch (licenseStatusReason)
					{
						case HR_SL_E_NOT_GENUINE:
							subline =
								String.Format(
									"Notification Reason: 0x{0} " +
									"(non-genuine).", licenseStatusReason);
							break;

						case HR_SL_E_GRACE_TIME_EXPIRED:
							subline =
								String.Format(
									"Notification Reason: 0x{0} " +
									"(grace time expired).", licenseStatusReason);
							break;

						default:
							subline =
								String.Format("Notification Reason: 0x{0}.",
												licenseStatusReason);
							break;
					}
					result += "\n" + subline;
					break;

				case 6:
					result = "Extended grace period";
					subline =
						String.Format("Time remaining: {0} minute(s) " +
										"({1} day(s))", gpMin, gpDay);
					result += "\n" + subline;
					break;

				default:
					result = "Unknown";
					break;
			}
		}
	}
	
	// return
	return result;
}

An overboard implementation:


/// <summary>
/// Connects to a target computer through Kerberos authentication.
/// </summary>
/// <param name="computername">The computername.</param>
/// <param name="user">The user.</param>
/// <param name="securePass">The secure pass.</param>
/// <returns>
/// A ManagementScope context for the current connection.
/// </returns>
public static ManagementScope Connect(string computername, string user, 
	SecureString securePass)
{
	// Build an options object for the remote connection
	var options = new ConnectionOptions
		{
			Impersonation = ImpersonationLevel.Impersonate,
			EnablePrivileges = true
		};

	// Set properties
	// Check name
	if (String.IsNullOrEmpty(computername))
	{
		computername = ".";
	}

	// Check credentials
	// Cannot pass a blank user and password.
	if (!String.IsNullOrEmpty(user))
	{
		options.Username = user;
		options.SecurePassword = securePass;
	}
	
	// Make a connection to a remote computer.
	var managementScope =
		new ManagementScope($@"\{computername}\root\cimv2", options);

	try
	{
		// Connect
		managementScope.Connect();
	}
	catch (Exception ex)
	{
		throw ex;
	}
	
	// return
	return managementScope;
}

/// <summary>
/// Gets the license you so desperately want.
/// </summary>
public void GetMyLicense()
{
	// Get the management scope
	var myManagementScope = Connect(".", "XCALIBUR\jarzt", {my secure password});

	// Get the licensing
	string licensing = GetLicensingInfo(myManagementScope);

	// Report to client
	MessageBox.Show("My current licensing status: " + licensing);
}

So, really that’s all there is to it.

Happy coding!

This is a bit dated, but the question is about how you get (near) accurate processor information in Windows 2003 with WMI and no external executables.

I wrote this a long time ago for VBS and translated it into C# for my upcoming application: Astronomy. I added some snazzy XAML in there to pimp it out a bit. There is a lot of code here, but that is because a lot needs to be done to format the data into something we want to see.

The desired result:

So, let’s see all that code:

The XAML:


<Window x:Class="GetProcessCountLegacy.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="200" Width="525">

    <Window.Resources>

        <!-- Text Styles -->
        <Style x:Key="ListHeader" TargetType="{x:Type TextBlock}">
            <Setter Property="FontWeight" Value="Bold" />
        </Style>
        <Style x:Key="ListLabel" TargetType="{x:Type TextBlock}">
            <Setter Property="FontWeight" Value="Normal" />
        </Style>
        <Style x:Key="ListValue" TargetType="{x:Type TextBlock}">
            <Setter Property="FontWeight" Value="DemiBold" />
        </Style>

        <!-- Border -->
        <Style x:Key="BorderStyle" TargetType="Border">
            <Setter Property="BorderBrush" Value="#999" />
            <Setter Property="BorderThickness" Value="1" />
            <Setter Property="CornerRadius" Value="6" />
            <Setter Property="Padding" Value="16,14,16,16" />
            <Setter Property="Margin" Value="12,6,12,6" />
            <Setter Property="Background">
                <Setter.Value>
                    <LinearGradientBrush StartPoint="0.5, 0" EndPoint="0.5, 1" Opacity="1.0">
                        <GradientStop Color="#FFFFFF" Offset="0" />
                        <GradientStop Color="#FFFFFF" Offset="0.5" />
                        <GradientStop Color="#EDEDED" Offset="1" />
                    </LinearGradientBrush>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>

    <Grid>
        <!-- Processor Information -->
        <Border Style="{StaticResource BorderStyle}">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="28"/>
                    <RowDefinition Height="22"/>
                    <RowDefinition Height="22"/>
                    <RowDefinition Height="22"/>
                    <RowDefinition Height="22"/>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="120"/>
                    <ColumnDefinition Width="600"/>
                </Grid.ColumnDefinitions>

                <TextBlock Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
                                       VerticalAlignment="Top"
                                       Text="Processor Information"
                                       Style="{StaticResource ListHeader}" />

                <TextBlock Grid.Row="1" Grid.Column="0"
                                       Text="Description:"
                                       Style="{StaticResource ListLabel}" />
                <TextBlock Grid.Row="1" Grid.Column="1"
                                       x:Name="txtDescription"
                                       Style="{StaticResource ListValue}" />

                <TextBlock Grid.Row="2" Grid.Column="0"
                                       Text="Core Speed:"
                                       Style="{StaticResource ListLabel}" />
                <TextBlock Grid.Row="2" Grid.Column="1"
                                       x:Name="txtSpeed"
                                       Style="{StaticResource ListValue}" />

                <TextBlock Grid.Row="3" Grid.Column="0"
                                       Text="Count:"
                                       Style="{StaticResource ListLabel}" />
                <TextBlock Grid.Row="3" Grid.Column="1"
                                       x:Name="txtCount"
                                       Style="{StaticResource ListValue}" />

                <TextBlock Grid.Row="4" Grid.Column="0"
                                       Text="Width:"
                                       Style="{StaticResource ListLabel}" />
                <TextBlock Grid.Row="4" Grid.Column="1"
                                       x:Name="txtWidth"
                                       Style="{StaticResource ListValue}" />
            </Grid>
        </Border>
    </Grid>
</Window>

C#

[Main Code]


    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        #region Contructors

        /// <summary>
        /// Main constructor.
        /// </summary>
        public MainWindow()
        {
            // Initialize UI
            InitializeComponent();

            // Get processor information
            GetProcessorInformation();
        }

        #endregion

        /// <summary>
        /// Retrieve the processor information and add to UI.
        /// </summary>
        private void GetProcessorInformation()
        {
            // Build an options object for the remote connection
            var options = new ConnectionOptions
                {
                    Impersonation = ImpersonationLevel.Impersonate,
                    EnablePrivileges = true
                };

            // Make a connection to a remote computer.
            var ms = new ManagementScope(@"" + "." + @"rootcimv2", options);

            // Query
            string query = "SELECT Manufacturer, MaxClockSpeed, AddressWidth, Name, " +
                           "SocketDesignation, ProcessorID, UniqueID FROM Win32_Processor";

            // Searcher
            var searcher =
                new ManagementObjectSearcher(ms, new WqlObjectQuery(query));

            // Get legacy info
            var processor = GetProcessorInfoLegacy(searcher);

            // Processor information
            // Description
            txtDescription.Text = RemoveExcessSpaces(processor.Name);
            if (String.IsNullOrEmpty(txtDescription.Text))
            {
                txtDescription.Text = "{Unknown}";
            }

            // Speed
            txtSpeed.Text = GetProcessorSpeed(processor.Speed);
            if (String.IsNullOrEmpty(txtSpeed.Text))
            {
                txtSpeed.Text = "{Unknown}";
            }

            // Count
            txtCount.Text = GetProcessorCount(5.2, processor);
            if (String.IsNullOrEmpty(txtCount.Text))
            {
                txtCount.Text = "{Unknown}";
            }

            // Width
            txtWidth.Text = "x86-" + processor.Architecture;
            if (String.IsNullOrEmpty(txtWidth.Text))
            {
                txtWidth.Text = "{Unknown}";
            }
        }

        /// <summary>
        /// Gets processor information for Windows version 5.x.
        /// </summary>
        /// <param name="searcher"></param>
        /// <returns></returns>
        private static ProcessorInfo GetProcessorInfoLegacy(
            ManagementObjectSearcher searcher)
        {
            // Processor object
            var processor = new ProcessorInfo();

            // Descriptors
            var socket = new List<string>();
            var procId = new List<string>();
            var uniqueId = new List<string>();

            // Get data
            ManagementObjectCollection colItems = searcher.Get();

            try
            {
                // Evaluate data
                foreach (ManagementBaseObject objItem in colItems)
                {
                    // Manufacturer
                    if (objItem["Manufacturer"] != null)
                    {
                        processor.Manufacturer = objItem["Manufacturer"].ToString();
                    }

                    // Speed
                    if (objItem["MaxClockSpeed"] != null)
                    {
                        processor.Speed = Convert.ToInt32(objItem["MaxClockSpeed"]);
                    }

                    // Architecture
                    if (objItem["AddressWidth"] != null)
                    {
                        processor.Architecture = objItem["AddressWidth"].ToString();
                    }

                    // Socket Designation
                    if (objItem["SocketDesignation"] != null)
                    {
                        processor.SocketDesignation =
                            objItem["SocketDesignation"].ToString();
                        socket.Add(processor.SocketDesignation);
                    }

                    // Name
                    if (objItem["Name"] != null)
                    {
                        processor.Name = objItem["Name"].ToString();
                    }

                    // ProcessorID
                    if (objItem["ProcessorID"] != null)
                    {
                        processor.ProcessorID = objItem["ProcessorID"].ToString();
                        procId.Add(processor.ProcessorID);
                    }
                    else
                    {
                        procId.Add("");
                    }

                    // UniqueID
                    if (objItem["UniqueID"] != null)
                    {
                        processor.UniqueID = objItem["UniqueID"].ToString();
                        uniqueId.Add(processor.UniqueID);
                    }
                    else
                    {
                        uniqueId.Add("");
                    }
                }

                // Logical count
                int totalProcCount = colItems.Count;
                processor.LogicalProcessors = totalProcCount;

                // Cores
                GetLegacyCoreCount(socket, procId, uniqueId, ref processor);

                // Get #of possible sockets
                if ((processor.Cores > 0) && (processor.LogicalProcessors > 0))
                {
                    int result = (processor.LogicalProcessors / processor.Cores);
                    processor.Count = result;
                }
            }
            catch
            {
            }
            finally
            {
                colItems.Dispose();
            }

            // return
            return processor;
        }

        /// <summary>
        /// Gets the number of processor cores for Windows version 5.x.
        /// </summary>
        /// <param name="socket"></param>
        /// <param name="procId"></param>
        /// <param name="uniqueId"></param>
        /// <param name="processor"></param>
        private static void GetLegacyCoreCount(List<string> socket, List<string> procId,
                                               List<string> uniqueId, ref ProcessorInfo processor)
        {
            int totalProcessors = 0;

            // Processor marker

            // Check Socket Designation
            for (int i = 0; i < socket.Count; i++)
            {
                // Start with the assumption this is unique.
                bool isUnique = true;

                // Check for Redundancies
                for (int j = i + 1; j < socket.Count; j++)
                {
                    if (socket[i] == socket[j])
                    {
                        isUnique = false;
                        break;
                    }
                }

                // If Redundant Unique ID's Exist
                for (int j = i + 1; j < socket.Count; j++)
                {
                    if ((uniqueId[i] != "") && (uniqueId[j] != "") &&
                        (uniqueId[i] != uniqueId[j]))
                    {
                        isUnique = true;
                        break;
                    }
                }

                // Check for NULL ProcessorID
                if (procId[i].Trim() == "0000000000000000")
                {
                    isUnique = false;
                }

                // Calculate Total
                if (isUnique)
                {
                    totalProcessors++;
                }
            }

            // Get Cores
            int result = (processor.LogicalProcessors / totalProcessors);
            processor.Cores = result;
        }

        /// <summary>
        /// Retrieves the Processor speed in human-readable format.
        /// </summary>
        /// <param name="speed"></param>
        /// <returns></returns>
        public static string GetProcessorSpeed(int speed)
        {
            string result = string.Empty;

            if (speed.ToString().Length >= 4)
            {
                double dSpeed = Convert.ToDouble(speed) / 1000;
                result = dSpeed.ToString("0.00") + " GHz";
            }
            else
            {
                result = speed.ToString() + " MHz";
            }

            // Return
            return result;
        }

        /// <summary>
        /// Returns the processor count in a human-readable format.
        /// </summary>
        /// <param name="oSver"></param>
        /// <param name="proc"></param>
        /// <returns></returns>
        public static string GetProcessorCount(double oSver, ProcessorInfo proc)
        {
            string result = "";

            int physical = proc.Count;
            int cores = proc.Cores;

            // Manufacturer
            string hyper = "HyperThreading";
            if (proc.Manufacturer.ToLower().IndexOf("intel") == -1)
            {
                hyper = "HyperTransport";
            }

            // Processor count
            string physDesc = physical.ToString() + " processor(s)";

            // Cores
            string coreDesc = string.Empty;

            // Current
            if (oSver >= 6)
            {
                coreDesc = cores.ToString() + " core(s)";
                if (proc.IsHyperThreaded)
                {
                    coreDesc += " w/ HyperThreading";
                }
            }

                // Legacy
            else
            {
                coreDesc = cores.ToString() + " core(s)";

                if ((cores / physical) == 2)
                {
                    // Intel
                    if (proc.Manufacturer.ToLower().IndexOf("intel") != -1)
                    {
                        coreDesc += " (or " + physical + " core(s) w/ " + hyper + ")";
                    }
                }
            }

            result = physDesc + " (" + coreDesc + ")";

            // Return
            return result;
        }

        /// <summary>
        /// Removes extra spaces between words.
        /// </summary>
        /// <param name="procDesc"></param>
        /// <returns></returns>
        public static string RemoveExcessSpaces(string procDesc)
        {
            string result = "";

            // Evaluate
            string[] desc = procDesc.Split((char)32);

            // Name
            for (int i = 0; i < desc.Length; i++)
            {
                if (desc[i].Trim() != "")
                {
                    result += desc[i] + " ";
                }
            }

            // Return
            return result;
        }
    }

[ProcessorInfo class]


    /// <summary>
    /// Information relevant to the physical processors.
    /// </summary>
    public class ProcessorInfo
    {
        #region Properties

        /// <summary>
        /// Win32_Processor: AddressWidth: On a 32-bit operating system, the value is 32
        /// and on a 64-bit operating system it is 64.
        /// </summary>
        public string Architecture { get; set; }

        /// <summary>
        /// Win32_Processor: NumberOfCores: Number of cores for the current instance of
        /// the processor.
        /// </summary>
        public int Cores { get; set; }

        /// <summary>
        /// Total processor count.
        /// </summary>
        public int Count { get; set; }

        /// <summary>
        /// Determines whether processors support Hyper-Threading or Hyper-Transport.
        /// </summary>
        public bool IsHyperThreaded { get; set; }

        /// <summary>
        /// Win32_Processor: NumberOfLogicalProcessors: Number of logical processors for
        /// the current instance of the processor.
        /// </summary>
        public int LogicalProcessors { get; set; }

        /// <summary>
        /// Win32_Processor: Manufacturer: Name of the processor manufacturer.
        /// </summary>
        public string Manufacturer { get; set; }

        /// <summary>
        /// Win32_Processor: Name: Label by which the object is known.
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// Win32_Processor: ProcessorID: Processor information that describes the
        /// processor features.
        /// </summary>
        public string ProcessorID { get; set; }

        /// <summary>
        /// Win32_Processor: SocketDesignation: Type of chip socket used on the circuit.
        /// </summary>
        public string SocketDesignation { get; set; }

        /// <summary>
        /// Win32_Processor: MaxClockSpeed: Detected processor speed.
        /// </summary>
        public int Speed { get; set; }

        /// <summary>
        /// Win32_Processor: UniqueID: Globally unique identifier for the processor.
        /// </summary>
        public string UniqueID { get; set; }

        #endregion

        #region Constructors

        public ProcessorInfo()
        {
            Architecture = "x86";
        }

        #endregion
    }