1302 lines
43 KiB
C++
1302 lines
43 KiB
C++
/** \file
|
|
* Windows Registry iteration and manipulation functions.
|
|
*/
|
|
/*
|
|
* Copyright © 2000, 2001 Paul Hollingsworth
|
|
*
|
|
* This material is provided "as is", with absolutely no warranty
|
|
* expressed or implied. Any use is at your own risk. Permission to
|
|
* use or copy this software for any purpose is hereby granted without
|
|
* fee, provided the above notices are retained on all copies.
|
|
* Permission to modify the code and to distribute modified code is
|
|
* granted, provided the above notices are retained, and a notice that
|
|
* the code was modified is included with the above copyright notice.
|
|
*
|
|
* This header is part of Comet version 2.
|
|
* https://github.com/alamaison/comet
|
|
*/
|
|
|
|
#ifndef COMET_REGISTRY_H
|
|
#define COMET_REGISTRY_H
|
|
|
|
#include <windows.h>
|
|
|
|
#include <comet/reference_count.h>
|
|
#include <comet/assert.h>
|
|
#include <comet/tstring.h>
|
|
|
|
#ifdef __BORLANDC__
|
|
#define COMET_ITERATOR_VOID(tag) std::iterator<tag, void, void, void *, void >
|
|
#else
|
|
#ifndef __SGI_STL
|
|
#ifdef __MINGW32__
|
|
#define COMET_ITERATOR_VOID(tag) std::forward_iterator<tag, void>
|
|
#else
|
|
#ifdef _CPPLIB_VER
|
|
#define COMET_ITERATOR_VOID(tag) std::iterator<tag, void, void, void, void>
|
|
#else
|
|
#define COMET_ITERATOR_VOID(tag) std::iterator<tag, void, void>
|
|
#endif
|
|
#endif
|
|
#else
|
|
#define COMET_ITERATOR_VOID(tag) std::iterator<tag, void, void, void, void>
|
|
#endif
|
|
#endif
|
|
|
|
namespace comet {
|
|
/** Contains implementation of registry classes.
|
|
*/
|
|
namespace registry {
|
|
/// Unicode compatible string class used in registry operations.
|
|
// typedef std::basic_string<TCHAR> tstring;
|
|
|
|
namespace impl {
|
|
// Only when an iterator is dereferenced
|
|
// do we want to go about reading the value
|
|
// from the registry. This requires us to create
|
|
// a temporary object. But in order to overload
|
|
// operator->, our temporary object must overload
|
|
// operator-> - and often it doesn't.
|
|
// Proxy is here to effectively overload operator-> for the temporary
|
|
// object.
|
|
template<typename T>
|
|
class proxy
|
|
{
|
|
T t_;
|
|
public:
|
|
proxy(const T &t) : t_(t) {}
|
|
T *operator->()
|
|
{
|
|
return &t_;
|
|
}
|
|
|
|
const T *operator->() const
|
|
{
|
|
return &t_;
|
|
}
|
|
};
|
|
|
|
class key_base
|
|
{
|
|
HKEY key_;
|
|
mutable reference_count rc_;
|
|
|
|
static bool valid_key_(HKEY key) {
|
|
return key != 0;
|
|
}
|
|
|
|
void close_() {
|
|
if(valid_key_(key_) && (--rc_ == 0)) {
|
|
LONG errcode = ::RegCloseKey(key_);
|
|
COMET_ASSERT(ERROR_SUCCESS == errcode);
|
|
/* C4189 */ errcode;
|
|
}
|
|
key_ = 0; // invalidate key handle
|
|
}
|
|
public:
|
|
key_base(const key_base &rhs)
|
|
: key_(rhs.key_) {
|
|
if(valid_key_(key_))
|
|
++rhs.rc_;
|
|
rc_ = rhs.rc_;
|
|
}
|
|
|
|
key_base(HKEY key = 0) : key_(key) {
|
|
}
|
|
|
|
~key_base() throw() {
|
|
close_();
|
|
}
|
|
|
|
key_base &operator=(const key_base &rhs)
|
|
{
|
|
key_base tmp(rhs);
|
|
swap(tmp);
|
|
return *this;
|
|
}
|
|
|
|
void swap(key_base &rhs) throw() {
|
|
std::swap(key_, rhs.key_);
|
|
std::swap(rc_, rhs.rc_);
|
|
}
|
|
|
|
LONG open(const tstring &subkey,
|
|
REGSAM sam_desired,
|
|
HKEY *childkey) const {
|
|
*childkey = 0;
|
|
return ::RegOpenKeyEx(key_,
|
|
subkey.c_str(),
|
|
0,
|
|
sam_desired,
|
|
childkey);
|
|
}
|
|
|
|
HKEY open_nothrow(const tstring &subkey,
|
|
REGSAM sam_desired,
|
|
LONG *errcode) const {
|
|
HKEY childkey_ = 0;
|
|
LONG errcode_ = open(subkey,
|
|
sam_desired,
|
|
&childkey_);
|
|
if(errcode) *errcode = errcode_;
|
|
return childkey_;
|
|
}
|
|
|
|
LONG create(const tstring &subkey,
|
|
DWORD options,
|
|
REGSAM sam_desired,
|
|
LPSECURITY_ATTRIBUTES security_attributes,
|
|
LPDWORD disposition,
|
|
HKEY *childkey) const {
|
|
return ::RegCreateKeyEx(key_,
|
|
subkey.c_str(),
|
|
0,
|
|
REG_NONE,
|
|
options,
|
|
sam_desired,
|
|
security_attributes,
|
|
childkey,
|
|
disposition);
|
|
}
|
|
|
|
HKEY create_nothrow(const tstring &subkey,
|
|
DWORD options,
|
|
REGSAM sam_desired,
|
|
LPSECURITY_ATTRIBUTES security_attributes,
|
|
LPDWORD disposition,
|
|
LONG *errcode) const {
|
|
HKEY childkey = 0;
|
|
LONG errcode_ = create(subkey,
|
|
options,
|
|
sam_desired,
|
|
security_attributes,
|
|
disposition,
|
|
&childkey);
|
|
if(errcode) *errcode = errcode_;
|
|
return childkey;
|
|
}
|
|
|
|
LONG flush_nothrow() const {
|
|
return ::RegFlushKey(key_);
|
|
}
|
|
|
|
LONG delete_subkey_nothrow(const tstring &subkey) const {
|
|
return ::RegDeleteKey(key_, subkey.c_str());
|
|
}
|
|
|
|
LONG delete_value_nothrow(const tstring &value_name) const {
|
|
return ::RegDeleteValue(key_, value_name.c_str());
|
|
}
|
|
|
|
void close() {
|
|
close_();
|
|
}
|
|
|
|
HKEY get() const {
|
|
return key_;
|
|
}
|
|
|
|
bool is_valid() const throw()
|
|
{
|
|
return key_ ? true : false;
|
|
}
|
|
|
|
operator const void *() const throw()
|
|
{
|
|
return is_valid() ? this : 0;
|
|
}
|
|
}; // class key_base
|
|
|
|
// Enumerating through keys or values is
|
|
// very similar. These two class encapsulate
|
|
// all that is different between the two algorithms.
|
|
struct next_value
|
|
{
|
|
static LONG perform(HKEY key,
|
|
DWORD dwIndex,
|
|
LPTSTR lpName,
|
|
LPDWORD lpcName)
|
|
{
|
|
return ::RegEnumValue(key, dwIndex, lpName, lpcName, 0, 0, 0, 0);
|
|
}
|
|
};
|
|
|
|
struct next_key
|
|
{
|
|
static LONG perform(HKEY key,
|
|
DWORD dwIndex,
|
|
LPTSTR lpName,
|
|
LPDWORD lpcName)
|
|
{
|
|
return ::RegEnumKeyEx(key, dwIndex, lpName, lpcName, 0, 0, 0, 0);
|
|
}
|
|
};
|
|
} // namespace impl
|
|
|
|
|
|
/** \struct name_iterator registry.h comet/registry.h
|
|
* Iterates through a list of names. The names might be either key
|
|
* names or value names, depending on get_next.
|
|
*/
|
|
template<class error_policy, class get_next>
|
|
struct name_iterator : public COMET_ITERATOR_VOID(std::forward_iterator_tag)
|
|
{
|
|
// key_ is the only assignment that can fail. So
|
|
// we make it the first member, so that
|
|
// default assignment is exception safe.
|
|
impl::key_base key_;
|
|
DWORD index_;
|
|
DWORD buf_size_;
|
|
DWORD num_values_;
|
|
|
|
static void check_exception_(LONG errcode)
|
|
{
|
|
if(ERROR_SUCCESS != errcode)
|
|
error_policy::on_error(errcode);
|
|
}
|
|
|
|
// Is this iterator an end iterator?
|
|
bool is_end() const
|
|
{
|
|
return key_ ? false : true;
|
|
}
|
|
|
|
// Make this iterator an end
|
|
// iterator
|
|
void make_end()
|
|
{
|
|
key_.close();
|
|
}
|
|
|
|
public:
|
|
typedef tstring value_type;
|
|
name_iterator(const impl::key_base &key,
|
|
DWORD num_values,
|
|
DWORD buf_size)
|
|
: key_(key),
|
|
num_values_(num_values),
|
|
buf_size_(buf_size),
|
|
index_(0)
|
|
{
|
|
if(0 == num_values)
|
|
make_end();
|
|
}
|
|
|
|
void swap(name_iterator &rhs)
|
|
{
|
|
key_.swap(rhs.key_);
|
|
std::swap(index_, rhs.index_);
|
|
std::swap(buf_size_, rhs.buf_size_);
|
|
std::swap(num_values_, rhs.num_values_);
|
|
}
|
|
|
|
name_iterator &operator=(const name_iterator &rhs)
|
|
{
|
|
key_ = rhs.key_;
|
|
index_ = rhs.index_;
|
|
buf_size_ = rhs.buf_size_;
|
|
num_values_ = rhs.num_values_;
|
|
return *this;
|
|
}
|
|
|
|
impl::key_base key() const
|
|
{
|
|
return key_;
|
|
}
|
|
|
|
name_iterator()
|
|
{
|
|
make_end();
|
|
}
|
|
|
|
value_type operator*() const
|
|
{
|
|
tstring::value_type *buf = static_cast<tstring::value_type *>(_alloca(buf_size_));
|
|
DWORD dummy = buf_size_;
|
|
check_exception_(get_next::perform(key_.get(),
|
|
index_,
|
|
buf,
|
|
&dummy));
|
|
return buf;
|
|
}
|
|
|
|
impl::proxy<value_type> operator->() const
|
|
{
|
|
return **this;
|
|
}
|
|
|
|
name_iterator &operator++()
|
|
{
|
|
++index_;
|
|
if(index_ == num_values_)
|
|
make_end();
|
|
return *this;
|
|
}
|
|
|
|
name_iterator operator++(int) const
|
|
{
|
|
name_iterator retval = *this;
|
|
++(*this);
|
|
return retval;
|
|
}
|
|
|
|
bool operator==(const name_iterator &rhs) const
|
|
{
|
|
if(is_end()) return rhs.is_end();
|
|
if(rhs.is_end()) return is_end();
|
|
return index_ == rhs.index_;
|
|
}
|
|
|
|
bool operator!=(const name_iterator &rhs) const
|
|
{
|
|
return !(*this == rhs);
|
|
}
|
|
};
|
|
|
|
/** \class value registry.h comet/registry.h
|
|
* A pseudo-reference to a value in the registry.
|
|
* Assign to instances of this object to make changes to the corresponding registry value
|
|
* Read from this object to read values from the registry.
|
|
*/
|
|
template<class error_policy>
|
|
class value
|
|
{
|
|
impl::key_base key_;
|
|
tstring value_name_;
|
|
static void check_exception_(LONG errcode)
|
|
{
|
|
if(ERROR_SUCCESS != errcode)
|
|
error_policy::on_error(errcode);
|
|
}
|
|
static void type_mismatch()
|
|
{
|
|
error_policy::on_typemismatch();
|
|
}
|
|
|
|
LONG get_value_(DWORD *type,
|
|
BYTE *buffer,
|
|
DWORD *number_bytes) const
|
|
{
|
|
return ::RegQueryValueEx(key_.get(),
|
|
value_name_.c_str(),
|
|
0,
|
|
type,
|
|
buffer,
|
|
number_bytes);
|
|
}
|
|
|
|
void get_value(DWORD *type,
|
|
BYTE *buffer,
|
|
DWORD *number_bytes) const {
|
|
check_exception_(
|
|
get_value_(type,
|
|
buffer,
|
|
number_bytes)
|
|
);
|
|
}
|
|
|
|
LONG set_value_(DWORD type,
|
|
const BYTE *data,
|
|
DWORD number_bytes)
|
|
{
|
|
return ::RegSetValueEx(key_.get(),
|
|
value_name_.c_str(),
|
|
0,
|
|
type,
|
|
data,
|
|
number_bytes);
|
|
}
|
|
|
|
void set_value(DWORD type,
|
|
const BYTE *data,
|
|
DWORD number_bytes)
|
|
{
|
|
check_exception_(
|
|
set_value_(type,
|
|
data,
|
|
number_bytes));
|
|
}
|
|
|
|
tstring name() const
|
|
{
|
|
return value_name_;
|
|
}
|
|
public:
|
|
|
|
//! This can be used to query if a value exists.
|
|
/*!
|
|
For example:
|
|
\code
|
|
string get_thread_model(const regkey &clsid_key)
|
|
{
|
|
regkey::value_type t = clsid_key.open("InprocServer32", KEY_READ)["ThreadingModel"];
|
|
if(t.exists())
|
|
return t.str();
|
|
else
|
|
return "Single";
|
|
}
|
|
\endcode
|
|
*/
|
|
bool exists() const
|
|
{
|
|
return ERROR_SUCCESS == get_value_(0, 0, 0);
|
|
}
|
|
|
|
value(const impl::key_base &key,
|
|
const tstring &value_name)
|
|
: key_(key),
|
|
value_name_(value_name)
|
|
{
|
|
}
|
|
|
|
//! Non throwing swap
|
|
/*! This is for efficiency only.
|
|
operator= is overloaded to have a different
|
|
meaning (copying one part of the
|
|
registry to another part of the registry).
|
|
*/
|
|
void swap(value &rhs)
|
|
{
|
|
key_.swap(rhs.key_);
|
|
value_name_.swap(rhs.value_name_);
|
|
}
|
|
|
|
|
|
//! Get a value of any type. The arguments are passed directly to RegQueryValueEx
|
|
/*!
|
|
\param type Pointer to the type - can be 0
|
|
\param buffer Pointer to a buffer - can be 0
|
|
\param number_bytes Indicates size of the buffer - can be 0 if buffer is 0
|
|
*/
|
|
void get(DWORD *type,
|
|
BYTE *buffer,
|
|
DWORD *number_bytes) const
|
|
{
|
|
get_value(type,
|
|
buffer,
|
|
number_bytes);
|
|
}
|
|
|
|
//! Get a value - return errcode
|
|
/*!
|
|
\param type Pointer to the type - can be 0
|
|
\param buffer Pointer to a buffer - can be 0
|
|
\param number_bytes Indicates size of the buffer - can be 0 if buffer is 0
|
|
*/
|
|
LONG get_nothrow(DWORD *type,
|
|
BYTE *buffer,
|
|
DWORD *number_bytes) const
|
|
{
|
|
return get_value_(type, buffer, number_bytes);
|
|
}
|
|
|
|
//! Set a value arbitrarily. The arguments are passed directly to RegSetValueEx
|
|
/*!
|
|
\param type Type to set it to (e.g. REG_SZ)
|
|
\param buffer Pointer to a buffer
|
|
\param number_bytes Indicates size of the buffer
|
|
*/
|
|
void set(DWORD type,
|
|
const BYTE *buffer,
|
|
DWORD number_bytes)
|
|
{
|
|
set_value(type,
|
|
buffer,
|
|
number_bytes);
|
|
}
|
|
|
|
//! Set a value - return errcode
|
|
/*!
|
|
\param type Type to set it to (e.g. REG_SZ)
|
|
\param buffer Pointer to a buffer
|
|
\param number_bytes Indicates size of the buffer
|
|
*/
|
|
LONG set_nothrow(DWORD type,
|
|
const BYTE *buffer,
|
|
DWORD number_bytes)
|
|
{
|
|
return set_value_(type, buffer, number_bytes);
|
|
}
|
|
|
|
//! Interpret value as a string
|
|
/*!
|
|
\exception com_error If the type is not REG_SZ or REG_EXPAND_SZ (using standard error_policy)
|
|
*/
|
|
tstring str() const
|
|
{
|
|
DWORD number_bytes = 0;
|
|
DWORD type;
|
|
get_value(&type,
|
|
0,
|
|
&number_bytes);
|
|
if( (REG_SZ != type) && (REG_EXPAND_SZ != type))
|
|
type_mismatch();
|
|
BYTE *buffer = static_cast<BYTE *>(_alloca(number_bytes));
|
|
get_value(0,
|
|
buffer,
|
|
&number_bytes);
|
|
return tstring(reinterpret_cast<const tstring::value_type *>(buffer),
|
|
(number_bytes/sizeof(tstring::value_type)) - 1);
|
|
}
|
|
|
|
tstring str(const tstring& default_val) const
|
|
{
|
|
DWORD number_bytes = 0;
|
|
DWORD type;
|
|
if (get_value_(&type,
|
|
0,
|
|
&number_bytes) != ERROR_SUCCESS) return default_val;
|
|
if( (REG_SZ != type) && (REG_EXPAND_SZ != type))
|
|
type_mismatch();
|
|
BYTE *buffer = static_cast<BYTE *>(_alloca(number_bytes));
|
|
get_value(0,
|
|
buffer,
|
|
&number_bytes);
|
|
return tstring(reinterpret_cast<const tstring::value_type *>(buffer),
|
|
(number_bytes/sizeof(tstring::value_type)) - 1);
|
|
}
|
|
|
|
//! Implicit conversion to string.
|
|
operator tstring() const
|
|
{
|
|
return str();
|
|
}
|
|
|
|
//! Implicit conversion to unsigned int
|
|
operator DWORD() const
|
|
{
|
|
return dword();
|
|
}
|
|
|
|
//! Interpret value as a DWORD
|
|
/*!
|
|
\exception com_error If the type is not REG_DWORD or REG_DWORD_LITTLE_ENDIAN (using standard error_policy)
|
|
*/
|
|
DWORD dword() const
|
|
{
|
|
DWORD number_bytes = sizeof( DWORD );
|
|
DWORD type;
|
|
DWORD retval;
|
|
get_value(&type,
|
|
reinterpret_cast<BYTE *>(&retval),
|
|
&number_bytes);
|
|
if( (REG_DWORD != type) && (REG_DWORD_LITTLE_ENDIAN != type) )
|
|
type_mismatch();
|
|
return retval;
|
|
}
|
|
|
|
DWORD dword(DWORD default_val) const
|
|
{
|
|
DWORD number_bytes = sizeof( DWORD );
|
|
DWORD type;
|
|
DWORD retval;
|
|
if (get_value_(&type,
|
|
reinterpret_cast<BYTE *>(&retval),
|
|
&number_bytes) != ERROR_SUCCESS) return default_val;
|
|
if( (REG_DWORD != type) && (REG_DWORD_LITTLE_ENDIAN != type) )
|
|
type_mismatch();
|
|
return retval;
|
|
}
|
|
|
|
// These two operators are currently useless
|
|
// because VC++ 6.0 appears to have a bug -
|
|
// it claims that no conversion to pair<string,string>
|
|
// exists even though it clearly does here.
|
|
// However, it's possible these would be
|
|
// useful on other compilers (or future compilers).
|
|
operator std::pair<tstring, tstring>() const
|
|
{
|
|
return std::make_pair(name(), str());
|
|
}
|
|
|
|
operator std::pair<tstring, DWORD>() const
|
|
{
|
|
return std::make_pair(name(), dword());
|
|
}
|
|
|
|
//! Assign a string value and set the type to REG_SZ
|
|
value &operator=(const tstring &rhs)
|
|
{
|
|
set_value(REG_SZ,
|
|
reinterpret_cast<const BYTE *>(rhs.c_str()),
|
|
(rhs.length() + 1) * sizeof ( tstring::value_type));
|
|
return *this;
|
|
}
|
|
|
|
//! Assign a DWORD value and set the type to REG_DWORD
|
|
value &operator=(const DWORD &rhs)
|
|
{
|
|
set_value(REG_DWORD,
|
|
reinterpret_cast<const BYTE *>(&rhs),
|
|
sizeof (DWORD) );
|
|
return *this;
|
|
}
|
|
|
|
//! Assign an integer value - sets type to REG_DWORD
|
|
value &operator=(int rhs)
|
|
{
|
|
return *this = DWORD(rhs);
|
|
}
|
|
|
|
//! Assign value from another registry value
|
|
/*!
|
|
Because value objects always refer to a part of the registry,
|
|
this effectively copies a registry value
|
|
from somewhere else in the registry.
|
|
*/
|
|
value &operator=(const value &rhs)
|
|
{
|
|
DWORD type;
|
|
DWORD size;
|
|
rhs.get_value(&type,
|
|
0,
|
|
&size);
|
|
BYTE *buffer = static_cast<BYTE *>(_alloca(size));
|
|
rhs.get_value_(0,
|
|
buffer,
|
|
&size);
|
|
set_value(type,
|
|
buffer,
|
|
size);
|
|
return *this;
|
|
}
|
|
}; // class value
|
|
|
|
/** \class const_value_iterator registry.h comet/registry.h
|
|
* Forward const iterator over registry values.
|
|
*/
|
|
template<typename error_policy>
|
|
class const_value_iterator : public COMET_ITERATOR_VOID(std::forward_iterator_tag)
|
|
{
|
|
typedef const value<error_policy> second_type;
|
|
typedef std::pair<const tstring, second_type > value_type_;
|
|
|
|
value_type_ get_value() const
|
|
{
|
|
tstring name = *index_;
|
|
return std::make_pair(name, second_type(index_.key(), name) );
|
|
}
|
|
protected:
|
|
name_iterator<error_policy, impl::next_value> index_;
|
|
public:
|
|
typedef value_type_ value_type;
|
|
|
|
const_value_iterator(const impl::key_base &key,
|
|
DWORD num_values,
|
|
DWORD buf_size)
|
|
: index_(key, num_values, buf_size)
|
|
{
|
|
}
|
|
|
|
const_value_iterator() {}
|
|
|
|
void swap(const_value_iterator &rhs)
|
|
{
|
|
index_.swap(rhs.index_);
|
|
}
|
|
|
|
value_type operator*() const
|
|
{
|
|
return get_value();
|
|
}
|
|
|
|
const_value_iterator &operator++()
|
|
{
|
|
++index_;
|
|
return *this;
|
|
}
|
|
|
|
const_value_iterator operator++(int)
|
|
{
|
|
const_value_iterator retval(*this);
|
|
++(*this);
|
|
return retval;
|
|
}
|
|
|
|
impl::proxy<value_type> operator->()
|
|
{
|
|
return **this;
|
|
}
|
|
|
|
bool operator==(const const_value_iterator &rhs) const
|
|
{
|
|
return index_ == rhs.index_;
|
|
}
|
|
|
|
bool operator!=(const const_value_iterator &rhs) const
|
|
{
|
|
return index_ != rhs.index_;
|
|
}
|
|
};
|
|
|
|
/** \class value_iterator registry.h comet/registry.h
|
|
* Forward iterator over registry values.
|
|
*/
|
|
template<typename error_policy>
|
|
class value_iterator : public const_value_iterator<error_policy>
|
|
{
|
|
typedef value<error_policy> second_type;
|
|
typedef std::pair<const tstring, second_type> value_type_;
|
|
typedef const_value_iterator<error_policy> base;
|
|
value_type_ get_value() const
|
|
{
|
|
tstring name = *this->index_;
|
|
return std::make_pair(name, second_type(this->index_.key(), name) );
|
|
}
|
|
|
|
public:
|
|
typedef value_type_ value_type;
|
|
value_iterator(const impl::key_base &key,
|
|
DWORD num_values,
|
|
DWORD buf_size)
|
|
: base(key, num_values, buf_size) {}
|
|
|
|
value_iterator() {}
|
|
|
|
value_type operator*() const
|
|
{
|
|
return get_value();
|
|
}
|
|
|
|
value_iterator &operator++()
|
|
{
|
|
base::operator++();
|
|
return *this;
|
|
}
|
|
|
|
value_iterator operator++(int)
|
|
{
|
|
value_iterator retval(*this);
|
|
++(*this);
|
|
return retval;
|
|
}
|
|
|
|
impl::proxy<value_type> operator->()
|
|
{
|
|
return get_value();
|
|
}
|
|
|
|
bool operator==(const value_iterator &rhs) const
|
|
{
|
|
return this->index_ == rhs.index_;
|
|
}
|
|
|
|
bool operator!=(const value_iterator &rhs) const
|
|
{
|
|
return this->index_ != rhs.index_;
|
|
}
|
|
};
|
|
|
|
/** \struct collection registry.h comet/registry.h
|
|
* STL style container class for various types of registry based
|
|
* aggregations.
|
|
*/
|
|
template<typename error_policy,
|
|
typename iterator_,
|
|
typename const_iterator_ = iterator_>
|
|
struct collection
|
|
{
|
|
impl::key_base key_;
|
|
DWORD num_values_;
|
|
DWORD buf_size_;
|
|
|
|
public:
|
|
collection(const impl::key_base &key,
|
|
DWORD num_values,
|
|
DWORD buf_size)
|
|
: key_(key),
|
|
num_values_(num_values),
|
|
buf_size_(buf_size)
|
|
{
|
|
}
|
|
|
|
typedef iterator_ iterator;
|
|
typedef const_iterator_ const_iterator;
|
|
typedef typename iterator_::value_type value_type;
|
|
typedef size_t size_type;
|
|
|
|
//! Number of elements in the collection
|
|
size_type size() const { return num_values_; }
|
|
|
|
//! Exception safe swap
|
|
void swap(collection &rhs)
|
|
{
|
|
key_.swap(rhs.key_);
|
|
std::swap(num_values_, rhs.num_values_);
|
|
std::swap(buf_size_, rhs.buf_size_);
|
|
}
|
|
|
|
//! Get the first iterator
|
|
iterator begin()
|
|
{
|
|
return iterator(key_, num_values_, buf_size_);
|
|
}
|
|
|
|
//! Signature iterator marking the end of the sequence
|
|
iterator end()
|
|
{
|
|
return iterator();
|
|
}
|
|
|
|
const_iterator begin() const
|
|
{
|
|
return const_iterator(key_, num_values_, buf_size_);
|
|
}
|
|
|
|
const_iterator end() const
|
|
{
|
|
return const_iterator();
|
|
}
|
|
};
|
|
|
|
/** \class info registry.h comet/registry.h
|
|
* Structure returned by regkey.enumerate()
|
|
*/
|
|
template<typename error_policy>
|
|
class info
|
|
{
|
|
// Make key_ the first member - this
|
|
// ensures exception safe assignment.
|
|
impl::key_base key_;
|
|
|
|
// Number of values
|
|
DWORD num_values_;
|
|
|
|
// Maximum length of a value name
|
|
DWORD value_name_tchars_;
|
|
|
|
// Number of sub keys
|
|
DWORD num_subkeys_;
|
|
|
|
// Maximum length of a sub key name
|
|
DWORD subkey_name_tchars_;
|
|
|
|
static void check_exception_(LONG errcode)
|
|
{
|
|
if(ERROR_SUCCESS != errcode)
|
|
error_policy::on_error(errcode);
|
|
}
|
|
|
|
static DWORD bytes(DWORD tchars)
|
|
{
|
|
return (tchars + 1) * sizeof( tstring::value_type);
|
|
}
|
|
public:
|
|
info(const impl::key_base &key)
|
|
: key_(key)
|
|
{
|
|
check_exception_(::RegQueryInfoKey(key_.get(),
|
|
0, // lpClass - reserved
|
|
0, // lpcClass - reserved
|
|
0, // lpReserved
|
|
&num_subkeys_,
|
|
&subkey_name_tchars_,
|
|
0, // lpcMaxClassLen - I think this is also reserved
|
|
&num_values_,
|
|
&value_name_tchars_,
|
|
0, // lpcMaxValueLen - not necessary for us
|
|
0, // lpcbSecurityDescriptor
|
|
0)); // lpftLastWriteTime
|
|
}
|
|
|
|
//! Exception safe swap
|
|
void swap(info &rhs)
|
|
{
|
|
key_.swap(rhs.key_);
|
|
std::swap(num_subkeys_, rhs.num_subkeys_);
|
|
std::swap(subkey_name_tchars_, rhs.subkey_name_tchars_);
|
|
std::swap(value_name_tchars_, rhs.value_name_tchars_);
|
|
std::swap(num_values_, rhs.num_values_);
|
|
}
|
|
|
|
//! Type returned by values()
|
|
typedef collection<error_policy, value_iterator<error_policy>, const_value_iterator<error_policy> > values_type;
|
|
|
|
//! Type returned by value_names()
|
|
typedef collection<error_policy, name_iterator<error_policy, impl::next_value> > value_names_type;
|
|
|
|
//! Type returned by subkeys()
|
|
typedef collection<error_policy, name_iterator<error_policy, impl::next_key> > subkeys_type;
|
|
|
|
//! Number of values under this key (excluding the default value)
|
|
size_t num_values() const
|
|
{
|
|
return num_values_;
|
|
}
|
|
|
|
//! Number of subkeys under this key
|
|
size_t num_subkeys() const
|
|
{
|
|
return num_subkeys_;
|
|
}
|
|
|
|
//! Length of the longest value_name under this key (in TCHARs)
|
|
size_t max_value_name() const
|
|
{
|
|
return value_name_tchars_;
|
|
}
|
|
|
|
//! Length of the longest subkey name under this key (in TCHARs)
|
|
size_t max_subkey_name() const
|
|
{
|
|
return subkey_name_tchars_;
|
|
}
|
|
|
|
//! Return the collection of values
|
|
/*!
|
|
The value_type of the collection is std::pair<const std::basic_string<TCHAR>, regkey::mapped_type>
|
|
regkey::mapped_type has implicit conversions to unsigned int and std::basic_string however.
|
|
Example - copy all value-name/value pairs into a map
|
|
\code
|
|
typedef std::basic_string<TCHAR> tstring;
|
|
regkey::info_type info(regkey(HKEY_LOCAL_MACHINE).enumerate());
|
|
map<tstring, regkey::mapped_type> values_map;
|
|
copy(info.values().begin(), info.values().end(), inserter(values_map, values_map.end()));
|
|
\endcode
|
|
Example - copy all value-name/value pairs into a string map - exception will
|
|
be thrown if a non string value is encountered.
|
|
\code
|
|
typedef std::basic_string<TCHAR> tstring;
|
|
regkey::info_type info(regkey(HKEY_LOCAL_MACHINE).enumerate());
|
|
map<bstr_t, bstr_t> values_map;
|
|
copy(info.values().begin(), info.values().end(), inserter(values_map, values_map.end()));
|
|
\endcode
|
|
Example - copy all value-name/value pairs into a string/int map - exception will
|
|
be thrown if a non string value is encountered.
|
|
\code
|
|
typedef std::basic_string<TCHAR> tstring;
|
|
regkey::info_type info(regkey(HKEY_LOCAL_MACHINE).enumerate());
|
|
map<bstr_t, int> values_map;
|
|
copy(info.values().begin(), info.values().end(), inserter(values_map, values_map.end()));
|
|
\endcode
|
|
*/
|
|
values_type values() const
|
|
{
|
|
return values_type(key_, num_values_, bytes(value_name_tchars_));
|
|
}
|
|
|
|
//! Returns the collection of value names
|
|
/*! The value_type of the collection is std::basic_string<TCHAR>.
|
|
Example - copy all value names of HKEY_LOCAL_MACHINE into a list
|
|
\code
|
|
regkey::info_type info(regkey(HKEY_LOCAL_MACHINE).enumerate());
|
|
vector<string> value_names;
|
|
copy(info.value_names().begin(), info.value_names().end(), back_inserter(value_names));
|
|
\endcode
|
|
*/
|
|
value_names_type value_names() const
|
|
{
|
|
return value_names_type(key_, num_values_, bytes(value_name_tchars_));
|
|
}
|
|
|
|
//! Returns the collection of subkey names
|
|
/*! The value_type of the collection is std::basic_string<TCHAR>.
|
|
Example - copy all subkeys of HKEY_LOCAL_MACHINE into a list
|
|
\code
|
|
regkey::info_type info(regkey(HKEY_LOCAL_MACHINE).enumerate());
|
|
vector<string> subkey_names;
|
|
copy(info.subkeys().begin(), info.subkeys().end(), back_inserter(subkey_names));
|
|
\endcode
|
|
*/
|
|
subkeys_type subkeys() const
|
|
{
|
|
return subkeys_type(key_, num_subkeys_, bytes(subkey_name_tchars_));
|
|
}
|
|
};
|
|
|
|
#ifdef __BORLANDC__
|
|
using impl::key_base;
|
|
#endif
|
|
|
|
/** \class key registry.h comet/registry.h
|
|
* Registry key wrapper.
|
|
Because an HKEY cannot be duplicated in a platform independent way,
|
|
reference counting is used to enable copying of key objects.
|
|
|
|
Methods with the _nothrow suffix do not throw exceptions other than
|
|
std::bad_alloc.
|
|
|
|
Key instances can be used as output iterators for std::pair
|
|
assignments. The second argument of each pair must be assignable to
|
|
an instance of regkey::mapped_type. Currently, this means that the
|
|
second member of each pair must be convertable to either an int,
|
|
unsigned int, or std::basic_string<TCHAR>.
|
|
Example:
|
|
\code
|
|
regkey the_key = regkey(HKEY_LOCAL_MACHINE).open("Software\\Comet");
|
|
map<string,string> names;
|
|
names["one"] = "one";
|
|
names["two"] = "two";
|
|
copy(names.begin(), names.end(), the_key);
|
|
map<string,int> values;
|
|
values["three"] = 3;
|
|
values["four"] = 4;
|
|
copy(values.begin(), values.end(), the_key);
|
|
\endcode
|
|
*/
|
|
template<class error_policy>
|
|
class key : private impl::key_base {
|
|
private:
|
|
static void check_exception_(LONG errcode)
|
|
{
|
|
if(ERROR_SUCCESS != errcode)
|
|
error_policy::on_error(errcode);
|
|
}
|
|
public:
|
|
key(HKEY key_handle = 0) : key_base(key_handle) {}
|
|
|
|
//! Copy a key.
|
|
/*! In reality this
|
|
only increments a reference count.
|
|
We have to use reference counting
|
|
because ::DuplicateHandle does not
|
|
work for registry keys on windows 95/98.
|
|
*/
|
|
|
|
key(const key &rhs)
|
|
: key_base(rhs) {
|
|
}
|
|
//! Copy a key.
|
|
/*! This is useful for attaching to keys from a name iterator.
|
|
*/
|
|
key( const impl::key_base &rhs)
|
|
: key_base(rhs) {
|
|
}
|
|
|
|
void swap(key &rhs)
|
|
{
|
|
key_base::swap(rhs);
|
|
}
|
|
|
|
//! Operator overload to allow you to put key instances in a conditional.
|
|
/*! This operator allows you to write code like this:
|
|
\code
|
|
if(subkey = key(HKEY_LOCAL_MACHINE).open_nothrow(_T("Software")))
|
|
{
|
|
...
|
|
};
|
|
\endcode
|
|
const void * is used instead of the more obvious "bool" to disable
|
|
implicit conversions to int and the like.
|
|
Example:
|
|
\code
|
|
int success = key(HKEY_LOCAL_MACHINE).open_nothrow(_T("Software")); // Won't compile fortunately!!
|
|
\endcode
|
|
*/
|
|
operator const void *() const throw()
|
|
{
|
|
return is_valid() ? this : 0;
|
|
}
|
|
|
|
|
|
//! Open a subkey
|
|
key open(const tstring &subkey,
|
|
REGSAM sam_desired = KEY_ALL_ACCESS) const {
|
|
HKEY childkey_ = 0;
|
|
check_exception_(
|
|
key_base::open(subkey,
|
|
sam_desired,
|
|
&childkey_)
|
|
);
|
|
return childkey_;
|
|
}
|
|
|
|
//! Open a subkey, no exceptions
|
|
key open_nothrow(const tstring &subkey,
|
|
REGSAM sam_desired = KEY_ALL_ACCESS,
|
|
LONG *errcode = 0) const {
|
|
return key_base::open_nothrow(subkey,
|
|
sam_desired, errcode);
|
|
}
|
|
|
|
//! Create a subkey
|
|
key create(const tstring &subkey,
|
|
DWORD options = REG_OPTION_NON_VOLATILE,
|
|
REGSAM sam_desired = KEY_ALL_ACCESS,
|
|
LPSECURITY_ATTRIBUTES security_attributes = 0,
|
|
LPDWORD disposition = 0) const {
|
|
HKEY childkey_ = 0;
|
|
check_exception_(
|
|
key_base::create(subkey,
|
|
options,
|
|
sam_desired,
|
|
security_attributes,
|
|
disposition,
|
|
&childkey_)
|
|
);
|
|
return childkey_;
|
|
}
|
|
|
|
//! Create a subkey - no exceptions
|
|
key create_nothrow(const tstring &subkey,
|
|
DWORD options = REG_OPTION_NON_VOLATILE,
|
|
REGSAM sam_desired = KEY_ALL_ACCESS,
|
|
LPSECURITY_ATTRIBUTES security_attributes = 0,
|
|
LPDWORD disposition = 0,
|
|
LONG *errcode = 0) const {
|
|
return key_base::create_nothrow(subkey,
|
|
options,
|
|
sam_desired,
|
|
security_attributes,
|
|
disposition,
|
|
errcode);
|
|
}
|
|
|
|
//! Call RegFlushKey with the contained key
|
|
void flush() const {
|
|
check_exception_(
|
|
flush_nothrow()
|
|
);
|
|
}
|
|
|
|
//! Call RegFlushKey with the contained key - no exceptions
|
|
LONG flush_nothrow() const {
|
|
return key_base::flush_nothrow();
|
|
}
|
|
|
|
//! Delete the subkey
|
|
/*! Warning - the behaviour is different
|
|
between WinNT and Win95/98 if
|
|
sub keys are present.
|
|
See documentation for ::RegDeleteKey for
|
|
more information.
|
|
*/
|
|
void delete_subkey(const tstring &subkey) const {
|
|
check_exception_(
|
|
delete_subkey_nothrow(subkey.c_str())
|
|
);
|
|
}
|
|
|
|
//! Delete the subkey
|
|
/*! Warning - the behaviour is different
|
|
between WinNT and Win95/98 if
|
|
sub keys are present.
|
|
See documentation for ::RegDeleteKey for
|
|
more information.
|
|
*/
|
|
LONG delete_subkey_nothrow(const tstring &subkey) const {
|
|
return key_base::delete_subkey_nothrow(subkey);
|
|
}
|
|
|
|
//! delete a value
|
|
void delete_value(const tstring &value_name) const {
|
|
check_exception_(
|
|
delete_value_nothrow(value_name)
|
|
);
|
|
}
|
|
|
|
//! delete a value - no exceptions
|
|
LONG delete_value_nothrow(const tstring &value_name) const {
|
|
return key_base::delete_value_nothrow(value_name);
|
|
}
|
|
|
|
//! Release reference to the key.
|
|
/*! Note that this will only call ::RegCloseKey
|
|
if this was the last reference to the outstanding key.
|
|
This method is implicitly called by the destructor.
|
|
*/
|
|
void close() {
|
|
key_base::close();
|
|
}
|
|
|
|
//! Get access to the raw key without releasing ownership
|
|
HKEY get() const {
|
|
return key_base::get();
|
|
}
|
|
|
|
typedef value<error_policy> mapped_type;
|
|
|
|
// All of these methods are const because I figured
|
|
// it was more useful. It allows you to create
|
|
// temporary regkey objects for the purposes of
|
|
// updating.
|
|
|
|
//! Get a reference to a value in the registry.
|
|
/*! The returned value can be used on both sides of an assignment. Example:
|
|
\code
|
|
key.get_value("Name") = "Paul";
|
|
string name = key.get_value("Name");
|
|
cout << key.get_value("Name").str() << endl;
|
|
\endcode
|
|
*/
|
|
mapped_type get_value(const tstring &value_name = _T("")) const
|
|
{
|
|
return mapped_type(*this, value_name);
|
|
}
|
|
|
|
//! Subscript operator overload - syntactic sugar for get_value().
|
|
/*! Example:
|
|
\code
|
|
key["Name"] = "Paul";
|
|
string name = key["Name"];
|
|
cout << key["Name"].str() << endl;
|
|
\endcode
|
|
*/
|
|
mapped_type operator[](const tstring &value_name) const
|
|
{
|
|
return get_value(value_name);
|
|
}
|
|
|
|
//! Type returned by enumerate()
|
|
typedef info<error_policy> info_type;
|
|
//! Type returned by enumerate().subkeys()
|
|
typedef typename info_type::subkeys_type subkeys_type;
|
|
//! Type returned by enumerate().values()
|
|
typedef typename info_type::values_type values_type;
|
|
//! Type returned by enumerate().value_names()
|
|
typedef typename info_type::value_names_type value_names_type;
|
|
|
|
//! Enumerate the subkeys, values or value_names, or obtain other information about the key. See also info.
|
|
/*!
|
|
The enumerate() method effectively calls RegQueryInfoKey, so you should
|
|
minimize the number of calls to enumerate() if efficiency is
|
|
a concern. e.g.
|
|
The following
|
|
\code
|
|
regkey::info_type info = key.enumerate();
|
|
copy(info.values().begin(), info.values().end(), inserter(value_map, value_map.end()));
|
|
\endcode
|
|
is more efficient than
|
|
\code
|
|
copy(key.enumerate().values().begin(), key.enumerate().values().end(), inserter(value_map, value_map.end()));
|
|
\endcode
|
|
The second version will end up calling RegQueryInfoKey twice.
|
|
*/
|
|
info_type enumerate() const
|
|
{
|
|
return info_type(*this);
|
|
}
|
|
|
|
//! Part of making key into an output iterator
|
|
key &operator*()
|
|
{
|
|
return *this;
|
|
}
|
|
|
|
//! Assignment. This is designed to work with 'std::pair's - the value type of map classes
|
|
template<typename T>
|
|
key &operator=(const T &val)
|
|
{
|
|
get_value(val.first) = val.second;
|
|
return *this;
|
|
}
|
|
|
|
//! Exception safe assignment operator.
|
|
//! Can still throw std::bad_alloc due
|
|
//! to reference counting.
|
|
//template<>
|
|
key &operator=(const key &rhs)
|
|
{
|
|
key_base::operator=(rhs);
|
|
return *this;
|
|
}
|
|
|
|
//! Noop increment
|
|
key &operator++() { return *this; }
|
|
//! Noop decrement
|
|
key &operator++(int) { return *this; }
|
|
}; // class key
|
|
} // namespace registry
|
|
} // namespace comet
|
|
|
|
#endif // COMET_REGISTRY_H
|