3

我想制作一个程序,它从 Windows 注册表中收集一些信息,然后将其保存到文件中。但是,我在收集信息时遇到了问题。我想将数组存储在结构中。我的问题是在初始化结构后指定数组的大小。

typedef struct RESULTSKEY{
char *ResultsKeys;
char *ResultsValues;
} RESULTSKEYS;


RESULTSKEYS RES_OS_Keys[] =
{
 { _T("ProductName"),_T(" ")},
 { _T("CSDVersion"),_T(" ") },
 { _T("BuildLab"),_T(" ") },
 { _T("SystemRoot"),_T(" ") },
};

然后使用以下功能,魔法应该发生。

for (l=0; l< _countof(RES_OS_Keys);l++)
{
    variable = (char*)ReadRegistry((LPTSTR) clave,(LPTSTR)RES_OS_Keys[l].ResultsKeys);
    RES_OS_Keys[l].ResultsValues = variable;
} 

但当然,有一个问题:RES_OS_Keys[l].ResultsValues具有相同的值:

RES_OS_Keys[0] 
  { ResultsKeys=0x00420894"ProductName"  Resultsvalues=0x0012f488 "C:\WINDOWS"}

RES_OS_Keys[1] 
  { ResultsKeys=0x00420880"CSDVersion"  Resultsvalues=0x0012f488 "C:\WINDOWS"}

RES_OS_Keys[2] 
  { ResultsKeys=0x00420874"ProductName"  Resultsvalues=0x0012f488 "C:\WINDOWS"}

RES_OS_Keys[3] 
  { ResultsKeys=0x00420864"ProductName"  Resultsvalues=0x0012f488 "C:\WINDOWS"}

我注意到,它写在同一个内存段上。这就是我想到这个想法的原因,问题在于结构中变量的内存分配。我一直在寻找方法,但是我很困惑。所以,如果有人能给我建议,我将不胜感激。

这是ReadRegistry功能:

unsigned char *ReadRegistry(LPTSTR clave, LPTSTR valor) 
{   
    unsigned char* buffer =new unsigned char[1024]; 
    unsigned char infoValue [1024];
    unsigned char infocadena [1024];
    HKEY hKey;
    LONG lStatus;
    DWORD dwType=REG_SZ;
    DWORD dwSize=1023;
    int i=0;
    int j=0;
    lStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE,clave,0,KEY_READ|KEY_WOW64_64KEY,&hKey);
    if (lStatus == ERROR_SUCCESS)
    {
         lStatus = RegQueryValueEx(hKey,(LPCTSTR )valor, 0,&dwType, (LPBYTE)&infoValue, &dwSize);
         if (lStatus == ERROR_SUCCESS)
         {  
            for(i=0;infoValue[i]!=0 && infoValue[i]!=204;i++)
                infocadena[i]=(char)infoValue[i];

            infocadena [i]='\0';
            buffer = infocadena;
            RegCloseKey(hKey);
            return buffer;
            delete[] buffer;
         }  
    }
    RegCloseKey(hKey);
    cout << "error by reading registry";
    delete[] buffer;
    return 0;
}
4

2 回答 2

3

帮自己一个忙,并且:

  1. 放弃这种过时的、容易出错、难以编写、读取和维护的 C 风格的原始字符串指针、原始数组等,并使用 C++(可能带有一些方便的 C++11 特性)和STL容器和类(像std::map, std::wstring, ...)。

  2. 放弃过时的TCHAR模型,只需编写Unicode Win32 代码。

以下代码使用 C++、STL 和RAII以及 Win32 API 的接口。
错误使用 C++ 异常表示。
字符串存储在健壮的std::wstring类实例中。
(key, value) 对存储在方便的std::mapSTL 容器中。

代码已注释,因此请按照代码中的注释获取更多详细信息。

为了测试,我在注册表上创建了一些测试数据(如下图所示): 一些注册表测试数据

然后使用 VC10 (VS2010 SP1) 从命令行编译代码:

C:\Temp\CppTests>cl /EHsc /W4 /nologo /MTd TestRegistry.cpp  
TestRegistry.cpp

并运行可执行文件,获得以下输出:

C:\Temp\CppTests>TestRegistry.exe  
BuildLab: Cool LAB  
ProductName: My Cool Product.  
Version: 1.2.3.4A  

可编译代码如下:

/////////////////////////////////////////////////////////////////////////////
//
// Test program to read some strings from the registry.
//
// Uses C++ techniques like RAII and STL containers.
//
/////////////////////////////////////////////////////////////////////////////


// Build in Unicode mode 
// (it's the default inside VS IDE, since VS2005).
#define UNICODE
#define _UNICODE

//
// Headers:
//
#include <exception>    // for std::exception
#include <iostream>     // for console output
#include <map>          // for std::map
#include <stdexcept>    // for std::runtime_error
#include <string>       // for std::wstring
#include <Windows.h>    // Win32 Platform SDK


// Link with this for registry APIs.
#pragma comment(lib, "advapi32.lib")


//
// Represents Win32 API errors.
//
class Win32Error 
    : public std::runtime_error
{
public:
    // Init with message and error code
    Win32Error(const char* message, DWORD error)
        : std::runtime_error(message),
          m_error(error)
    {
    }

    DWORD ErrorCode() const
    {
        return m_error;
    }

private:
    DWORD m_error;
};

// Throws Win32Error exception based on last error code.
inline void ThrowLastWin32(const char* message)
{
    const DWORD lastError = ::GetLastError();
    throw Win32Error(message, lastError);
}


//
// RAII wrapper to Win32 registry key.
//
class RegKey
{
public:

    // Tries opening the specified key.
    // Throws a Win32Error exception on failure.
    RegKey(
        HKEY hKeyParent, 
        const std::wstring& subKey,
        REGSAM desideredAccess
        )
    {
        LONG result = ::RegOpenKeyEx(
            hKeyParent,
            subKey.c_str(),
            0,
            desideredAccess,
            &m_hKey
        );
        if (result != ERROR_SUCCESS)
        {
            ThrowLastWin32("Can't open registry key.");
        }
    }

    // Closes the key.
    ~RegKey()
    {
        ::RegCloseKey(m_hKey);
    }

    // Gets the wrapped key handle.
    HKEY Get() const
    {
        return m_hKey;
    }

private:
    HKEY m_hKey; // raw key resource wrapper in this RAII class
};


//
// Reads a string from the registry.
// (Throws exceptions like Win32Error on errors.)
//
std::wstring ReadRegistryString(
    HKEY hKeyParent,
    const std::wstring& keyName, 
    const std::wstring& value)
{
    // Try opening the specified key
    RegKey key( hKeyParent, keyName, KEY_READ|KEY_WOW64_64KEY);

    // First call to ::RegQueryValueEx() to get destination buffer size
    DWORD dataSize = 0;
    LONG result = ::RegQueryValueEx(
        key.Get(),      // handle to open registry key
        value.c_str(),  // the name of the registry value
        nullptr,        // reserved
        nullptr,        // no need to know the type of value
        nullptr,        // data is not required in this step
        &dataSize       // get data size, in bytes      
    );
    if (result != ERROR_SUCCESS)
        ThrowLastWin32("ReadRegistryString - Can't get buffer size.");

    // Create a string with proper size to store the value
    // read from registry.
    // Consider that sizeof(wchar_t) == 2 bytes.
    std::wstring data( dataSize/2, 'x' );

    // Second call to ::RegQueryValueEx() to get the actual string
    DWORD type;
    result = ::RegQueryValueEx(
        key.Get(),      // handle to open registry key
        value.c_str(),  // the name of the registry value
        nullptr,        // reserved
        &type,          // the type of value
        reinterpret_cast<BYTE*>(&data[0]), // string buffer
        &dataSize       // data size, in bytes      
    );
    if (result != ERROR_SUCCESS)
        ThrowLastWin32("ReadRegistryString - Can't get value data.");

    // Check that we are reading a string
    if (type != REG_SZ)
        throw Win32Error("ReadRegistryString - Type is not string.",
            ERROR_INVALID_DATA);

    // To avoid duouble-NUL termination,
    // remove the last NUL in the string, if present.
    // (In fact, there can be a NUL copied by ::RegQueryValueEx,
    // and there is the NUL of std::wstring).
    if (data[data.length()-1] == L'\0')
        data.resize(data.length()-1);

    return data;
}


//
// Test function: read some key/value pairs from the registry.
//
std::map<std::wstring, std::wstring> ReadDesideredKeyValuePairs()
{
    // Keys to read value for    
    const wchar_t* keys[] = {
        L"ProductName",
        L"Version",
        L"BuildLab"
    };

    // Will store (key, value) pairs
    std::map<std::wstring, std::wstring> result;

    // Read key/value pairs from the registry
    for (int i = 0; i < _countof(keys); i++)
    {    
        result[keys[i]] = ReadRegistryString(
                HKEY_CURRENT_USER, 
                L"C64Test", 
                keys[i]
        );
    }

    return result;
}


//
// Console app entry point
//
int main()
{
    static const int kExitOk = 0;
    static const int kExitError = 1;

    try
    {
        // Call test function
        std::map<std::wstring, std::wstring> result = 
            ReadDesideredKeyValuePairs(); 

        // Print result to console
        for (auto it = result.begin(); it != result.end(); ++it)
            std::wcout << it->first << ": " << it->second << std::endl;

        // All right
        return kExitOk;
    }
    catch(const std::exception& e)
    {
        // Print error message
        std::wcerr << "\n*** ERROR: " << e.what() << std::endl;

        // Exit with failure code
        return kExitError;
    }    
}


/////////////////////////////////////////////////////////////////////////////
于 2013-04-30T16:27:17.670 回答
1

ResultsValues变量是一个指针,因此您需要先为其分配内存,然后才能将“ReadRegistry”的结果存储到其中。

如果“ReadRegistry”是您编写的函数,返回值的类型是什么?如果它是一个指针,您可能还会在函数中遇到内存分配问题,您可能希望使用 CString 作为返回值或将结果指针作为参数。

注意:之后您还需要释放分配的内存!

编辑:评论已编辑的问题:

a) 制作函数void ReadRegistry(LPTSTR clave, LPTSTR valor, LPTSTR infocadena)

b) 删除函数中的变量声明infocadenabuffer所有引用buffer

c)RES_OS_Keys[l].ResultsValues在调用逻辑中分配内存,然后调用函数ReadRegistry((LPTSTR) clave,(LPTSTR)RES_OS_Keys[l].ResultsKeys, RES_OS_Keys[l].ResultsValues);

d) 之后释放分配的内存

于 2013-04-30T14:00:53.690 回答