帮自己一个忙,并且:
放弃这种过时的、容易出错、难以编写、读取和维护的 C 风格的原始字符串指针、原始数组等,并使用 C++(可能带有一些方便的 C++11 特性)和STL容器和类(像std::map
, std::wstring
, ...)。
放弃过时的TCHAR
模型,只需编写Unicode Win32 代码。
以下代码使用 C++、STL 和RAII以及 Win32 API 的接口。
错误使用 C++ 异常表示。
字符串存储在健壮的std::wstring
类实例中。
(key, value) 对存储在方便的std::map
STL 容器中。
代码已注释,因此请按照代码中的注释获取更多详细信息。
为了测试,我在注册表上创建了一些测试数据(如下图所示):
然后使用 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;
}
}
/////////////////////////////////////////////////////////////////////////////