mapnik/boost/property_tree/registry_parser.hpp
2006-10-03 09:06:13 +00:00

520 lines
17 KiB
C++

// ----------------------------------------------------------------------------
// Copyright (C) 2002-2005 Marcin Kalicinski
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// For more information, see www.boost.org
// ----------------------------------------------------------------------------
#ifndef BOOST_PROPERTY_TREE_REGISTRY_PARSER_HPP_INCLUDED
#define BOOST_PROPERTY_TREE_REGISTRY_PARSER_HPP_INCLUDED
// Include minimal version of windows.h if not included yet
#ifndef _WINDOWS_
#ifndef NOMINMAX
#define NOMINMAX
#endif
#define STRICT
#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN
#define NOGDICAPMASKS
#define NOVIRTUALKEYCODES
#define NOWINMESSAGES
#define NOWINSTYLES
#define NOSYSMETRICS
#define NOMENUS
#define NOICONS
#define NOKEYSTATES
#define NOSYSCOMMANDS
#define NORASTEROPS
#define NOSHOWWINDOW
#define OEMRESOURCE
#define NOATOM
#define NOCLIPBOARD
#define NOCOLOR
#define NOCTLMGR
#define NODRAWTEXT
#define NOGDI
#define NOKERNEL
#define NOUSER
#define NONLS
#define NOMB
#define NOMEMMGR
#define NOMETAFILE
#define NOMSG
#define NOOPENFILE
#define NOSCROLL
#define NOSERVICE
#define NOSOUND
#define NOTEXTMETRIC
#define NOWH
#define NOWINOFFSETS
#define NOCOMM
#define NOKANJI
#define NOHELP
#define NOPROFILER
#define NODEFERWINDOWPOS
#define NOMCX
#include <windows.h>
#endif
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/detail/ptree_utils.hpp>
#include <boost/cstdint.hpp> // for 64 bit int
#include <sstream>
#include <iomanip>
#include <string>
#include <vector>
#include <stdexcept>
namespace boost { namespace property_tree { namespace registry_parser
{
//! Registry parser error
class registry_parser_error: public ptree_error
{
public:
// Construct error
registry_parser_error(const std::string &message, DWORD windows_error):
ptree_error(format_what(message, windows_error)),
m_windows_error(windows_error)
{
}
// Get windows error
DWORD windows_error()
{
return m_windows_error;
}
private:
DWORD m_windows_error;
// Format error message to be returned by std::runtime_error::what()
std::string format_what(const std::string &message,
DWORD windows_error)
{
std::stringstream stream;
if (windows_error)
stream << message << " (windows error 0x" << std::hex << windows_error << ")";
else
stream << message;
return stream.str();
}
};
// Translate from binary buffer to string
template<class Ch>
std::basic_string<Ch> translate(DWORD type, const std::vector<BYTE> &data)
{
typedef std::basic_string<Ch> Str;
typedef std::basic_stringstream<Ch> Stream;
Str value;
switch (type)
{
// No data
case REG_NONE:
break;
// Binary data
case REG_BINARY:
if (!data.empty())
{
Stream stream;
stream << std::hex << std::setfill(Ch('0'));
for (std::vector<BYTE>::const_iterator it = data.begin(), end = data.end();
it != end; ++it)
stream << std::setw(2) << static_cast<int>(*it) << Ch(' ');
value = stream.str();
value.resize(value.size() - 1); // remove final space
}
break;
// DWORD value
case REG_DWORD:
if (!data.empty())
{
Stream stream;
stream << *reinterpret_cast<const DWORD *>(&data.front());
value = stream.str();
}
break;
// QWORD value
case REG_QWORD:
if (!data.empty())
{
Stream stream;
stream << *reinterpret_cast<const boost::uint64_t *>(&data.front());
value = stream.str();
}
break;
// Zero terminated string
case REG_SZ: case REG_EXPAND_SZ:
if (!data.empty())
value.assign(reinterpret_cast<const Ch *>(&data.front()));
break;
// Unknown data type
default:
throw registry_parser_error("unsupported data type", 0);
};
return value;
}
// Translate from string to binary buffer
template<class Ch>
std::vector<BYTE> translate(DWORD type, const std::basic_string<Ch> &s)
{
typedef std::basic_string<Ch> Str;
typedef std::basic_stringstream<Ch> Stream;
std::vector<BYTE> data;
switch (type)
{
// No data
case REG_NONE:
break;
// Binary data
case REG_BINARY:
{
int v;
Stream stream(s);
stream >> std::hex;
while (1)
{
stream >> v >> std::ws;
if (stream.fail() || stream.bad())
throw registry_parser_error("bad REG_BINARY value", 0);
data.push_back(v);
if (stream.eof())
break;
}
}
break;
// DWORD value
case REG_DWORD:
{
DWORD v;
Stream stream(s);
stream >> v >> std::ws;
if (!stream.eof() || stream.fail() || stream.bad())
throw registry_parser_error("bad REG_DWORD value", 0);
for (size_t i = 0; i < sizeof(v); ++i)
data.push_back(*(reinterpret_cast<BYTE *>(&v) + i));
}
break;
// QWORD value
case REG_QWORD:
{
boost::uint64_t v;
Stream stream(s);
stream >> v;
if (!stream.eof() || stream.fail() || stream.bad())
throw registry_parser_error("bad REG_QWORD value", 0);
for (size_t i = 0; i < sizeof(v); ++i)
data.push_back(*(reinterpret_cast<BYTE *>(&v) + i));
}
break;
// Zero terminated string
case REG_SZ: case REG_EXPAND_SZ:
{
const Ch *sz = s.c_str();
size_t len = (s.size() + 1) * sizeof(Ch);
for (size_t i = 0; i < len; ++i)
data.push_back(*(reinterpret_cast<const BYTE *>(sz) + i));
}
break;
// Unknown data type
default:
throw registry_parser_error("unsupported data type", 0);
};
return data;
}
/////////////////////////////////////////////////////////////////////////////
// Registry functions wrappers
template<class Ch>
inline LONG reg_create_key_ex(HKEY hkey, const Ch *subkey, REGSAM sam, HKEY *result);
template<>
inline LONG reg_create_key_ex<char>(HKEY hkey, const char *subkey, REGSAM sam, HKEY *result)
{
return RegCreateKeyExA(hkey, subkey, 0, NULL, REG_OPTION_NON_VOLATILE, sam, NULL, result, NULL);
}
template<>
inline LONG reg_create_key_ex<wchar_t>(HKEY hkey, const wchar_t *subkey, REGSAM sam, HKEY *result)
{
return RegCreateKeyExW(hkey, subkey, 0, NULL, REG_OPTION_NON_VOLATILE, sam, NULL, result, NULL);
}
template<class Ch>
inline LONG reg_set_value_ex(HKEY hkey, const Ch *name, DWORD type, const BYTE *data, DWORD size);
template<>
inline LONG reg_set_value_ex<char>(HKEY hkey, const char *name, DWORD type, const BYTE *data, DWORD size)
{
return RegSetValueExA(hkey, name, 0, type, data, size);
}
template<>
inline LONG reg_set_value_ex<wchar_t>(HKEY hkey, const wchar_t *name, DWORD type, const BYTE *data, DWORD size)
{
return RegSetValueExW(hkey, name, 0, type, data, size);
}
template<class Ch>
inline LONG reg_open_key_ex(HKEY hkey, const Ch *subkey, REGSAM sam, HKEY *result);
template<>
inline LONG reg_open_key_ex<char>(HKEY hkey, const char *subkey, REGSAM sam, HKEY *result)
{
return RegOpenKeyExA(hkey, subkey, 0, sam, result);
}
template<>
inline LONG reg_open_key_ex<wchar_t>(HKEY hkey, const wchar_t *subkey, REGSAM sam, HKEY *result)
{
return RegOpenKeyExW(hkey, subkey, 0, sam, result);
}
template<class Ch>
inline LONG reg_enum_key_ex(HKEY hkey, DWORD index, Ch *name, DWORD *size);
template<>
inline LONG reg_enum_key_ex<char>(HKEY hkey, DWORD index, char *name, DWORD *size)
{
FILETIME ft;
return RegEnumKeyExA(hkey, index, name, size, 0, NULL, NULL, &ft);
}
template<>
inline LONG reg_enum_key_ex<wchar_t>(HKEY hkey, DWORD index, wchar_t *name, DWORD *size)
{
FILETIME ft;
return RegEnumKeyExW(hkey, index, name, size, 0, NULL, NULL, &ft);
}
template<class Ch>
inline LONG reg_enum_value(HKEY hkey, DWORD index, Ch *name, DWORD *name_size, DWORD *type, BYTE *data, DWORD *data_size);
template<>
inline LONG reg_enum_value<char>(HKEY hkey, DWORD index, char *name, DWORD *name_size, DWORD *type, BYTE *data, DWORD *data_size)
{
return RegEnumValueA(hkey, index, name, name_size, NULL, type, data, data_size);
}
template<>
inline LONG reg_enum_value<wchar_t>(HKEY hkey, DWORD index, wchar_t *name, DWORD *name_size, DWORD *type, BYTE *data, DWORD *data_size)
{
return RegEnumValueW(hkey, index, name, name_size, NULL, type, data, data_size);
}
template<class Ch>
inline LONG reg_query_info_key(HKEY hkey, DWORD *max_subkey_len, DWORD *max_name_len, DWORD *max_value_len);
template<>
inline LONG reg_query_info_key<char>(HKEY hkey, DWORD *max_subkey_len, DWORD *max_name_len, DWORD *max_value_len)
{
return RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, max_subkey_len, NULL, NULL, max_name_len, max_value_len, NULL, NULL);
}
template<>
inline LONG reg_query_info_key<wchar_t>(HKEY hkey, DWORD *max_subkey_len, DWORD *max_name_len, DWORD *max_value_len)
{
return RegQueryInfoKeyW(hkey, NULL, NULL, NULL, NULL, max_subkey_len, NULL, NULL, max_name_len, max_value_len, NULL, NULL);
}
/////////////////////////////////////////////////////////////////////////////
// Registry key handle wrapper
template<class Ch>
class reg_key
{
public:
typedef std::basic_string<Ch> Str;
reg_key(HKEY root, const std::basic_string<Ch> &key, bool create):
hkey(0)
{
if (create)
{
LONG result = reg_create_key_ex(root, key.c_str(), KEY_WRITE, &hkey);
if (result != ERROR_SUCCESS)
throw registry_parser_error("RegCreateKeyEx failed", result);
}
else
{
LONG result = reg_open_key_ex(root, key.c_str(), KEY_READ, &hkey);
if (result != ERROR_SUCCESS)
throw registry_parser_error("RegOpenKeyEx failed", result);
}
BOOST_ASSERT(hkey);
}
~reg_key()
{
BOOST_ASSERT(hkey);
RegCloseKey(hkey);
}
HKEY handle()
{
BOOST_ASSERT(hkey);
return hkey;
}
private:
HKEY hkey;
};
/////////////////////////////////////////////////////////////////////////////
// Registry parser
//! Read registry
template<class Ptree>
void read_registry(HKEY root,
const std::basic_string<typename Ptree::char_type> &key,
Ptree &pt)
{
typedef typename Ptree::char_type Ch;
typedef std::basic_string<Ch> Str;
typedef std::basic_stringstream<Ch> Stream;
Ptree local;
// Open key
reg_key<Ch> rk(root, key, false);
// Query key info
DWORD max_subkey_len, max_name_len, max_value_len;
LONG result = reg_query_info_key<Ch>(rk.handle(), &max_subkey_len, &max_name_len, &max_value_len);
if (result != ERROR_SUCCESS)
throw registry_parser_error("RegQueryInfoKey failed", result);
// For all subkeys
std::vector<Ch> subkey(max_subkey_len + 1);
for (DWORD index = 0; true; ++index)
{
// Get subkey name
DWORD size = static_cast<DWORD>(subkey.size());
LONG result = reg_enum_key_ex(rk.handle(), index, &subkey.front(), &size);
if (result == ERROR_NO_MORE_ITEMS)
break;
if (result != ERROR_SUCCESS)
throw registry_parser_error("RegEnumKeyEx failed", result);
// Parse recursively
Ptree &child = local.push_back(typename Ptree::value_type(&subkey.front(), Ptree()))->second;
read_registry<Ptree>(rk.handle(), &subkey.front(), child);
}
// For all values
for (DWORD index = 0; true; ++index)
{
// Resize data to max size
std::vector<Ch> name(max_name_len + 1);
std::vector<BYTE> data(max_value_len + 1);
// Get name and value from registry
DWORD name_size = static_cast<DWORD>(name.size());
DWORD data_size = static_cast<DWORD>(data.size());
DWORD type;
result = reg_enum_value<Ch>(rk.handle(), index, &name.front(), &name_size, &type, &data.front(), &data_size);
if (result == ERROR_NO_MORE_ITEMS)
break;
if (result != ERROR_SUCCESS)
throw registry_parser_error("RegEnumValue failed", result);
// Truncate data to actual size
name.resize(name_size + 1);
data.resize(data_size);
// Translate and put value in tree
Str value = translate<Ch>(type, data);
if (name_size > 0)
{
local.put(Str(detail::widen<Ch>("\\values.") + &name.front()), value);
local.put(Str(detail::widen<Ch>("\\types.") + &name.front()), type);
}
else
local.data() = value;
}
// Swap pt and local
pt.swap(local);
}
//! Write registry
template<class Ptree>
void write_registry(HKEY root,
const std::basic_string<typename Ptree::char_type> &key,
const Ptree &pt)
{
typedef typename Ptree::char_type Ch;
typedef std::basic_string<Ch> Str;
typedef std::basic_stringstream<Ch> Stream;
// Create key
reg_key<Ch> rk(root, key, true);
// Set default key value
if (!pt.data().empty())
{
std::vector<BYTE> data = translate<Ch>(REG_SZ, pt.data());
reg_set_value_ex<Ch>(rk.handle(), NULL, REG_SZ,
data.empty() ? NULL : &data.front(),
static_cast<DWORD>(data.size()));
}
// Create values
const Ptree &values = pt.get_child(detail::widen<Ch>("\\values"), empty_ptree<Ptree>());
const Ptree &types = pt.get_child(detail::widen<Ch>("\\types"), empty_ptree<Ptree>());
for (typename Ptree::const_iterator it = values.begin(), end = values.end(); it != end; ++it)
{
DWORD type = types.get(it->first, REG_SZ);
std::vector<BYTE> data = translate<Ch>(type, it->second.data());
reg_set_value_ex<Ch>(rk.handle(), it->first.c_str(), type,
data.empty() ? NULL : &data.front(),
static_cast<DWORD>(data.size()));
}
// Create subkeys
for (typename Ptree::const_iterator it = pt.begin(), end = pt.end(); it != end; ++it)
if (&it->second != &values && &it->second != &types)
write_registry(rk.handle(), it->first, it->second);
}
} } }
namespace boost { namespace property_tree
{
using registry_parser::read_registry;
using registry_parser::write_registry;
using registry_parser::registry_parser_error;
} }
#endif