如何从注册表中读取 REG_BINARY 值关联值?

Posted

技术标签:

【中文标题】如何从注册表中读取 REG_BINARY 值关联值?【英文标题】:How can i read a REG_BINARY values associated value from registry? 【发布时间】:2011-09-06 08:53:57 【问题描述】:

在注册表中有一个(或多个)键,具体取决于您有多少监视器HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\DISPLAY\DEL404C\Some Unique ID\Device Parameters\EDID,即REG_BINARY key。就我而言,这是:

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

此 reg_binary 值包含有关已连接监视器的信息(例如序列号和类型)。我只需要这两个值。我的问题是如何使用 C 或 C++ 读取这些值?

我有一个可以执行此操作的 VB 脚本: '你可以判断位置是否包含序列号如果它以&H00 00 00 ff开头 strSerFind=Chr(&H00) & Chr(&H00) & Chr(&H00) & Chr(&HfF)

'或者型号描述如果以&H00 00 00 fc开头

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

此链接还包含有关 EDID 的信息:http://en.wikipedia.org/wiki/Extended_display_identification_data

有人可以帮助我吗,我如何在 C 中做到这一点?我只能找到 VB 脚本示例,但不幸的是我不理解它们,这对我来说也很重要。

【问题讨论】:

相关:***.com/questions/4831471/… 【参考方案1】:

您提到想要“序列号”和“类型”。没有“类型”,但有制造商 ID 和产品 ID。在大多数情况下,这些不会作为有意义的字符串存储在您返回的信息中……它们只是数值。它们都在前 16 个字节中。

我会根据你引用的规范解码开头。


字节 0,1,2,3,4,5,6,7 - 标头信息

这应该是文字字符串“00h FFh FFh FFh FFh FFh FFh 00h”,作为我们正在查看有效 EDID 块的完整性检查。您的数据完全符合我们的预期:

00 FF FF FF FF FF FF 00

字节 8 和 9 - 制造商 ID。

这些 ID 由 Microsoft 分配,并且是三个字母的代码。哦,当然,他们可能为此“浪费”了 ASCII 中的三个完整字节。但这未免太明智了。所以他们在一个非常“非魔法”的数字上浪费了八个字节作为标题,并发明了一种“巧妙”的方法来将这三个字母编码为两个字节所包含的十六位。他们是怎么做到的?

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

所以 Byte 8 的最高位始终为零,剩下的 15 位被分成三组,每组 5 位(我称之为 α、β 和 γ)。每个都被解释为一个字母,其中“00001=A”; “00010=B”; ...“11010=Z”。

你有:

10 AC

而以 16 位二进制表示的十六进制 10AC0001000010101100。所以让我们把那张桌子带回来:

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

所以α = 00100(十进制 4)、β = 00101(十进制 5)、γ = 01100(十进制 12)。使用这些十进制数字作为英文字母表的索引,我们得到 D-E-L。通过这种神秘的魔法,我们确定您的显示器很可能是戴尔制造的。 :)

字节 10 和 11 - 产品 ID 代码

这是一个由制造商分配的两字节数字,存储为“LSB first”。这就是说第一个字节是最低有效位值。你有:

4C 40

我们需要将其解释为十六进制数404C

字节 12,13,14,15 - 序列号。

这是制造商分配的 32 位值,对格式没有要求。它“通常先存储为 LSB”,但并非必须如此。

53 43 34 42

您可以将其解释为0x53433442,或0x42344353,或其他任何...只要您在比较一个值与另一个值时保持一致。


所以现在你看到它只是三个字母和一些数字。一旦将字节放入缓冲区,就有很多方法可以提取信息。 @freerider 提供了一些相关信息,我会再补充一点。

EDID 标准规定,作为描述返回的内容是 128 字节。此处的注册表项就是这种情况,您可以假设如果不完全是 128 个字节,则它已损坏。因此,使用@freerider 提供的代码,不需要传递比这更大的任何内容……如果这是您感兴趣的 EDID 的唯一部分,从技术上讲,您可以降至 16:

#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

(注意:我更喜欢避免在数组上使用sizeof,比如上面的@freerider 的sizeof( Buffer )。虽然在这种情况下它在技术上可以工作,但它不会返回数组中元素的数量。 .. 而是the number of bytes the array occupies in memory. 在这种情况下,元素恰好是字节,所以它会工作......但是你很快就会遇到问题,比如当你通过指针将数组传递给另一个函数时,它突然开始报告它的大小作为指针的大小...)

除此之外,您关于如何从字节缓冲区中提取结构化数据的问题是一个非常普遍的问题,并且对于 C 风格编程是如此基础,如果您不知道从哪里开始,那么您应该可能通过更简单的程序工作。从制造商名称中获取三个五位段涉及位移、位掩码或位字段等内容。遍历数组处理地址以及如何索引数组和类似的东西。

我现在能临时找到的最接近的平行问题是:

extract IP from a buffer of bytes

有很多方法可以做到这一点,但有趣的是,您可以在内存中定义结构的布局,然后告诉程序“嘿,我找到的这块内存的布局就像我定义的结构一样。因此,让我从中提取信息,就好像我在程序中定义了对象一样”...

但是您必须对数据结构对齐等问题保持敏感。那是因为你的编译器自然地将对象放入内存的方式并不一定符合你的想法:

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

通过以上信息,您至少应该能够尝试阅读一些教程并了解哪些方法有效。如果您无法弄清楚问题的一部分,那么将那一小部分作为自己的问题分解出来,并展示您尝试了什么以及为什么它不起作用......

【讨论】:

非常感谢您的解释,通俗易懂。【参考方案2】:

上一个问题解释了如何使用 C/C++/C# 获取 EDID。它不是通过注册表,但只要它有效......

Win32 code to get EDID in Windows XP/7

如果您还想阅读注册表,请使用RegQueryValueEx 和朋友。

【讨论】:

很多时候,您在 VB 示例中找到面向注册表的解决方案的原因是因为它们手头有注册表功能,但无法直接访问解释注册表的 API。这可能是“当你只有一把锤子时,一切看起来都像钉子”。避免注册表并使用系统提供的更抽象的 API(通常)是更好的方法。 @Hostile Fork:您可能是对的,但首先 WMI 非常不可靠,其次 XP 不支持 WmiMonitorID 类。所以看来我别无选择,只有注册表:( @kampi:冒险进入这个“有点前沿”的 API 领域总是很痛苦。为了制作最流畅的应用程序,人们经常使用一种混合方法......如果它们可用,您可以通过动态链接它们(GetProcAddress 或其他)来使用这些功能,并且只有在平台没有的情况下才回退到可能更粗略的方法提供它。这是更多的工作,但可以通过利用平台上最兼容和最受支持的情况来提供竞争优势,而不是总是最小的公分母...... @Hostile Fork:你可能是对的......再次:) 看来你比我有更多的编程经验。很可能我会按照你的建议去做,但你能不能帮我使该注册表读取器功能起作用?我不知道,如何确定序列号和型号类型的开始位置和结束位置。【参考方案3】:
   DWORD GetLocalMachineProfileBuffer(BYTE* pBuffer, DWORD nMaxLength )
    
        CString szSubKey = "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\DISPLAY\DEL404CSome 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;
           
    

这样称呼它:

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

【讨论】:

谢谢,这很好用。现在我只有一个问题。我可以打印数据,并且可以看到序列号和型号类型,但是在 VB 脚本示例中,定义了一个位置,序列号和型号类型开始的位置。我需要字符串中的序列号和型号类型。我怎样才能将它们复制到字符串中?我的意思是我怎么知道数据是否是序列号?希望你明白我的意思,我的英语有点生硬:) 用for循环搜索Buffer中的特殊字符。 对不起,这可能是一个愚蠢的问题,但这是我不知道的,我如何搜索二进制数据???

以上是关于如何从注册表中读取 REG_BINARY 值关联值?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C++ 中使用 RegQueryValueEx(..) 从注册表中读取 REG_MULTI_SZ 类型值

注册表新建 -------DWORD(32-位)

从注册表中读取值

正确地将字符串添加到 Windows 注册表中的 REG_BINARY 类型

从注册表读取字符串值,但结果包含一些奇怪的字符

注册表操作(VC_Win32)