mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2026-04-19 11:00:51 -04:00
From wxWidgets master 81570ae070b35c9d52de47b1f14897f3ff1a66c7. include/wx/defs.h -- __w64 warning disable patch by comex brought forward. include/wx/msw/window.h -- added GetContentScaleFactor() which was not implemented on Windows but is necessary for wxBitmap scaling on Mac OS X so it needs to work to avoid #ifdef-ing the code. src/gtk/window.cpp -- Modified DoSetClientSize() to direct call wxWindowGTK::DoSetSize() instead of using public wxWindowBase::SetSize() which now prevents derived classes (like wxAuiToolbar) intercepting the call and breaking it. This matches Windows which does NOT need to call DoSetSize internally. End result is this fixes Dolphin's debug tools toolbars on Linux. src/osx/window_osx.cpp -- Same fix as for GTK since it has the same issue. src/msw/radiobox.cpp -- Hacked to fix display in HiDPI (was clipping off end of text). Updated CMakeLists for Linux and Mac OS X. Small code changes to Dolphin to fix debug error boxes, deprecation warnings, and retain previous UI behavior on Windows.
1596 lines
46 KiB
C++
1596 lines
46 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
// Name: src/msw/registry.cpp
|
|
// Purpose: implementation of registry classes and functions
|
|
// Author: Vadim Zeitlin
|
|
// Modified by:
|
|
// Created: 03.04.98
|
|
// Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
|
|
// Licence: wxWindows licence
|
|
// TODO: - parsing of registry key names
|
|
// - support of other (than REG_SZ/REG_DWORD) registry types
|
|
// - add high level functions (RegisterOleServer, ...)
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// for compilers that support precompilation, includes "wx.h".
|
|
#include "wx/wxprec.h"
|
|
|
|
#ifdef __BORLANDC__
|
|
#pragma hdrstop
|
|
#endif
|
|
|
|
#if wxUSE_REGKEY
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/msw/wrapwin.h"
|
|
#include "wx/string.h"
|
|
#include "wx/intl.h"
|
|
#include "wx/log.h"
|
|
#include "wx/crt.h"
|
|
#include "wx/utils.h"
|
|
#endif
|
|
|
|
#include "wx/dynlib.h"
|
|
#include "wx/file.h"
|
|
#include "wx/wfstream.h"
|
|
#include "wx/msw/private.h"
|
|
|
|
// other std headers
|
|
#include <stdlib.h> // for _MAX_PATH
|
|
|
|
#ifndef _MAX_PATH
|
|
#define _MAX_PATH 512
|
|
#endif
|
|
|
|
// our header
|
|
#define HKEY_DEFINED // already defined in windows.h
|
|
#include "wx/msw/registry.h"
|
|
|
|
// some registry functions don't like signed chars
|
|
typedef unsigned char *RegString;
|
|
typedef BYTE* RegBinary;
|
|
|
|
#ifndef HKEY_PERFORMANCE_DATA
|
|
#define HKEY_PERFORMANCE_DATA ((HKEY)0x80000004)
|
|
#endif
|
|
|
|
#ifndef HKEY_CURRENT_CONFIG
|
|
#define HKEY_CURRENT_CONFIG ((HKEY)0x80000005)
|
|
#endif
|
|
|
|
#ifndef HKEY_DYN_DATA
|
|
#define HKEY_DYN_DATA ((HKEY)0x80000006)
|
|
#endif
|
|
|
|
#ifndef KEY_WOW64_64KEY
|
|
#define KEY_WOW64_64KEY 0x0100
|
|
#endif
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// constants
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// the standard key names, short names and handles all bundled together for
|
|
// convenient access
|
|
static struct
|
|
{
|
|
HKEY hkey;
|
|
const wxChar *szName;
|
|
const wxChar *szShortName;
|
|
}
|
|
aStdKeys[] =
|
|
{
|
|
{ HKEY_CLASSES_ROOT, wxT("HKEY_CLASSES_ROOT"), wxT("HKCR") },
|
|
{ HKEY_CURRENT_USER, wxT("HKEY_CURRENT_USER"), wxT("HKCU") },
|
|
{ HKEY_LOCAL_MACHINE, wxT("HKEY_LOCAL_MACHINE"), wxT("HKLM") },
|
|
{ HKEY_USERS, wxT("HKEY_USERS"), wxT("HKU") }, // short name?
|
|
{ HKEY_PERFORMANCE_DATA, wxT("HKEY_PERFORMANCE_DATA"), wxT("HKPD") },
|
|
{ HKEY_CURRENT_CONFIG, wxT("HKEY_CURRENT_CONFIG"), wxT("HKCC") },
|
|
{ HKEY_DYN_DATA, wxT("HKEY_DYN_DATA"), wxT("HKDD") }, // short name?
|
|
};
|
|
|
|
// the registry name separator (perhaps one day MS will change it to '/' ;-)
|
|
#define REG_SEPARATOR wxT('\\')
|
|
|
|
// useful for Windows programmers: makes somewhat more clear all these zeroes
|
|
// being passed to Windows APIs
|
|
#define RESERVED (0)
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// macros
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// const_cast<> is not yet supported by all compilers
|
|
#define CONST_CAST ((wxRegKey *)this)->
|
|
|
|
// and neither is mutable which m_dwLastError should be
|
|
#define m_dwLastError CONST_CAST m_dwLastError
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// non member functions
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// removes the trailing backslash from the string if it has one
|
|
static inline void RemoveTrailingSeparator(wxString& str);
|
|
|
|
// returns true if given registry key exists
|
|
static bool KeyExists(
|
|
WXHKEY hRootKey,
|
|
const wxString& szKey,
|
|
wxRegKey::WOW64ViewMode viewMode = wxRegKey::WOW64ViewMode_Default);
|
|
|
|
// return the WOW64 registry view flag which can be used with MSW registry
|
|
// functions for opening the key in the specified view
|
|
static long GetMSWViewFlags(wxRegKey::WOW64ViewMode viewMode);
|
|
|
|
// return the access rights which can be used with MSW registry functions for
|
|
// opening the key in the specified mode
|
|
static long
|
|
GetMSWAccessFlags(wxRegKey::AccessMode mode, wxRegKey::WOW64ViewMode viewMode);
|
|
|
|
// combines value and key name
|
|
static wxString GetFullName(const wxRegKey *pKey);
|
|
static wxString GetFullName(const wxRegKey *pKey, const wxString& szValue);
|
|
|
|
// returns "value" argument of wxRegKey methods converted into a value that can
|
|
// be passed to win32 registry functions; specifically, converts empty string
|
|
// to NULL
|
|
static inline const wxChar *RegValueStr(const wxString& szValue);
|
|
|
|
// Return the user-readable name of the given REG_XXX type constant.
|
|
static wxString GetTypeString(DWORD dwType)
|
|
{
|
|
#define REG_TYPE_TO_STR(type) case REG_ ## type: return wxS(#type)
|
|
|
|
switch ( dwType )
|
|
{
|
|
REG_TYPE_TO_STR(NONE);
|
|
REG_TYPE_TO_STR(SZ);
|
|
REG_TYPE_TO_STR(EXPAND_SZ);
|
|
REG_TYPE_TO_STR(BINARY);
|
|
REG_TYPE_TO_STR(DWORD);
|
|
// REG_TYPE_TO_STR(DWORD_LITTLE_ENDIAN); -- same as REG_DWORD
|
|
REG_TYPE_TO_STR(DWORD_BIG_ENDIAN);
|
|
REG_TYPE_TO_STR(LINK);
|
|
REG_TYPE_TO_STR(MULTI_SZ);
|
|
REG_TYPE_TO_STR(RESOURCE_LIST);
|
|
REG_TYPE_TO_STR(FULL_RESOURCE_DESCRIPTOR);
|
|
REG_TYPE_TO_STR(RESOURCE_REQUIREMENTS_LIST);
|
|
REG_TYPE_TO_STR(QWORD);
|
|
// REG_TYPE_TO_STR(QWORD_LITTLE_ENDIAN); -- same as REG_QWORD
|
|
|
|
default:
|
|
return wxString::Format(_("unknown (%lu)"), dwType);
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// implementation of wxRegKey class
|
|
// ============================================================================
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// static functions and variables
|
|
// ----------------------------------------------------------------------------
|
|
|
|
const size_t wxRegKey::nStdKeys = WXSIZEOF(aStdKeys);
|
|
|
|
// @@ should take a `StdKey key', but as it's often going to be used in loops
|
|
// it would require casts in user code.
|
|
const wxChar *wxRegKey::GetStdKeyName(size_t key)
|
|
{
|
|
// return empty string if key is invalid
|
|
wxCHECK_MSG( key < nStdKeys, wxEmptyString, wxT("invalid key in wxRegKey::GetStdKeyName") );
|
|
|
|
return aStdKeys[key].szName;
|
|
}
|
|
|
|
const wxChar *wxRegKey::GetStdKeyShortName(size_t key)
|
|
{
|
|
// return empty string if key is invalid
|
|
wxCHECK( key < nStdKeys, wxEmptyString );
|
|
|
|
return aStdKeys[key].szShortName;
|
|
}
|
|
|
|
wxRegKey::StdKey wxRegKey::ExtractKeyName(wxString& strKey)
|
|
{
|
|
wxString strRoot = strKey.BeforeFirst(REG_SEPARATOR);
|
|
|
|
size_t ui;
|
|
for ( ui = 0; ui < nStdKeys; ui++ ) {
|
|
if ( strRoot.CmpNoCase(aStdKeys[ui].szName) == 0 ||
|
|
strRoot.CmpNoCase(aStdKeys[ui].szShortName) == 0 ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( ui == nStdKeys ) {
|
|
wxFAIL_MSG(wxT("invalid key prefix in wxRegKey::ExtractKeyName."));
|
|
|
|
ui = HKCR;
|
|
}
|
|
else {
|
|
strKey = strKey.After(REG_SEPARATOR);
|
|
if ( !strKey.empty() && strKey.Last() == REG_SEPARATOR )
|
|
strKey.Truncate(strKey.Len() - 1);
|
|
}
|
|
|
|
return (StdKey)ui;
|
|
}
|
|
|
|
wxRegKey::StdKey wxRegKey::GetStdKeyFromHkey(WXHKEY hkey)
|
|
{
|
|
for ( size_t ui = 0; ui < nStdKeys; ui++ ) {
|
|
if ( aStdKeys[ui].hkey == (HKEY)hkey )
|
|
return (StdKey)ui;
|
|
}
|
|
|
|
wxFAIL_MSG(wxT("non root hkey passed to wxRegKey::GetStdKeyFromHkey."));
|
|
|
|
return HKCR;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// ctors and dtor
|
|
// ----------------------------------------------------------------------------
|
|
|
|
wxRegKey::wxRegKey(WOW64ViewMode viewMode) : m_viewMode(viewMode)
|
|
{
|
|
m_hRootKey = (WXHKEY) aStdKeys[HKCR].hkey;
|
|
|
|
Init();
|
|
}
|
|
|
|
wxRegKey::wxRegKey(const wxString& strKey, WOW64ViewMode viewMode)
|
|
: m_strKey(strKey), m_viewMode(viewMode)
|
|
{
|
|
m_hRootKey = (WXHKEY) aStdKeys[ExtractKeyName(m_strKey)].hkey;
|
|
|
|
Init();
|
|
}
|
|
|
|
// parent is a predefined (and preopened) key
|
|
wxRegKey::wxRegKey(StdKey keyParent,
|
|
const wxString& strKey,
|
|
WOW64ViewMode viewMode)
|
|
: m_strKey(strKey), m_viewMode(viewMode)
|
|
{
|
|
RemoveTrailingSeparator(m_strKey);
|
|
m_hRootKey = (WXHKEY) aStdKeys[keyParent].hkey;
|
|
|
|
Init();
|
|
}
|
|
|
|
// parent is a normal regkey
|
|
wxRegKey::wxRegKey(const wxRegKey& keyParent, const wxString& strKey)
|
|
: m_strKey(keyParent.m_strKey), m_viewMode(keyParent.GetView())
|
|
{
|
|
// combine our name with parent's to get the full name
|
|
if ( !m_strKey.empty() &&
|
|
(strKey.empty() || strKey[0] != REG_SEPARATOR) ) {
|
|
m_strKey += REG_SEPARATOR;
|
|
}
|
|
|
|
m_strKey += strKey;
|
|
RemoveTrailingSeparator(m_strKey);
|
|
|
|
m_hRootKey = keyParent.m_hRootKey;
|
|
|
|
Init();
|
|
}
|
|
|
|
// dtor closes the key releasing system resource
|
|
wxRegKey::~wxRegKey()
|
|
{
|
|
Close();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// change the key name/hkey
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// set the full key name
|
|
void wxRegKey::SetName(const wxString& strKey)
|
|
{
|
|
Close();
|
|
|
|
m_strKey = strKey;
|
|
m_hRootKey = (WXHKEY) aStdKeys[ExtractKeyName(m_strKey)].hkey;
|
|
}
|
|
|
|
// the name is relative to the parent key
|
|
void wxRegKey::SetName(StdKey keyParent, const wxString& strKey)
|
|
{
|
|
Close();
|
|
|
|
m_strKey = strKey;
|
|
RemoveTrailingSeparator(m_strKey);
|
|
m_hRootKey = (WXHKEY) aStdKeys[keyParent].hkey;
|
|
}
|
|
|
|
// the name is relative to the parent key
|
|
void wxRegKey::SetName(const wxRegKey& keyParent, const wxString& strKey)
|
|
{
|
|
Close();
|
|
|
|
// combine our name with parent's to get the full name
|
|
|
|
// NB: this method is called by wxRegConfig::SetPath() which is a performance
|
|
// critical function and so it preallocates space for our m_strKey to
|
|
// gain some speed - this is why we only use += here and not = which
|
|
// would just free the prealloc'd buffer and would have to realloc it the
|
|
// next line!
|
|
m_strKey.clear();
|
|
m_strKey += keyParent.m_strKey;
|
|
if ( !strKey.empty() && strKey[0] != REG_SEPARATOR )
|
|
m_strKey += REG_SEPARATOR;
|
|
m_strKey += strKey;
|
|
|
|
RemoveTrailingSeparator(m_strKey);
|
|
|
|
m_hRootKey = keyParent.m_hRootKey;
|
|
}
|
|
|
|
// hKey should be opened and will be closed in wxRegKey dtor
|
|
void wxRegKey::SetHkey(WXHKEY hKey)
|
|
{
|
|
Close();
|
|
|
|
m_hKey = hKey;
|
|
|
|
// we don't know the parent of this key, assume HKLM by default
|
|
m_hRootKey = HKEY_LOCAL_MACHINE;
|
|
|
|
// we don't know in which mode was this key opened but we can't reopen it
|
|
// anyhow because we don't know its name, so the only thing we can is to hope
|
|
// that it allows all the operations which we're going to perform on it
|
|
m_mode = Write;
|
|
|
|
// reset old data
|
|
m_strKey.clear();
|
|
m_dwLastError = 0;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// info about the key
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// returns true if the key exists
|
|
bool wxRegKey::Exists() const
|
|
{
|
|
// opened key has to exist, try to open it if not done yet
|
|
return IsOpened()
|
|
? true
|
|
: KeyExists(m_hRootKey, m_strKey, m_viewMode);
|
|
}
|
|
|
|
// returns the full name of the key (prefix is abbreviated if bShortPrefix)
|
|
wxString wxRegKey::GetName(bool bShortPrefix) const
|
|
{
|
|
StdKey key = GetStdKeyFromHkey((WXHKEY) m_hRootKey);
|
|
wxString str = bShortPrefix ? aStdKeys[key].szShortName
|
|
: aStdKeys[key].szName;
|
|
if ( !m_strKey.empty() )
|
|
str << wxT("\\") << m_strKey;
|
|
|
|
return str;
|
|
}
|
|
|
|
bool wxRegKey::GetKeyInfo(size_t *pnSubKeys,
|
|
size_t *pnMaxKeyLen,
|
|
size_t *pnValues,
|
|
size_t *pnMaxValueLen) const
|
|
{
|
|
// it might be unexpected to some that this function doesn't open the key
|
|
wxASSERT_MSG( IsOpened(), wxT("key should be opened in GetKeyInfo") );
|
|
|
|
// We need to use intermediate variables in 64 bit build as the function
|
|
// parameters must be 32 bit DWORDs and not 64 bit size_t values.
|
|
#ifdef __WIN64__
|
|
DWORD dwSubKeys = 0,
|
|
dwMaxKeyLen = 0,
|
|
dwValues = 0,
|
|
dwMaxValueLen = 0;
|
|
|
|
#define REG_PARAM(name) &dw##name
|
|
#else // Win32
|
|
#define REG_PARAM(name) (LPDWORD)(pn##name)
|
|
#endif
|
|
|
|
|
|
m_dwLastError = ::RegQueryInfoKey
|
|
(
|
|
(HKEY) m_hKey,
|
|
NULL, // class name
|
|
NULL, // (ptr to) size of class name buffer
|
|
RESERVED,
|
|
REG_PARAM(SubKeys), // [out] number of subkeys
|
|
REG_PARAM(MaxKeyLen), // [out] max length of a subkey name
|
|
NULL, // longest subkey class name
|
|
REG_PARAM(Values), // [out] number of values
|
|
REG_PARAM(MaxValueLen), // [out] max length of a value name
|
|
NULL, // longest value data
|
|
NULL, // security descriptor
|
|
NULL // time of last modification
|
|
);
|
|
|
|
#ifdef __WIN64__
|
|
if ( pnSubKeys )
|
|
*pnSubKeys = dwSubKeys;
|
|
if ( pnMaxKeyLen )
|
|
*pnMaxKeyLen = dwMaxKeyLen;
|
|
if ( pnValues )
|
|
*pnValues = dwValues;
|
|
if ( pnMaxValueLen )
|
|
*pnMaxValueLen = dwMaxValueLen;
|
|
#endif // __WIN64__
|
|
|
|
#undef REG_PARAM
|
|
|
|
if ( m_dwLastError != ERROR_SUCCESS ) {
|
|
wxLogSysError(m_dwLastError, _("Can't get info about registry key '%s'"),
|
|
GetName().c_str());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// operations
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// opens key (it's not an error to call Open() on an already opened key)
|
|
bool wxRegKey::Open(AccessMode mode)
|
|
{
|
|
if ( IsOpened() )
|
|
{
|
|
if ( mode <= m_mode )
|
|
return true;
|
|
|
|
// we had been opened in read mode but now must be reopened in write
|
|
Close();
|
|
}
|
|
|
|
HKEY tmpKey;
|
|
m_dwLastError = ::RegOpenKeyEx
|
|
(
|
|
(HKEY) m_hRootKey,
|
|
m_strKey.t_str(),
|
|
RESERVED,
|
|
GetMSWAccessFlags(mode, m_viewMode),
|
|
&tmpKey
|
|
);
|
|
|
|
if ( m_dwLastError != ERROR_SUCCESS )
|
|
{
|
|
wxLogSysError(m_dwLastError, _("Can't open registry key '%s'"),
|
|
GetName().c_str());
|
|
return false;
|
|
}
|
|
|
|
m_hKey = (WXHKEY) tmpKey;
|
|
m_mode = mode;
|
|
|
|
return true;
|
|
}
|
|
|
|
// creates key, failing if it exists and !bOkIfExists
|
|
bool wxRegKey::Create(bool bOkIfExists)
|
|
{
|
|
// check for existence only if asked (i.e. order is important!)
|
|
if ( !bOkIfExists && Exists() )
|
|
return false;
|
|
|
|
if ( IsOpened() )
|
|
return true;
|
|
|
|
HKEY tmpKey;
|
|
DWORD disposition;
|
|
// Minimum supported OS for RegCreateKeyEx: Win 95, Win NT 3.1, Win CE 1.0
|
|
m_dwLastError = RegCreateKeyEx((HKEY) m_hRootKey, m_strKey.t_str(),
|
|
0, // reserved and must be 0
|
|
NULL, // The user-defined class type of this key.
|
|
REG_OPTION_NON_VOLATILE, // supports other values as well; see MS docs
|
|
GetMSWAccessFlags(wxRegKey::Write, m_viewMode),
|
|
NULL, // pointer to a SECURITY_ATTRIBUTES structure
|
|
&tmpKey,
|
|
&disposition);
|
|
|
|
if ( m_dwLastError != ERROR_SUCCESS ) {
|
|
wxLogSysError(m_dwLastError, _("Can't create registry key '%s'"),
|
|
GetName().c_str());
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
m_hKey = (WXHKEY) tmpKey;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// close the key, it's not an error to call it when not opened
|
|
bool wxRegKey::Close()
|
|
{
|
|
if ( IsOpened() ) {
|
|
m_dwLastError = RegCloseKey((HKEY) m_hKey);
|
|
m_hKey = 0;
|
|
|
|
if ( m_dwLastError != ERROR_SUCCESS ) {
|
|
wxLogSysError(m_dwLastError, _("Can't close registry key '%s'"),
|
|
GetName().c_str());
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
wxRegKey::RenameValue(const wxString& szValueOld, const wxString& szValueNew)
|
|
{
|
|
bool ok = true;
|
|
if ( HasValue(szValueNew) ) {
|
|
wxLogError(_("Registry value '%s' already exists."), szValueNew);
|
|
|
|
ok = false;
|
|
}
|
|
|
|
if ( !ok ||
|
|
!CopyValue(szValueOld, *this, szValueNew) ||
|
|
!DeleteValue(szValueOld) ) {
|
|
wxLogError(_("Failed to rename registry value '%s' to '%s'."),
|
|
szValueOld, szValueNew);
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool wxRegKey::CopyValue(const wxString& szValue,
|
|
wxRegKey& keyDst,
|
|
const wxString& szValueNew)
|
|
{
|
|
wxString valueNew(szValueNew);
|
|
if ( valueNew.empty() ) {
|
|
// by default, use the same name
|
|
valueNew = szValue;
|
|
}
|
|
|
|
switch ( GetValueType(szValue) ) {
|
|
case Type_String:
|
|
case Type_Expand_String:
|
|
{
|
|
wxString strVal;
|
|
return QueryRawValue(szValue, strVal) &&
|
|
keyDst.SetValue(valueNew, strVal);
|
|
}
|
|
|
|
case Type_Dword:
|
|
/* case Type_Dword_little_endian: == Type_Dword */
|
|
{
|
|
long dwVal;
|
|
return QueryValue(szValue, &dwVal) &&
|
|
keyDst.SetValue(valueNew, dwVal);
|
|
}
|
|
|
|
case Type_Binary:
|
|
{
|
|
wxMemoryBuffer buf;
|
|
return QueryValue(szValue,buf) &&
|
|
keyDst.SetValue(valueNew,buf);
|
|
}
|
|
|
|
// these types are unsupported because I am not sure about how
|
|
// exactly they should be copied and because they shouldn't
|
|
// occur among the application keys (supposedly created with
|
|
// this class)
|
|
case Type_None:
|
|
case Type_Dword_big_endian:
|
|
case Type_Link:
|
|
case Type_Multi_String:
|
|
case Type_Resource_list:
|
|
case Type_Full_resource_descriptor:
|
|
case Type_Resource_requirements_list:
|
|
default:
|
|
wxLogError(_("Can't copy values of unsupported type %d."),
|
|
GetValueType(szValue));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool wxRegKey::Rename(const wxString& szNewName)
|
|
{
|
|
wxCHECK_MSG( !m_strKey.empty(), false, wxT("registry hives can't be renamed") );
|
|
|
|
if ( !Exists() ) {
|
|
wxLogError(_("Registry key '%s' does not exist, cannot rename it."),
|
|
GetFullName(this));
|
|
|
|
return false;
|
|
}
|
|
|
|
// do we stay in the same hive?
|
|
bool inSameHive = !wxStrchr(szNewName, REG_SEPARATOR);
|
|
|
|
// construct the full new name of the key
|
|
wxRegKey keyDst;
|
|
|
|
if ( inSameHive ) {
|
|
// rename the key to the new name under the same parent
|
|
wxString strKey = m_strKey.BeforeLast(REG_SEPARATOR);
|
|
if ( !strKey.empty() ) {
|
|
// don't add '\\' in the start if strFullNewName is empty
|
|
strKey += REG_SEPARATOR;
|
|
}
|
|
|
|
strKey += szNewName;
|
|
|
|
keyDst.SetName(GetStdKeyFromHkey(m_hRootKey), strKey);
|
|
}
|
|
else {
|
|
// this is the full name already
|
|
keyDst.SetName(szNewName);
|
|
}
|
|
|
|
bool ok = keyDst.Create(false /* fail if alredy exists */);
|
|
if ( !ok ) {
|
|
wxLogError(_("Registry key '%s' already exists."),
|
|
GetFullName(&keyDst));
|
|
}
|
|
else {
|
|
ok = Copy(keyDst) && DeleteSelf();
|
|
}
|
|
|
|
if ( !ok ) {
|
|
wxLogError(_("Failed to rename the registry key '%s' to '%s'."),
|
|
GetFullName(this), GetFullName(&keyDst));
|
|
}
|
|
else {
|
|
m_hRootKey = keyDst.m_hRootKey;
|
|
m_strKey = keyDst.m_strKey;
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
bool wxRegKey::Copy(const wxString& szNewName)
|
|
{
|
|
// create the new key first
|
|
wxRegKey keyDst(szNewName);
|
|
bool ok = keyDst.Create(false /* fail if alredy exists */);
|
|
if ( ok ) {
|
|
ok = Copy(keyDst);
|
|
|
|
// we created the dest key but copying to it failed - delete it
|
|
if ( !ok ) {
|
|
(void)keyDst.DeleteSelf();
|
|
}
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
bool wxRegKey::Copy(wxRegKey& keyDst)
|
|
{
|
|
bool ok = true;
|
|
|
|
// copy all sub keys to the new location
|
|
wxString strKey;
|
|
long lIndex;
|
|
bool bCont = GetFirstKey(strKey, lIndex);
|
|
while ( ok && bCont ) {
|
|
wxRegKey key(*this, strKey);
|
|
wxString keyName;
|
|
keyName << GetFullName(&keyDst) << REG_SEPARATOR << strKey;
|
|
ok = key.Copy(keyName);
|
|
|
|
if ( ok )
|
|
bCont = GetNextKey(strKey, lIndex);
|
|
else
|
|
wxLogError(_("Failed to copy the registry subkey '%s' to '%s'."),
|
|
GetFullName(&key), keyName.c_str());
|
|
|
|
}
|
|
|
|
// copy all values
|
|
wxString strVal;
|
|
bCont = GetFirstValue(strVal, lIndex);
|
|
while ( ok && bCont ) {
|
|
ok = CopyValue(strVal, keyDst);
|
|
|
|
if ( !ok ) {
|
|
wxLogSysError(m_dwLastError,
|
|
_("Failed to copy registry value '%s'"),
|
|
strVal.c_str());
|
|
}
|
|
else {
|
|
bCont = GetNextValue(strVal, lIndex);
|
|
}
|
|
}
|
|
|
|
if ( !ok ) {
|
|
wxLogError(_("Failed to copy the contents of registry key '%s' to '%s'."),
|
|
GetFullName(this), GetFullName(&keyDst));
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// delete keys/values
|
|
// ----------------------------------------------------------------------------
|
|
bool wxRegKey::DeleteSelf()
|
|
{
|
|
{
|
|
wxLogNull nolog;
|
|
if ( !Open() ) {
|
|
// it already doesn't exist - ok!
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// prevent a buggy program from erasing one of the root registry keys or an
|
|
// immediate subkey (i.e. one which doesn't have '\\' inside) of any other
|
|
// key except HKCR (HKCR has some "deleteable" subkeys)
|
|
if ( m_strKey.empty() ||
|
|
((m_hRootKey != (WXHKEY) aStdKeys[HKCR].hkey) &&
|
|
(m_strKey.Find(REG_SEPARATOR) == wxNOT_FOUND)) ) {
|
|
wxLogError(_("Registry key '%s' is needed for normal system operation,\ndeleting it will leave your system in unusable state:\noperation aborted."),
|
|
GetFullName(this));
|
|
|
|
return false;
|
|
}
|
|
|
|
// we can't delete keys while enumerating because it confuses GetNextKey, so
|
|
// we first save the key names and then delete them all
|
|
wxArrayString astrSubkeys;
|
|
|
|
wxString strKey;
|
|
long lIndex;
|
|
bool bCont = GetFirstKey(strKey, lIndex);
|
|
while ( bCont ) {
|
|
astrSubkeys.Add(strKey);
|
|
|
|
bCont = GetNextKey(strKey, lIndex);
|
|
}
|
|
|
|
size_t nKeyCount = astrSubkeys.Count();
|
|
for ( size_t nKey = 0; nKey < nKeyCount; nKey++ ) {
|
|
wxRegKey key(*this, astrSubkeys[nKey]);
|
|
if ( !key.DeleteSelf() )
|
|
return false;
|
|
}
|
|
|
|
// now delete this key itself
|
|
Close();
|
|
|
|
// deleting a key which doesn't exist is not considered an error
|
|
#if wxUSE_DYNLIB_CLASS
|
|
wxDynamicLibrary dllAdvapi32(wxT("advapi32"));
|
|
// Minimum supported OS for RegDeleteKeyEx: Vista, XP Pro x64, Win Server 2008, Win Server 2003 SP1
|
|
if(dllAdvapi32.HasSymbol(wxT("RegDeleteKeyEx")))
|
|
{
|
|
typedef LONG (WINAPI *RegDeleteKeyEx_t)(HKEY, LPCTSTR, REGSAM, DWORD);
|
|
wxDYNLIB_FUNCTION(RegDeleteKeyEx_t, RegDeleteKeyEx, dllAdvapi32);
|
|
|
|
m_dwLastError = (*pfnRegDeleteKeyEx)((HKEY) m_hRootKey, m_strKey.t_str(),
|
|
GetMSWViewFlags(m_viewMode),
|
|
0); // This parameter is reserved and must be zero.
|
|
}
|
|
else
|
|
#endif // wxUSE_DYNLIB_CLASS
|
|
{
|
|
m_dwLastError = RegDeleteKey((HKEY) m_hRootKey, m_strKey.t_str());
|
|
}
|
|
|
|
if ( m_dwLastError != ERROR_SUCCESS &&
|
|
m_dwLastError != ERROR_FILE_NOT_FOUND ) {
|
|
wxLogSysError(m_dwLastError, _("Can't delete key '%s'"),
|
|
GetName().c_str());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool wxRegKey::DeleteKey(const wxString& szKey)
|
|
{
|
|
if ( !Open() )
|
|
return false;
|
|
|
|
wxRegKey key(*this, szKey);
|
|
return key.DeleteSelf();
|
|
}
|
|
|
|
bool wxRegKey::DeleteValue(const wxString& szValue)
|
|
{
|
|
if ( !Open() )
|
|
return false;
|
|
|
|
m_dwLastError = RegDeleteValue((HKEY) m_hKey, RegValueStr(szValue));
|
|
|
|
// deleting a value which doesn't exist is not considered an error
|
|
if ( (m_dwLastError != ERROR_SUCCESS) &&
|
|
(m_dwLastError != ERROR_FILE_NOT_FOUND) )
|
|
{
|
|
wxLogSysError(m_dwLastError, _("Can't delete value '%s' from key '%s'"),
|
|
szValue, GetName().c_str());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// access to values and subkeys
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// return true if value exists
|
|
bool wxRegKey::HasValue(const wxString& szValue) const
|
|
{
|
|
// this function should be silent, so suppress possible messages from Open()
|
|
wxLogNull nolog;
|
|
|
|
if ( !CONST_CAST Open(Read) )
|
|
return false;
|
|
|
|
LONG dwRet = ::RegQueryValueEx((HKEY) m_hKey,
|
|
RegValueStr(szValue),
|
|
RESERVED,
|
|
NULL, NULL, NULL);
|
|
return dwRet == ERROR_SUCCESS;
|
|
}
|
|
|
|
// returns true if this key has any values
|
|
bool wxRegKey::HasValues() const
|
|
{
|
|
// suppress possible messages from GetFirstValue()
|
|
wxLogNull nolog;
|
|
|
|
// just call GetFirstValue with dummy parameters
|
|
wxString str;
|
|
long l;
|
|
return CONST_CAST GetFirstValue(str, l);
|
|
}
|
|
|
|
// returns true if this key has any subkeys
|
|
bool wxRegKey::HasSubkeys() const
|
|
{
|
|
// suppress possible messages from GetFirstKey()
|
|
wxLogNull nolog;
|
|
|
|
// just call GetFirstKey with dummy parameters
|
|
wxString str;
|
|
long l;
|
|
return CONST_CAST GetFirstKey(str, l);
|
|
}
|
|
|
|
// returns true if given subkey exists
|
|
bool wxRegKey::HasSubKey(const wxString& szKey) const
|
|
{
|
|
// this function should be silent, so suppress possible messages from Open()
|
|
wxLogNull nolog;
|
|
|
|
if ( !CONST_CAST Open(Read) )
|
|
return false;
|
|
|
|
return KeyExists(m_hKey, szKey, m_viewMode);
|
|
}
|
|
|
|
wxRegKey::ValueType wxRegKey::GetValueType(const wxString& szValue) const
|
|
{
|
|
if ( ! CONST_CAST Open(Read) )
|
|
return Type_None;
|
|
|
|
DWORD dwType;
|
|
m_dwLastError = RegQueryValueEx((HKEY) m_hKey, RegValueStr(szValue), RESERVED,
|
|
&dwType, NULL, NULL);
|
|
if ( m_dwLastError != ERROR_SUCCESS ) {
|
|
wxLogSysError(m_dwLastError, _("Can't read value of key '%s'"),
|
|
GetName().c_str());
|
|
return Type_None;
|
|
}
|
|
|
|
return (ValueType)dwType;
|
|
}
|
|
|
|
bool wxRegKey::SetValue(const wxString& szValue, long lValue)
|
|
{
|
|
if ( CONST_CAST Open() ) {
|
|
m_dwLastError = RegSetValueEx((HKEY) m_hKey, RegValueStr(szValue),
|
|
(DWORD) RESERVED, REG_DWORD,
|
|
(RegString)&lValue, sizeof(lValue));
|
|
if ( m_dwLastError == ERROR_SUCCESS )
|
|
return true;
|
|
}
|
|
|
|
wxLogSysError(m_dwLastError, _("Can't set value of '%s'"),
|
|
GetFullName(this, szValue));
|
|
return false;
|
|
}
|
|
|
|
bool wxRegKey::QueryValue(const wxString& szValue, long *plValue) const
|
|
{
|
|
if ( CONST_CAST Open(Read) ) {
|
|
DWORD dwType, dwSize = sizeof(DWORD);
|
|
RegString pBuf = (RegString)plValue;
|
|
m_dwLastError = RegQueryValueEx((HKEY) m_hKey, RegValueStr(szValue),
|
|
RESERVED,
|
|
&dwType, pBuf, &dwSize);
|
|
if ( m_dwLastError != ERROR_SUCCESS ) {
|
|
wxLogSysError(m_dwLastError, _("Can't read value of key '%s'"),
|
|
GetName().c_str());
|
|
return false;
|
|
}
|
|
|
|
// check that we read the value of right type
|
|
if ( dwType != REG_DWORD_LITTLE_ENDIAN && dwType != REG_DWORD_BIG_ENDIAN ) {
|
|
wxLogError(_("Registry value \"%s\" is not numeric (but of type %s)"),
|
|
GetFullName(this, szValue), GetTypeString(dwType));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
bool wxRegKey::SetValue(const wxString& szValue, const wxMemoryBuffer& buffer)
|
|
{
|
|
#ifdef __TWIN32__
|
|
wxFAIL_MSG("RegSetValueEx not implemented by TWIN32");
|
|
return false;
|
|
#else
|
|
if ( CONST_CAST Open() ) {
|
|
m_dwLastError = RegSetValueEx((HKEY) m_hKey, RegValueStr(szValue),
|
|
(DWORD) RESERVED, REG_BINARY,
|
|
(RegBinary)buffer.GetData(),buffer.GetDataLen());
|
|
if ( m_dwLastError == ERROR_SUCCESS )
|
|
return true;
|
|
}
|
|
|
|
wxLogSysError(m_dwLastError, _("Can't set value of '%s'"),
|
|
GetFullName(this, szValue));
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool wxRegKey::QueryValue(const wxString& szValue, wxMemoryBuffer& buffer) const
|
|
{
|
|
if ( CONST_CAST Open(Read) ) {
|
|
// first get the type and size of the data
|
|
DWORD dwType, dwSize;
|
|
m_dwLastError = RegQueryValueEx((HKEY) m_hKey, RegValueStr(szValue),
|
|
RESERVED,
|
|
&dwType, NULL, &dwSize);
|
|
|
|
if ( m_dwLastError == ERROR_SUCCESS ) {
|
|
if ( dwType != REG_BINARY ) {
|
|
wxLogError(_("Registry value \"%s\" is not binary (but of type %s)"),
|
|
GetFullName(this, szValue), GetTypeString(dwType));
|
|
return false;
|
|
}
|
|
|
|
if ( dwSize ) {
|
|
const RegBinary pBuf = (RegBinary)buffer.GetWriteBuf(dwSize);
|
|
m_dwLastError = RegQueryValueEx((HKEY) m_hKey,
|
|
RegValueStr(szValue),
|
|
RESERVED,
|
|
&dwType,
|
|
pBuf,
|
|
&dwSize);
|
|
buffer.UngetWriteBuf(dwSize);
|
|
} else {
|
|
buffer.SetDataLen(0);
|
|
}
|
|
}
|
|
|
|
|
|
if ( m_dwLastError != ERROR_SUCCESS ) {
|
|
wxLogSysError(m_dwLastError, _("Can't read value of key '%s'"),
|
|
GetName().c_str());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
bool wxRegKey::QueryValue(const wxString& szValue,
|
|
wxString& strValue,
|
|
bool raw) const
|
|
{
|
|
if ( CONST_CAST Open(Read) )
|
|
{
|
|
|
|
// first get the type and size of the data
|
|
DWORD dwType=REG_NONE, dwSize=0;
|
|
m_dwLastError = RegQueryValueEx((HKEY) m_hKey,
|
|
RegValueStr(szValue),
|
|
RESERVED,
|
|
&dwType, NULL, &dwSize);
|
|
if ( m_dwLastError == ERROR_SUCCESS )
|
|
{
|
|
if ( dwType != REG_SZ && dwType != REG_EXPAND_SZ )
|
|
{
|
|
wxLogError(_("Registry value \"%s\" is not text (but of type %s)"),
|
|
GetFullName(this, szValue), GetTypeString(dwType));
|
|
return false;
|
|
}
|
|
|
|
// We need length in characters, not bytes.
|
|
DWORD chars = dwSize / sizeof(wxChar);
|
|
if ( !chars )
|
|
{
|
|
// must treat this case specially as GetWriteBuf() doesn't like
|
|
// being called with 0 size
|
|
strValue.Empty();
|
|
}
|
|
else
|
|
{
|
|
// extra scope for wxStringBufferLength
|
|
{
|
|
wxStringBufferLength strBuf(strValue, chars);
|
|
m_dwLastError = RegQueryValueEx((HKEY) m_hKey,
|
|
RegValueStr(szValue),
|
|
RESERVED,
|
|
&dwType,
|
|
(RegString)(wxChar*)strBuf,
|
|
&dwSize);
|
|
|
|
// The returned string may or not be NUL-terminated,
|
|
// exclude the trailing NUL if it's there (which is
|
|
// typically the case but is not guaranteed to always be).
|
|
if ( strBuf[chars - 1] == '\0' )
|
|
chars--;
|
|
|
|
strBuf.SetLength(chars);
|
|
}
|
|
|
|
// expand the var expansions in the string unless disabled
|
|
if ( (dwType == REG_EXPAND_SZ) && !raw )
|
|
{
|
|
DWORD dwExpSize = ::ExpandEnvironmentStrings(strValue.t_str(), NULL, 0);
|
|
bool ok = dwExpSize != 0;
|
|
if ( ok )
|
|
{
|
|
wxString strExpValue;
|
|
ok = ::ExpandEnvironmentStrings(strValue.t_str(),
|
|
wxStringBuffer(strExpValue, dwExpSize),
|
|
dwExpSize
|
|
) != 0;
|
|
strValue = strExpValue;
|
|
}
|
|
|
|
if ( !ok )
|
|
{
|
|
wxLogLastError(wxT("ExpandEnvironmentStrings"));
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( m_dwLastError == ERROR_SUCCESS )
|
|
return true;
|
|
}
|
|
}
|
|
|
|
wxLogSysError(m_dwLastError, _("Can't read value of '%s'"),
|
|
GetFullName(this, szValue));
|
|
return false;
|
|
}
|
|
|
|
bool wxRegKey::SetValue(const wxString& szValue, const wxString& strValue)
|
|
{
|
|
if ( CONST_CAST Open() ) {
|
|
m_dwLastError = RegSetValueEx((HKEY) m_hKey,
|
|
RegValueStr(szValue),
|
|
(DWORD) RESERVED, REG_SZ,
|
|
(RegString)wxMSW_CONV_LPCTSTR(strValue),
|
|
(strValue.Len() + 1)*sizeof(wxChar));
|
|
if ( m_dwLastError == ERROR_SUCCESS )
|
|
return true;
|
|
}
|
|
|
|
wxLogSysError(m_dwLastError, _("Can't set value of '%s'"),
|
|
GetFullName(this, szValue));
|
|
return false;
|
|
}
|
|
|
|
wxString wxRegKey::QueryDefaultValue() const
|
|
{
|
|
wxString str;
|
|
QueryValue(wxEmptyString, str, false);
|
|
return str;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// enumeration
|
|
// NB: all these functions require an index variable which allows to have
|
|
// several concurrently running indexations on the same key
|
|
// ----------------------------------------------------------------------------
|
|
|
|
bool wxRegKey::GetFirstValue(wxString& strValueName, long& lIndex)
|
|
{
|
|
if ( !Open(Read) )
|
|
return false;
|
|
|
|
lIndex = 0;
|
|
return GetNextValue(strValueName, lIndex);
|
|
}
|
|
|
|
bool wxRegKey::GetNextValue(wxString& strValueName, long& lIndex) const
|
|
{
|
|
wxASSERT( IsOpened() );
|
|
|
|
// are we already at the end of enumeration?
|
|
if ( lIndex == -1 )
|
|
return false;
|
|
|
|
wxChar szValueName[1024]; // @@ use RegQueryInfoKey...
|
|
DWORD dwValueLen = WXSIZEOF(szValueName);
|
|
|
|
m_dwLastError = RegEnumValue((HKEY) m_hKey, lIndex++,
|
|
szValueName, &dwValueLen,
|
|
RESERVED,
|
|
NULL, // [out] type
|
|
NULL, // [out] buffer for value
|
|
NULL); // [i/o] it's length
|
|
|
|
if ( m_dwLastError != ERROR_SUCCESS ) {
|
|
if ( m_dwLastError == ERROR_NO_MORE_ITEMS ) {
|
|
m_dwLastError = ERROR_SUCCESS;
|
|
lIndex = -1;
|
|
}
|
|
else {
|
|
wxLogSysError(m_dwLastError, _("Can't enumerate values of key '%s'"),
|
|
GetName().c_str());
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
strValueName = szValueName;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool wxRegKey::GetFirstKey(wxString& strKeyName, long& lIndex)
|
|
{
|
|
if ( !Open(Read) )
|
|
return false;
|
|
|
|
lIndex = 0;
|
|
return GetNextKey(strKeyName, lIndex);
|
|
}
|
|
|
|
bool wxRegKey::GetNextKey(wxString& strKeyName, long& lIndex) const
|
|
{
|
|
wxASSERT( IsOpened() );
|
|
|
|
// are we already at the end of enumeration?
|
|
if ( lIndex == -1 )
|
|
return false;
|
|
|
|
wxChar szKeyName[_MAX_PATH + 1];
|
|
|
|
m_dwLastError = RegEnumKey((HKEY) m_hKey, lIndex++, szKeyName, WXSIZEOF(szKeyName));
|
|
|
|
if ( m_dwLastError != ERROR_SUCCESS ) {
|
|
if ( m_dwLastError == ERROR_NO_MORE_ITEMS ) {
|
|
m_dwLastError = ERROR_SUCCESS;
|
|
lIndex = -1;
|
|
}
|
|
else {
|
|
wxLogSysError(m_dwLastError, _("Can't enumerate subkeys of key '%s'"),
|
|
GetName().c_str());
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
strKeyName = szKeyName;
|
|
return true;
|
|
}
|
|
|
|
// returns true if the value contains a number (else it's some string)
|
|
bool wxRegKey::IsNumericValue(const wxString& szValue) const
|
|
{
|
|
ValueType type = GetValueType(szValue);
|
|
switch ( type ) {
|
|
case Type_Dword:
|
|
/* case Type_Dword_little_endian: == Type_Dword */
|
|
case Type_Dword_big_endian:
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// exporting registry keys to file
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#if wxUSE_STREAMS
|
|
|
|
// helper functions for writing ASCII strings (even in Unicode build)
|
|
static inline bool WriteAsciiChar(wxOutputStream& ostr, char ch)
|
|
{
|
|
ostr.PutC(ch);
|
|
return ostr.IsOk();
|
|
}
|
|
|
|
static inline bool WriteAsciiEOL(wxOutputStream& ostr)
|
|
{
|
|
// as we open the file in text mode, it is enough to write LF without CR
|
|
return WriteAsciiChar(ostr, '\n');
|
|
}
|
|
|
|
static inline bool WriteAsciiString(wxOutputStream& ostr, const char *p)
|
|
{
|
|
return ostr.Write(p, strlen(p)).IsOk();
|
|
}
|
|
|
|
static inline bool WriteAsciiString(wxOutputStream& ostr, const wxString& s)
|
|
{
|
|
#if wxUSE_UNICODE
|
|
wxCharBuffer name(s.mb_str());
|
|
ostr.Write(name, strlen(name));
|
|
#else
|
|
ostr.Write(s.mb_str(), s.length());
|
|
#endif
|
|
|
|
return ostr.IsOk();
|
|
}
|
|
|
|
#endif // wxUSE_STREAMS
|
|
|
|
bool wxRegKey::Export(const wxString& filename) const
|
|
{
|
|
#if wxUSE_FFILE && wxUSE_STREAMS
|
|
if ( wxFile::Exists(filename) )
|
|
{
|
|
wxLogError(_("Exporting registry key: file \"%s\" already exists and won't be overwritten."),
|
|
filename.c_str());
|
|
return false;
|
|
}
|
|
|
|
wxFFileOutputStream ostr(filename, wxT("w"));
|
|
|
|
return ostr.IsOk() && Export(ostr);
|
|
#else
|
|
wxUnusedVar(filename);
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
#if wxUSE_STREAMS
|
|
bool wxRegKey::Export(wxOutputStream& ostr) const
|
|
{
|
|
// write out the header
|
|
if ( !WriteAsciiString(ostr, "REGEDIT4\n\n") )
|
|
return false;
|
|
|
|
return DoExport(ostr);
|
|
}
|
|
#endif // wxUSE_STREAMS
|
|
|
|
static
|
|
wxString
|
|
FormatAsHex(const void *data,
|
|
size_t size,
|
|
wxRegKey::ValueType type = wxRegKey::Type_Binary)
|
|
{
|
|
wxString value(wxT("hex"));
|
|
|
|
// binary values use just "hex:" prefix while the other ones must indicate
|
|
// the real type
|
|
if ( type != wxRegKey::Type_Binary )
|
|
value << wxT('(') << type << wxT(')');
|
|
value << wxT(':');
|
|
|
|
// write all the rest as comma-separated bytes
|
|
value.reserve(3*size + 10);
|
|
const char * const p = static_cast<const char *>(data);
|
|
for ( size_t n = 0; n < size; n++ )
|
|
{
|
|
// TODO: line wrapping: although not required by regedit, this makes
|
|
// the generated files easier to read and compare with the files
|
|
// produced by regedit
|
|
if ( n )
|
|
value << wxT(',');
|
|
|
|
value << wxString::Format(wxT("%02x"), (unsigned char)p[n]);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
static inline
|
|
wxString FormatAsHex(const wxString& value, wxRegKey::ValueType type)
|
|
{
|
|
return FormatAsHex(value.c_str(), value.length() + 1, type);
|
|
}
|
|
|
|
wxString wxRegKey::FormatValue(const wxString& name) const
|
|
{
|
|
wxString rhs;
|
|
const ValueType type = GetValueType(name);
|
|
switch ( type )
|
|
{
|
|
case Type_String:
|
|
case Type_Expand_String:
|
|
{
|
|
wxString value;
|
|
if ( !QueryRawValue(name, value) )
|
|
break;
|
|
|
|
// quotes and backslashes must be quoted, linefeeds are not
|
|
// allowed in string values
|
|
rhs.reserve(value.length() + 2);
|
|
rhs = wxT('"');
|
|
|
|
// there can be no NULs here
|
|
bool useHex = false;
|
|
for ( wxString::const_iterator p = value.begin();
|
|
p != value.end() && !useHex; ++p )
|
|
{
|
|
switch ( (*p).GetValue() )
|
|
{
|
|
case wxT('\n'):
|
|
// we can only represent this string in hex
|
|
useHex = true;
|
|
break;
|
|
|
|
case wxT('"'):
|
|
case wxT('\\'):
|
|
// escape special symbol
|
|
rhs += wxT('\\');
|
|
// fall through
|
|
|
|
default:
|
|
rhs += *p;
|
|
}
|
|
}
|
|
|
|
if ( useHex )
|
|
rhs = FormatAsHex(value, Type_String);
|
|
else
|
|
rhs += wxT('"');
|
|
}
|
|
break;
|
|
|
|
case Type_Dword:
|
|
/* case Type_Dword_little_endian: == Type_Dword */
|
|
{
|
|
long value;
|
|
if ( !QueryValue(name, &value) )
|
|
break;
|
|
|
|
rhs.Printf(wxT("dword:%08x"), (unsigned int)value);
|
|
}
|
|
break;
|
|
|
|
case Type_Multi_String:
|
|
{
|
|
wxString value;
|
|
if ( !QueryRawValue(name, value) )
|
|
break;
|
|
|
|
rhs = FormatAsHex(value, type);
|
|
}
|
|
break;
|
|
|
|
case Type_Binary:
|
|
{
|
|
wxMemoryBuffer buf;
|
|
if ( !QueryValue(name, buf) )
|
|
break;
|
|
|
|
rhs = FormatAsHex(buf.GetData(), buf.GetDataLen());
|
|
}
|
|
break;
|
|
|
|
// no idea how those appear in REGEDIT4 files
|
|
case Type_None:
|
|
case Type_Dword_big_endian:
|
|
case Type_Link:
|
|
case Type_Resource_list:
|
|
case Type_Full_resource_descriptor:
|
|
case Type_Resource_requirements_list:
|
|
default:
|
|
wxLogWarning(_("Can't export value of unsupported type %d."), type);
|
|
}
|
|
|
|
return rhs;
|
|
}
|
|
|
|
#if wxUSE_STREAMS
|
|
|
|
bool wxRegKey::DoExportValue(wxOutputStream& ostr, const wxString& name) const
|
|
{
|
|
// first examine the value type: if it's unsupported, simply skip it
|
|
// instead of aborting the entire export process because we failed to
|
|
// export a single value
|
|
wxString value = FormatValue(name);
|
|
if ( value.empty() )
|
|
{
|
|
wxLogWarning(_("Ignoring value \"%s\" of the key \"%s\"."),
|
|
name.c_str(), GetName().c_str());
|
|
return true;
|
|
}
|
|
|
|
// we do have the text representation of the value, now write everything
|
|
// out
|
|
|
|
// special case: unnamed/default value is represented as just "@"
|
|
if ( name.empty() )
|
|
{
|
|
if ( !WriteAsciiChar(ostr, '@') )
|
|
return false;
|
|
}
|
|
else // normal, named, value
|
|
{
|
|
if ( !WriteAsciiChar(ostr, '"') ||
|
|
!WriteAsciiString(ostr, name) ||
|
|
!WriteAsciiChar(ostr, '"') )
|
|
return false;
|
|
}
|
|
|
|
if ( !WriteAsciiChar(ostr, '=') )
|
|
return false;
|
|
|
|
return WriteAsciiString(ostr, value) && WriteAsciiEOL(ostr);
|
|
}
|
|
|
|
bool wxRegKey::DoExport(wxOutputStream& ostr) const
|
|
{
|
|
// write out this key name
|
|
if ( !WriteAsciiChar(ostr, '[') )
|
|
return false;
|
|
|
|
if ( !WriteAsciiString(ostr, GetName(false /* no short prefix */)) )
|
|
return false;
|
|
|
|
if ( !WriteAsciiChar(ostr, ']') || !WriteAsciiEOL(ostr) )
|
|
return false;
|
|
|
|
// dump all our values
|
|
long dummy;
|
|
wxString name;
|
|
wxRegKey& self = const_cast<wxRegKey&>(*this);
|
|
bool cont = self.GetFirstValue(name, dummy);
|
|
while ( cont )
|
|
{
|
|
if ( !DoExportValue(ostr, name) )
|
|
return false;
|
|
|
|
cont = GetNextValue(name, dummy);
|
|
}
|
|
|
|
// always terminate values by blank line, even if there were no values
|
|
if ( !WriteAsciiEOL(ostr) )
|
|
return false;
|
|
|
|
// recurse to subkeys
|
|
cont = self.GetFirstKey(name, dummy);
|
|
while ( cont )
|
|
{
|
|
wxRegKey subkey(*this, name);
|
|
if ( !subkey.DoExport(ostr) )
|
|
return false;
|
|
|
|
cont = GetNextKey(name, dummy);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#endif // wxUSE_STREAMS
|
|
|
|
// ============================================================================
|
|
// implementation of global private functions
|
|
// ============================================================================
|
|
|
|
bool KeyExists(WXHKEY hRootKey,
|
|
const wxString& szKey,
|
|
wxRegKey::WOW64ViewMode viewMode)
|
|
{
|
|
// don't close this key itself for the case of empty szKey!
|
|
if ( szKey.empty() )
|
|
return true;
|
|
|
|
HKEY hkeyDummy;
|
|
if ( ::RegOpenKeyEx
|
|
(
|
|
(HKEY)hRootKey,
|
|
szKey.t_str(),
|
|
RESERVED,
|
|
// we might not have enough rights for rw access
|
|
GetMSWAccessFlags(wxRegKey::Read, viewMode),
|
|
&hkeyDummy
|
|
) == ERROR_SUCCESS )
|
|
{
|
|
::RegCloseKey(hkeyDummy);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
long GetMSWViewFlags(wxRegKey::WOW64ViewMode viewMode)
|
|
{
|
|
long samWOW64ViewMode = 0;
|
|
|
|
switch ( viewMode )
|
|
{
|
|
case wxRegKey::WOW64ViewMode_32:
|
|
#ifdef __WIN64__ // the flag is only needed by 64 bit apps
|
|
samWOW64ViewMode = KEY_WOW64_32KEY;
|
|
#endif // Win64
|
|
break;
|
|
|
|
case wxRegKey::WOW64ViewMode_64:
|
|
#ifndef __WIN64__ // the flag is only needed by 32 bit apps
|
|
// 64 bit registry can only be accessed under 64 bit platforms
|
|
if ( wxIsPlatform64Bit() )
|
|
samWOW64ViewMode = KEY_WOW64_64KEY;
|
|
#endif // Win32
|
|
break;
|
|
|
|
default:
|
|
wxFAIL_MSG("Unknown registry view.");
|
|
// fall through
|
|
|
|
case wxRegKey::WOW64ViewMode_Default:
|
|
// Use default registry view for the current application,
|
|
// i.e. 32 bits for 32 bit ones and 64 bits for 64 bit apps
|
|
;
|
|
}
|
|
|
|
return samWOW64ViewMode;
|
|
}
|
|
|
|
long GetMSWAccessFlags(wxRegKey::AccessMode mode,
|
|
wxRegKey::WOW64ViewMode viewMode)
|
|
{
|
|
long sam = mode == wxRegKey::Read ? KEY_READ : KEY_ALL_ACCESS;
|
|
|
|
sam |= GetMSWViewFlags(viewMode);
|
|
|
|
return sam;
|
|
}
|
|
|
|
wxString GetFullName(const wxRegKey *pKey, const wxString& szValue)
|
|
{
|
|
wxString str(pKey->GetName());
|
|
if ( !szValue.empty() )
|
|
str << wxT("\\") << szValue;
|
|
|
|
return str;
|
|
}
|
|
|
|
wxString GetFullName(const wxRegKey *pKey)
|
|
{
|
|
return pKey->GetName();
|
|
}
|
|
|
|
inline void RemoveTrailingSeparator(wxString& str)
|
|
{
|
|
if ( !str.empty() && str.Last() == REG_SEPARATOR )
|
|
str.Truncate(str.Len() - 1);
|
|
}
|
|
|
|
inline const wxChar *RegValueStr(const wxString& szValue)
|
|
{
|
|
return szValue.empty() ? (const wxChar*)NULL : szValue.t_str();
|
|
}
|
|
|
|
#endif // wxUSE_REGKEY
|