简体   繁体   中英

How can i read a REG_BINARY values associated value from registry?

In the registry there is one ( or more ) key depending how many monitors you have HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Enum\\DISPLAY\\DEL404C\\{Some Unique ID}\\Device Parameters\\EDID which is a REG_BINARY key . In my case this is :

00 FF FF FF FF FF FF 00 10 AC 4C 40 53 43 34 42 34 14 01 03 0A 2F 1E 78 EE EE 95 A3 54
4C 99 26 0F 50 54 A5 4B 00 71 4F 81 80 B3 00 01 01 01 01 01 01 01 01 01 01 21 39 90 30 
62 1A 27 40 68 B0 36 00 DA 28 11 00 00 1C 00 00 00 FF 00 34 57 31 4D 44 30 43 53 42 34 
43 53 0A 00 00 00 FC 00 44 45 4C 4C 20 50 32 32 31 30 0A 20 20 00 00 00 FD 00 38 4B 1E 
53 10 00 0A 20 20 20 20 20 20 00 FA

This reg_binary value contains information (such as Serial Number and Type) about the connected monitor. I only need these two values. My question is how can i read these values using C or C++?

I have a VB script which can do this:
'you can tell If the location contains a serial number If it starts with &H00 00 00 ff strSerFind=Chr(&H00) & Chr(&H00) & Chr(&H00) & Chr(&HfF)

'or a model description If it starts with &H00 00 00 fc

strMdlFind=Chr(&H00) & Chr(&H00) & Chr(&H00) & Chr(&Hfc)

This link also contains information about EDID: http://en.wikipedia.org/wiki/Extended_display_identification_data

Could someone help me, how can i do this in C? I can find only VB script examples, but unfortunately i don't understand them, and also it would be very important for me.

You mention wanting the "serial number" and "type". There is no "type" but there is a manufacturer ID and a product ID. For the most part these aren't stored as meaningful strings in the information you get back...they are just numeric values. And they're all in the first 16 bytes.

I'll decode the beginning according to the spec you cite.


This should be the literal string "00h FFh FFh FFh FFh FFh FFh 00h", which serves as a sanity check that we're looking at a valid EDID block. Your data starts off with exactly what we expect:

00 FF FF FF FF FF FF 00

These IDs are assigned by Microsoft, and are three-letter codes. Oh sure, they could have "wasted" three whole bytes in ASCII for this. But that would have been too sensible. So they frittered away eight bytes on an extremely "non-magic" number for the header, and invented an "ingenious" way to encode those three letters into the sixteen bits held by two bytes. How'd they pull it off?

        +--------+--------+
        | Byte 8 | Byte 9 |
--------+--------+--------+
Bit #    76543210 76543210
-----------------=---------
Meaning  0αααααββ βββγγγγγ

So the highest-order bit of Byte 8 is always zero, and the remaining 15 bits are divided into three groups of 5 bits (which I've called α, β, and γ). Each is interpreted as a letter, where "00001=A"; "00010=B"; ... "11010=Z".

You've got:

10 AC

And hexadecimal 10AC expressed as 16 binary bits is 0001000010101100 . So let's bring that table back again:

        +--------+--------+
        | Byte 8 | Byte 9 |
--------+--------+--------+
Bit #    76543210 76543210
-----------------=---------
Meaning  0αααααββ βββγγγγγ
-----------------=---------
Yours    00010000 10101100

So α = 00100 (decimal 4), β = 00101 (decimal 5), γ = 01100 (decimal 12). Using those decimal numbers as indexes into the English alphabet we get DEL. By this arcane sorcery we have determined that your monitor is most likely made by Dell. :)

This is a two-byte number, assigned by the manufacturer, stored as "LSB first". This is to say that the first byte is the least significant place value. You have:

4C 40

Which we need to interpret as the hexadecimal number 404C .

This is a 32-bit value assigned by the manufacturer which has no requirement for the format. It is "usually stored as LSB first", but doesn't have to be.

53 43 34 42

You can interpret that as 0x53433442 , or 0x42344353 , or whatever...so long as you're consistent in comparing one value against another.


So now you see it's just three letters and some numbers. Once you get the bytes into a buffer there are a lot of ways to extract the information. @freerider provided some information on that, I'll just throw in a bit more.

The EDID standard says that what you get back as a description is 128 bytes. That is the case with the registry key here, and you can probably assume that if there are not exactly 128 bytes it is corrupt. So using the code provided by @freerider, there'd be no need to pass in anything larger than that...you could technically go down to just 16 if that's the only part of the EDID you're interested in:

#define EDID_BUFFER_SIZE 128
// in idiomatic C++ it's better to say:
//     const size_t edidBufferSize = 128;

BYTE edidBuffer[EDID_BUFFER_SIZE];
DWORD nLength = GetLocalMachineProfileBuffer( Buffer, EDID_BUFFER_SIZE );
if (nLength != EDID_BUFFER_SIZE) {
    // handle error case, not a valid EDID block
} else {
    // valid EDID block, do extraction:
    // * manufacturer ID
    // * product ID
    // * serial number
}

(Note: I prefer to avoid using the sizeof on arrays like @freerider's sizeof( Buffer ) above. While it will technically work in this case, it doesn't return the number of elements in the array...rather the number of bytes the array occupies in memory . In this case the elements happen to actually be bytes, so it will work...but you quickly run into problems, like when you pass an array to another function by pointer and suddenly it starts reporting its size as the size of a pointer...)

Beyond that, your question of how to extract structural data out of a buffer of bytes is a very general one, and is so foundational to C-style programming that if you don't know where to start on it then you should probably work through simpler programs. Getting the three five bit segments out of the manufacturer name involves things like bit shifting, bit masking, or bit fields. Going through the array deals with addresses and how to index arrays and things like that.

The closest parallel question I could find offhand right now is this:

extract IP from a buffer of bytes

Lots of ways to do it, but an interesting one is that you can define the layout of a structure in memory and then tell the program "hey, this block of memory I found is laid out just like the structure I defined. So let me extract information from it as simply as if I'd defined the object in my program"...

But then you have to be sensitive to issues like data structure alignment. That's because the way your compiler will naturally put objects into memory doesn't necessarily match what you think it would do:

http://en.wikipedia.org/wiki/Data_structure_alignment

With the information above you should at least be able to make a shot at reading some tutorials and seeing what works. If you can't figure out one part of the problem then break that little part out as its own question, and show what you tried and why it didn't work...

This previous question explains how to get EDID with C/C++/C#. It's not through the registry, but as long it works...

Win32 code to get EDID in Windows XP/7

If you want to still read the registry, use RegQueryValueEx and friends.

   DWORD GetLocalMachineProfileBuffer(BYTE* pBuffer, DWORD nMaxLength )
    {
        CString szSubKey = "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\DISPLAY\DEL404C{Some Unique ID}\Device Parameters\EDID";

        DWORD   rc; 
        DWORD   dwType; 
        HKEY    hOpenedKey;

        if( ERROR_SUCCESS == RegOpenKeyEx (
                HKEY_LOCAL_MACHINE, // handle of open key 
                szSubKey,               // address of name of subkey to open 
                0,                  // reserved 
                KEY_READ,       // security access mask 
                &hOpenedKey            // address of handle of open key 
                ) )
        {
            rc = RegQueryValueEx( 
                hOpenedKey, 
                (const char*)szValueName, 
                0, 
                &dwType, 
                (LPBYTE)pBuffer, 
                &nMaxLength ); 
            if( rc != ERROR_SUCCESS ) 
            { 
                return (DWORD)-1;
            } 
            else 
            { 
                ASSERT( dwType == REG_BINARY ); 
            } 

            RegCloseKey( hOpenedKey );
            return nMaxLength; 
        }
        else
        {
            return (DWORD)-1;
        }   
    }

call it like this:

BYTE Buffer[20000];
DWORD nLength = GetLocalMachineProfileBuffer( Buffer, sizeof( Buffer ) );

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM