Click here to Skip to main content
15,913,685 members
Please Sign up or sign in to vote.
1.00/5 (1 vote)
See more:
hello,
How to get ProductId in windows 7 64 bit using C++?
Posted
Comments
Richard MacCutchan 23-Feb-12 10:47am    
See my last entry in response to enhzflep's second solution.

Many, many, MANY thanks to Richard for his invaluable insight.

I've modded the old code I have such that
C++
ReturnStatus = RegOpenKeyEx( HKEY_LOCAL_MACHINE, RegKey, 0, KEY_QUERY_VALUE, &Registry );

becomes
C++
ReturnStatus = RegOpenKeyEx( HKEY_LOCAL_MACHINE, RegKey, 0, KEY_QUERY_VALUE | KEY_WOW64_64KEY, &Registry );


I can now happily report that the decrypt function that works for XP generates the same ProductKey as other available solutions (MagicJellyBean KeyFinder, WinKeyFinder, ProduKey-64).

Here you go ladies and gents:

C++
char* DecodeMicrosoftKey( BYTE* digitalProductId )
{

    /* NULL is a valid byte value, so check for it. */
    if ( digitalProductId )
    {
        /* Offset first value to 34H. */
        const int keyStartIndex = 52;
        /* Offset last value to 43H. */
        const int keyEndIndex = keyStartIndex + 15;
        /* Valid Product Key Characters. */
        char digits[] =
        {
            '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 key in byte-form (each byte = 2 chars). */
        const int decodeStringLength = 15;
        /* Array to contain decoded key. */
        char* pDecodedChars = new char[ decodeLength + 1 ];

        memset( pDecodedChars, 0, decodeLength + 1 );

        /* Extract byte 52 to 67 inclusive. */
        byte hexPid[ keyEndIndex - keyStartIndex + 1 ];

        for ( int i = keyStartIndex; i <= keyEndIndex; i++ )
        {
            hexPid[ i - keyStartIndex ] = digitalProductId[ i ];
        }

        for ( int i = decodeLength - 1; i >= 0; i-- )
        {
            /* Every 6th character is a seperator. */
            if ( ( i + 1 ) % 6 == 0 )
            {
                *( pDecodedChars + i ) = '-';
            }
            else
            {
                /* Do the actual decoding. */
                int digitMapIndex = 0;
                for ( int j = decodeStringLength - 1; j >= 0; j-- )
                {
                    int byteValue = ( digitMapIndex << 8 ) | hexPid[ j ];
                    hexPid[ j ] = ( byte )( byteValue / 24 );
                    digitMapIndex = byteValue % 24;
                    *( pDecodedChars + i ) = digits[ digitMapIndex ];
                }
            }
        }
        /*
         * Return the decoded product key.
         */
        return pDecodedChars;
    }
    /* digitalProductID was passed as a NULL value, return NULL. */
    else
    {
        return NULL;
    }
}
 
Share this answer
 
Comments
Richard MacCutchan 23-Feb-12 11:17am    
Thanks, I seem to have lost my copy of this code.
You can read it from the registry key
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProductId.

[UPDATE]
The WMI Win32_OperatingSystem class can be also used (SerialNumber member) [^]
[/UPDATE]
 
Share this answer
 
v2
I would have said the same thing as Jochen did in Answer 1, _however_ it seems that this task is not quite so simple...

I coded a program that retrieves assorted windows installation stats:
Product Name
Product Id
Product Key
Registered To
Service Pack
Build No

My program attempts to read from the key mentioned in Answer 1. The problem I have, is that this program worked fine in Win2003(x86) and WinXp(x86).

Now, when I run the same executable under Win7 x64, Product ID, Product Key, Service Pack information are all returned as empty strings. The "Registered To" field is always filled with "Microsoft"

The most interesting thing is that nowhere in the registry key HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion does the string "Microsoft" exist!!!!

Sure enough, browsing to the mentioned key using RegEdit.exe does present the expected information..

(And yes! I did try the program in both User and Admin mode - same result)

Scratches head - hopes for good answer. The Windows sticker on the bottom of my laptop is no longer legible. The OS seems like it would benefit from being re-installed. Problem? No Product Key is returned to use during the (re)installation. :sad:

Although, I guess I could simply copy the data as read in RegEdit before running it through the key decryption function.

I'll try it and report results.


[EDIT:] Just checked the program again in WinXP (running inside VirtualBox emulator) -- it works just fine. On closer examination, I can see that in XP, this field(DigitalProductId) contains 0xA4 bytes. While in Win7(x64), this field is now contains 0x4F0 bytes.

I should also mention that even when editing this field, one cannot simply select all of the data and copy it - windows copies nothing to the clipboard, even though right-click brings up a menu that includes cut and copy. Copy does absolutely nothing, Cut deletes the text but doesn't copy it to the clipboard. (same behaviour in XP and Win7)


I'm beginning to suspect that the OS checks who is trying to read particular registry keys, before tailoring the returned result to 'suit'.

Really hope I don't have to chew on someone's ear at DickSmith, Acer or Microsoft
 
Share this answer
 
Comments
Richard MacCutchan 23-Feb-12 5:39am    
If you look at that key using regedit you will notice a number of values. Some of these are common to all installations and some are OEM or even version specific. The only way to handle this issue is to enumerate the values and check the returned keys against the list of items you are interested in.
enhzflep 23-Feb-12 6:01am    
Yes indeed, cheers for that. This differs from the key in XP in so far as the Win7 they clearly contains data that is both (a) readable and (b) contained within other keys under HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion

:face-palm:
Just realized that the Win7 RegQueryEx is playing funny buggers! When this function is used to retrieve the value of HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\DigitalProductId, guess what? The fifth parameter (a pointer to a buffer used to store the number of characters returned) is filled with ZERO!! Even when the correct information is retrieved!!

Very seriously suspecting that Microsoft has made the retrieval of this set of keys difficult on purpose. :(
Richard MacCutchan 23-Feb-12 6:14am    
DigitalProductId is a binary value so it returns a number of bytes, not characters. You also need to specify the correct type (or accept any) when trying to retrieve it.
Jochen Arndt 23-Feb-12 5:52am    
I have just tried it with a 32-bit app runing on Win 7 Pro 64. I can read the value. Is your program a 64-bit one?
Not really an answer, though I'd be keen to understand the issue better. Here's the code I've just whipped up to display the things I mention.

Code:
C++
#include <windows.h>
#include <stdio.h>

byte* GetRegistryKeyValue(const char* RegKey, const char* pPIDName)
{
    HKEY		Registry;
    long		ReturnStatus;
    DWORD       regType	= 0;
    DWORD       regSize = 0;
    byte*		pPID = 0;

    /* Open Key. */
    ReturnStatus = RegOpenKeyEx( HKEY_LOCAL_MACHINE, RegKey, 0, KEY_QUERY_VALUE, &Registry );

    if ( ReturnStatus == ERROR_SUCCESS )
    {
        /* get size of key */
        ReturnStatus = RegQueryValueEx(Registry, pPIDName, 0, ®Type, 0, ®Size);
        pPID = new byte[ regSize ];

        /* Get Value. */
        ReturnStatus = RegQueryValueEx( Registry, pPIDName, 0, ®Type, pPID, ®Size );
        RegCloseKey( Registry );

        /*
         * Check & trim last character if ascii value is > 127.
         * Some companies (WebsuperGoo) seem to append an extended
         * ascii character
         */
         if ( pPID[regSize] > 127 || pPID[regSize] < 32 )
         {
            pPID[regSize] = '\0';
         }

        /* Ensure we're returning a valid result. */
        if ( regSize > 1 )
        {
            printf("Size > 1 (%d)\n", regSize);
            return pPID;
        }
        else
        {
            printf("Size not > 1 (%d)\n", regSize);
            return NULL;
        }
    }
    else
    {
        /* Close Key. */
        RegCloseKey( Registry );
        return NULL;
    }
}


int main()
{
    BYTE *resultData;
    printf("-\n");
    resultData = GetRegistryKeyValue("SOFTWARE\\MICROSOFT\\Windows NT\\CurrentVersion", "ProductName");
    printf("ProductName: %s\n",resultData);
    delete resultData;

    printf("-\n");
    resultData = GetRegistryKeyValue("SOFTWARE\\MICROSOFT\\Windows NT\\CurrentVersion", "ProductId");
    delete resultData;

    printf("-\n");
    resultData = GetRegistryKeyValue("SOFTWARE\\MICROSOFT\\Windows NT\\CurrentVersion", "DigitalProductId");
    delete resultData;

    printf("-\n");
    resultData = GetRegistryKeyValue("SOFTWARE\\MICROSOFT\\Windows NT\\CurrentVersion", "RegisteredOwner");
    printf("RegisteredOwner: %s\n",resultData);
    delete resultData;

    printf("-\n");
    resultData = GetRegistryKeyValue("SOFTWARE\\MICROSOFT\\Windows NT\\CurrentVersion", "CSDVersion");
    printf("ServicePack: %s\n",resultData);
    delete resultData;

    return 0;
}
</stdio.h></windows.h>


Result:
-
Size > 1 (23)
ProductName: Windows 7 Home Premium
-
Size not > 1 (0)
-
Size not > 1 (0)
-
Size > 1 (10)
RegisteredOwner: Microsoft
-
Size not > 1 (0)
ServicePack: (null)

Process returned 0 (0x0)   execution time : 0.103 s
Press any key to continue.




Keys as displayed by RegEdit:
ProductName = "Windows 7 Home Premium"
ProductId = "00359-OEM-8992687-00006"
DigitalProductId = (164 bytes)
RegisteredOwner = "enhzflep"
CSDVersion = "1"


Clearly, all but one of these values are being incorrectly reported on my machine.
Also, we can see that attempts to retrieve ProductId and DigitalProductId fail, since RegQueryValueEx is returning the number of bytes read as zero.

This same code behaves differently under XP x86 and Win7 x64.

Anybody else have any suggestions?
I also have the decode function to turn the (encrypted)DigitalProductId key back into the (decrypted, plain-text)product key used when installing the OS if anybody wants it.
 
Share this answer
 
Comments
Jochen Arndt 23-Feb-12 7:07am    
There are buffer overruns when accessing pPID[regSize].
enhzflep 23-Feb-12 7:16am    
Changed all instances of pPID[regSize] with pPID[regSize-1]
The output remains the same. Still don't know where the "Microsoft" is appearing from..
Jochen Arndt 23-Feb-12 7:29am    
The line printf("ProductID: %s\n",resultData); is missing.
With that line I got (ID anonymized):
<pre>
-
Size > 1 (23)
ProductName: Windows 7 Professional
-
Size > 1 (24)
ProductID: 00371-OEM-8992671-XXXXX
-
Size not > 1 (0)
-
Size > 1 (10)
RegisteredOwner: joe_local
-
Size not > 1 (0)
ServicePack: (null)
</pre>
Richard MacCutchan 23-Feb-12 10:33am    
I just tried a similar test and even running as administrator it did not return the four values CSDVersion, DigitalProductId, DigitalProductId4, ProductId. I tried to get them individually using RegQueryValueEx() but it returned ERROR_FILE_NOT_FOUND. Most curious.
Richard MacCutchan 23-Feb-12 10:46am    
Found it!
When creating your registry key you need to OR in the KEY_WOW64_64KEY access value. This bypasses the 'smarts' that MS added to the 64 bit OS.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900