列举Windows注册表项中的所有子键和值

Enumerating all subkeys and values in a Windows registry key

本文关键字:键和值 Windows 注册表 列举      更新时间:2023-10-16

我正在尝试编写一个Windows应用程序,该应用程序可以让我返回给定某个键的所有子键和值。我编写的代码似乎可以在给定键内提供子键,但不能正确枚举值;它成功地枚举了不带值的子键,并以选项卡树的形式返回结果。但是,在枚举值时,程序会为每个存在的值返回一个随机值(每次都是相同的随机值),然后崩溃并出现调试错误。

它的预期输出基本上是:

(1) KEY
    (1) SUBKEY
    (1) SUBKEYWITHINSUBKEY
        Code: value1data
        Code: value2data
        Code: value3data
(2) SUBKEY
    (1) SUBKEYWITHINSUBKEY
(3) SUBKEY

…等等。

我得到的输出是这样的:
(1) KEY
(1) SUBKEY
    (1) SUBKEYWITHINSUBKEY
        Code: someValue
        Code: someValue
        Code: someValue

(…然后崩溃)

之后会出现以下错误:
"调试错误!"运行时检查失败#2 -变量'valNameLen'周围的堆栈已损坏。"

代码现在有点乱(我是一个Windows API新手),但是如果有人能告诉我我做错了什么,或者以任何他们认为合适的方式批评我的编码风格,那将是伟大的。

谢谢!

- r

/*
Windows Registry Subkey Enumeration Example
Based on example found at code-blue.org
*/
#include <windows.h>
#include <stdio.h>
void EnumerateValues(HKEY hKey, DWORD numValues)
{
 DWORD dwIndex = 0;
    LPSTR valueName = new CHAR[64];
 DWORD valNameLen;
 DWORD dataType;
 DWORD data;
 DWORD dataSize;
    for (int i = 0; i < numValues; i++)
 {
  RegEnumValue(hKey,
     dwIndex,
     valueName,
     &valNameLen,
     NULL,
     &dataType,
     (BYTE*)&data,
     &dataSize);
  dwIndex++;
        printf("Code: 0x%08Xn", data);
 }
}

void EnumerateSubKeys(HKEY RootKey, char* subKey, unsigned int tabs = 0) 
{
 HKEY hKey;
    DWORD cSubKeys;        //Used to store the number of Subkeys
    DWORD maxSubkeyLen;    //Longest Subkey name length
    DWORD cValues;        //Used to store the number of Subkeys
    DWORD maxValueLen;    //Longest Subkey name length
    DWORD retCode;        //Return values of calls
 RegOpenKeyEx(RootKey, subKey, 0, KEY_ALL_ACCESS, &hKey);
    RegQueryInfoKey(hKey,            // key handle
                    NULL,            // buffer for class name
                    NULL,            // size of class string
                    NULL,            // reserved
                    &cSubKeys,        // number of subkeys
                    &maxSubkeyLen,    // longest subkey length
                    NULL,            // longest class string 
                    &cValues,        // number of values for this key 
                    &maxValueLen,    // longest value name 
                    NULL,            // longest value data 
                    NULL,            // security descriptor 
                    NULL);            // last write time
    if(cSubKeys>0)
 {
        char currentSubkey[MAX_PATH];
        for(int i=0;i < cSubKeys;i++){
   DWORD currentSubLen=MAX_PATH;
            retCode=RegEnumKeyEx(hKey,    // Handle to an open/predefined key
            i,                // Index of the subkey to retrieve.
            currentSubkey,            // buffer to receives the name of the subkey
            &currentSubLen,            // size of that buffer
            NULL,                // Reserved
            NULL,                // buffer for class string 
            NULL,                // size of that buffer
            NULL);                // last write time
            if(retCode==ERROR_SUCCESS)
   {
                for (int i = 0; i < tabs; i++)
                    printf("t");
                printf("(%d) %sn", i+1, currentSubkey);
                char* subKeyPath = new char[currentSubLen + strlen(subKey)];
                sprintf(subKeyPath, "%s\%s", subKey, currentSubkey);
    EnumerateSubKeys(RootKey, subKeyPath, (tabs + 1));
   }
  }
 }
    else
 {
  EnumerateValues(hKey, cValues);
 }
 RegCloseKey(hKey); 
}

int main()
{
    EnumerateSubKeys(HKEY_CURRENT_USER,"SOFTWARE\MyKeyToSearchIn");
    return 0;
}

以这种方式枚举键是多余的。这只会浪费系统资源、内存、调用堆栈,并给注册表子系统带来压力。除非必要,否则不要这样做。

你打算在你的应用程序中有"搜索注册表"吗?如果是,则仅在用户要求时枚举。或者,如果您正在开发"注册表查看器/编辑器",请仅在需要时展开和打开子键。

如果你绝对需要检索和存储所有的键/值,你可以使用多个线程来枚举键。线程的数量最初将是hkey -major-key,然后您可以有更多的线程,这取决于子键的数量和您在枚举键时执行的运行时启发式。

递归可能是也可能不是子键"递归枚举"的好方法-你必须保持递归实现的参数数量最少-将参数放入一个struct或将它们放在类中。您可能也喜欢使用std::stack进行相同的操作。

似乎您正在调用RegEnumValue()而没有将lpcchValueName参数设置为适当的值。该参数既是[in]参数,也是[out]参数。试试这个:

for (int i = 0; i < numValues; i++)
 {
  DWORD valNameLen = 64; //added this line to match valueName buffer size
  RegEnumValue(hKey,
     dwIndex,
     valueName,
     &valNameLen,
     NULL,
     &dataType,
     (BYTE*)&data,
     &dataSize);

RegEnumValue()的文档:http://msdn.microsoft.com/en-us/library/ms724865(v=vs.85).aspx

我在一个具有少量子键的键上尝试了您的代码。

在函数EnumerateValues中,调用RegEnumValue一次后,numValues的值被一些随机的垃圾填充。解决方案是将RegEnumValue的参数更改为

    RegEnumValueA(hKey,
               dwIndex,
               valueName,
               &valNameLen,
               nullptr,
               nullptr,
               nullptr,
               nullptr);

这是最后的函数,

void EnumerateValues(HKEY hKey, DWORD numValues)
{
    DWORD dwIndex = 0;
    LPSTR valueName = new CHAR[64];
    DWORD valNameLen;
    DWORD numback = numValues;
    for (int i = 0; i < numValues; i++)
    {
        // printf("%lu, %dn", numValues, i);
        RegEnumValueA(hKey,
                      dwIndex,
                      valueName,
                      &valNameLen,
                      nullptr,
                      nullptr,
                      nullptr,
                      nullptr);
        dwIndex++;
        if (i > numback)
        {
            RegCloseKey(hKey);
            printf("Inf loop exiting...n");
            exit(-1);
        }
        // printf("Code: 0x%08X, %lu, %dn", data, numValues, i);
        // printf("Code: %lu, %dn", numValues, i);
    }
    RegCloseKey(hKey);
}

我修复了你的代码并将其发布在github上。

主要问题是你需要创建自己的缓冲区并将数据指向它,并且还需要在调用RegEnumValue之前设置dataSize。除此之外,如果您以所有权限调用RegOpenKeyEx,那么如果没有用户提升(admin),该程序将无法运行,因此我将它们更改为只读。