内存分配(key,value)对结构,并从注册表中读取

时间:2013-04-30 13:49:51

标签: c++ visual-studio-2010 winapi registry

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

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

2 个答案:

答案 0 :(得分:3)

帮自己一个忙,并且:

  1. 删除这个过时的容易出错的难以写入的读取和维护C样式的原始字符串指针,原始数组等,以及使用C ++ (可能还有一些方便的C ++ 11功能)和 STL 容器和类(如std::mapstd::wstring,...)。

  2. 删除过时的TCHAR模型,然后编写 Unicode Win32代码。

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

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

    为了测试,我在注册表上创建了一些测试数据(如下面的屏幕截图所示): Some registry test data

    然后使用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;
        }    
    }
    
    
    /////////////////////////////////////////////////////////////////////////////
    

答案 1 :(得分: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)之后释放分配的内存