2011-09-06 332 views
2

在注册表中有一个(或多个)密钥,具体取决于您有多少个监视器HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\DISPLAY\DEL404C\{Some Unique ID}\Device Parameters\EDID,这是一个REG_BINARY key。在我的情况下,这是:我如何从注册表读取REG_BINARY值相关值?

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

可能有人^ h elp我,我怎么能在C做这个?我只能找到VB脚本的例子,但不幸的是我不明白它们,这对我来说也是非常重要的。

+0

相关:http://stackoverflow.com/questions/4831471/windows-get-number-of-monitors-including-disabled-ones – HostileFork

回答

1
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; 
     } 
    } 

调用它像这样:

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

谢谢,这个作品非常好。现在我只有一个问题。我可以打印数据,我可以看到序列号和模型类型,但在VB脚本示例中,定义了一个位置,序列号和模型类型开始。我需要字符串中的序列号和模型类型。我怎样才能把它们复制成字符串?我的意思是我如何知道,如果数据是序列号?希望你明白我的意思,我的英语有点冷淡:) – kampi

+0

在for循环中搜索Buffer中的特殊字符。 – Lucian

+0

对不起,这可能是一个愚蠢的问题,但这是我不知道,我如何搜索二进制数据? – kampi

2

上一个问题解释了如何使用C/C++/C#获取EDID。如果你想仍然读取注册表,使用RegQueryValueEx和朋友这并不是通过注册表,但只要它的作品...

Win32 code to get EDID in Windows XP/7

+0

很多时候您在VB示例中找到注册表导向解决方案的原因是因为他们手边有注册表功能,但没有直接访问解释注册表的API。它可能是“当你拥有的只是一把锤子,一切看起来像钉子”的情况。避免使用注册表并使用系统提供的更多抽象API是(通常)更好的方法。 – HostileFork

+0

@Hostile Fork:你可能是对的,但是第一个WMI非常不可靠,并且在XP下不支持调用WmiMonitorID类。因此,似乎我没有其他选择只是注册表:( – kampi

+0

@ kampi:冒险进入这个“有点流血的边缘”API领域总是很痛苦。为了制作最清晰的应用程序,人们经常使用混合方法......在哪里使用如果可用的函数通过动态链接它们('GetProcAddress'或其他),并且只有在平台没有提供它的情况下回退到一个可能更加粗略的方法。这是更多的工作,但可以通过利用在一个平台上最兼容和最受支持的情况,而不是总是最低的共同分母... – HostileFork

5

你提到希望的 “序列号” 和 “类型”。没有“类型”,但有制造商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。

这些标识由Microsoft分配,且为三字母代码。哦,当然,他们可能已经浪费了三个ASCII字符的整个字节。但这太明智了。所以他们在头文件上写了一个非常“非魔术”数字的8个字节,并发明了一种“巧妙”的方法将这三个字母编码为由两个字节保存的16位。他们怎么把它关掉?

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

所以字节8的最高位总是零,并且剩余的15位被分成三个组,每组5个比特(我已经称为α,β和γ)的。每个被解释为一个字母,其中“00001 = A”; “00010 = B”; ...“11010 = Z”。

你已经有了:

10 AC 

和十六进制10AC表示为16个二进制位是0001000010101100。因此,让我们把该表再次:

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

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

字节10和11 - 产品ID代码

这是一个两字节数,由制造商分配,存储为 “LSB第一”。这就是说第一个字节是最不重要的地方值。你有:

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 
} 

(注:我宁愿避免像上面@ freerider的sizeof(Buffer)阵列使用sizeof虽然在技术上会在这种情况下工作,它不返回元素的数量阵列中......而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

看上面的信息至少应该能够让射击在阅读一些教程和看看有什么作用。如果你无法弄清楚问题的一部分,那么就把这一小部分当作自己的问题来解决,并且展示你所尝试的以及为什么它不起作用......

+0

非常感谢,为了您的解释,很容易理解。 – kampi