/** \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