blob: de7432754189554502c12e064c0a33bf77aba911 [file] [log] [blame]
/*
* libjingle
* Copyright 2003-2008, Google Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// Registry configuration wrapers class implementation
//
// Change made by S. Ganesh - ganesh@google.com:
// Use SHQueryValueEx instead of RegQueryValueEx throughout.
// A call to the SHLWAPI function is essentially a call to the standard
// function but with post-processing:
// * to fix REG_SZ or REG_EXPAND_SZ data that is not properly null-terminated;
// * to expand REG_EXPAND_SZ data.
#include "talk/base/win32regkey.h"
#include <shlwapi.h>
#include "talk/base/common.h"
#include "talk/base/logging.h"
#include "talk/base/scoped_ptr.h"
namespace talk_base {
RegKey::RegKey() {
h_key_ = NULL;
}
RegKey::~RegKey() {
Close();
}
HRESULT RegKey::Create(HKEY parent_key, const wchar_t* key_name) {
return Create(parent_key,
key_name,
REG_NONE,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
NULL);
}
HRESULT RegKey::Open(HKEY parent_key, const wchar_t* key_name) {
return Open(parent_key, key_name, KEY_ALL_ACCESS);
}
bool RegKey::HasValue(const TCHAR* value_name) const {
return (ERROR_SUCCESS == ::RegQueryValueEx(h_key_, value_name, NULL,
NULL, NULL, NULL));
}
HRESULT RegKey::SetValue(const wchar_t* full_key_name,
const wchar_t* value_name,
DWORD value) {
ASSERT(full_key_name != NULL);
return SetValueStaticHelper(full_key_name, value_name, REG_DWORD, &value);
}
HRESULT RegKey::SetValue(const wchar_t* full_key_name,
const wchar_t* value_name,
DWORD64 value) {
ASSERT(full_key_name != NULL);
return SetValueStaticHelper(full_key_name, value_name, REG_QWORD, &value);
}
HRESULT RegKey::SetValue(const wchar_t* full_key_name,
const wchar_t* value_name,
float value) {
ASSERT(full_key_name != NULL);
return SetValueStaticHelper(full_key_name, value_name,
REG_BINARY, &value, sizeof(value));
}
HRESULT RegKey::SetValue(const wchar_t* full_key_name,
const wchar_t* value_name,
double value) {
ASSERT(full_key_name != NULL);
return SetValueStaticHelper(full_key_name, value_name,
REG_BINARY, &value, sizeof(value));
}
HRESULT RegKey::SetValue(const wchar_t* full_key_name,
const wchar_t* value_name,
const TCHAR* value) {
ASSERT(full_key_name != NULL);
ASSERT(value != NULL);
return SetValueStaticHelper(full_key_name, value_name,
REG_SZ, const_cast<wchar_t*>(value));
}
HRESULT RegKey::SetValue(const wchar_t* full_key_name,
const wchar_t* value_name,
const uint8* value,
DWORD byte_count) {
ASSERT(full_key_name != NULL);
return SetValueStaticHelper(full_key_name, value_name, REG_BINARY,
const_cast<uint8*>(value), byte_count);
}
HRESULT RegKey::SetValueMultiSZ(const wchar_t* full_key_name,
const wchar_t* value_name,
const uint8* value,
DWORD byte_count) {
ASSERT(full_key_name != NULL);
return SetValueStaticHelper(full_key_name, value_name, REG_MULTI_SZ,
const_cast<uint8*>(value), byte_count);
}
HRESULT RegKey::GetValue(const wchar_t* full_key_name,
const wchar_t* value_name,
DWORD* value) {
ASSERT(full_key_name != NULL);
ASSERT(value != NULL);
return GetValueStaticHelper(full_key_name, value_name, REG_DWORD, value);
}
HRESULT RegKey::GetValue(const wchar_t* full_key_name,
const wchar_t* value_name,
DWORD64* value) {
ASSERT(full_key_name != NULL);
ASSERT(value != NULL);
return GetValueStaticHelper(full_key_name, value_name, REG_QWORD, value);
}
HRESULT RegKey::GetValue(const wchar_t* full_key_name,
const wchar_t* value_name,
float* value) {
ASSERT(value != NULL);
ASSERT(full_key_name != NULL);
DWORD byte_count = 0;
scoped_array<byte> buffer;
HRESULT hr = GetValueStaticHelper(full_key_name, value_name,
REG_BINARY, buffer.accept(), &byte_count);
if (SUCCEEDED(hr)) {
ASSERT(byte_count == sizeof(*value));
if (byte_count == sizeof(*value)) {
*value = *reinterpret_cast<float*>(buffer.get());
}
}
return hr;
}
HRESULT RegKey::GetValue(const wchar_t* full_key_name,
const wchar_t* value_name,
double* value) {
ASSERT(value != NULL);
ASSERT(full_key_name != NULL);
DWORD byte_count = 0;
scoped_array<byte> buffer;
HRESULT hr = GetValueStaticHelper(full_key_name, value_name,
REG_BINARY, buffer.accept(), &byte_count);
if (SUCCEEDED(hr)) {
ASSERT(byte_count == sizeof(*value));
if (byte_count == sizeof(*value)) {
*value = *reinterpret_cast<double*>(buffer.get());
}
}
return hr;
}
HRESULT RegKey::GetValue(const wchar_t* full_key_name,
const wchar_t* value_name,
wchar_t** value) {
ASSERT(full_key_name != NULL);
ASSERT(value != NULL);
return GetValueStaticHelper(full_key_name, value_name, REG_SZ, value);
}
HRESULT RegKey::GetValue(const wchar_t* full_key_name,
const wchar_t* value_name,
std::wstring* value) {
ASSERT(full_key_name != NULL);
ASSERT(value != NULL);
scoped_array<wchar_t> buffer;
HRESULT hr = RegKey::GetValue(full_key_name, value_name, buffer.accept());
if (SUCCEEDED(hr)) {
value->assign(buffer.get());
}
return hr;
}
HRESULT RegKey::GetValue(const wchar_t* full_key_name,
const wchar_t* value_name,
std::vector<std::wstring>* value) {
ASSERT(full_key_name != NULL);
ASSERT(value != NULL);
return GetValueStaticHelper(full_key_name, value_name, REG_MULTI_SZ, value);
}
HRESULT RegKey::GetValue(const wchar_t* full_key_name,
const wchar_t* value_name,
uint8** value,
DWORD* byte_count) {
ASSERT(full_key_name != NULL);
ASSERT(value != NULL);
ASSERT(byte_count != NULL);
return GetValueStaticHelper(full_key_name, value_name,
REG_BINARY, value, byte_count);
}
HRESULT RegKey::DeleteSubKey(const wchar_t* key_name) {
ASSERT(key_name != NULL);
ASSERT(h_key_ != NULL);
LONG res = ::RegDeleteKey(h_key_, key_name);
HRESULT hr = HRESULT_FROM_WIN32(res);
if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)) {
hr = S_FALSE;
}
return hr;
}
HRESULT RegKey::DeleteValue(const wchar_t* value_name) {
ASSERT(h_key_ != NULL);
LONG res = ::RegDeleteValue(h_key_, value_name);
HRESULT hr = HRESULT_FROM_WIN32(res);
if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)) {
hr = S_FALSE;
}
return hr;
}
HRESULT RegKey::Close() {
HRESULT hr = S_OK;
if (h_key_ != NULL) {
LONG res = ::RegCloseKey(h_key_);
hr = HRESULT_FROM_WIN32(res);
h_key_ = NULL;
}
return hr;
}
HRESULT RegKey::Create(HKEY parent_key,
const wchar_t* key_name,
wchar_t* lpszClass,
DWORD options,
REGSAM sam_desired,
LPSECURITY_ATTRIBUTES lpSecAttr,
LPDWORD lpdwDisposition) {
ASSERT(key_name != NULL);
ASSERT(parent_key != NULL);
DWORD dw = 0;
HKEY h_key = NULL;
LONG res = ::RegCreateKeyEx(parent_key, key_name, 0, lpszClass, options,
sam_desired, lpSecAttr, &h_key, &dw);
HRESULT hr = HRESULT_FROM_WIN32(res);
if (lpdwDisposition) {
*lpdwDisposition = dw;
}
// we have to close the currently opened key
// before replacing it with the new one
if (hr == S_OK) {
hr = Close();
ASSERT(hr == S_OK);
h_key_ = h_key;
}
return hr;
}
HRESULT RegKey::Open(HKEY parent_key,
const wchar_t* key_name,
REGSAM sam_desired) {
ASSERT(key_name != NULL);
ASSERT(parent_key != NULL);
HKEY h_key = NULL;
LONG res = ::RegOpenKeyEx(parent_key, key_name, 0, sam_desired, &h_key);
HRESULT hr = HRESULT_FROM_WIN32(res);
// we have to close the currently opened key
// before replacing it with the new one
if (hr == S_OK) {
// close the currently opened key if any
hr = Close();
ASSERT(hr == S_OK);
h_key_ = h_key;
}
return hr;
}
// save the key and all of its subkeys and values to a file
HRESULT RegKey::Save(const wchar_t* full_key_name, const wchar_t* file_name) {
ASSERT(full_key_name != NULL);
ASSERT(file_name != NULL);
std::wstring key_name(full_key_name);
HKEY h_key = GetRootKeyInfo(&key_name);
if (!h_key) {
return E_FAIL;
}
RegKey key;
HRESULT hr = key.Open(h_key, key_name.c_str(), KEY_READ);
if (FAILED(hr)) {
return hr;
}
AdjustCurrentProcessPrivilege(SE_BACKUP_NAME, true);
LONG res = ::RegSaveKey(key.h_key_, file_name, NULL);
AdjustCurrentProcessPrivilege(SE_BACKUP_NAME, false);
return HRESULT_FROM_WIN32(res);
}
// restore the key and all of its subkeys and values which are saved into a file
HRESULT RegKey::Restore(const wchar_t* full_key_name,
const wchar_t* file_name) {
ASSERT(full_key_name != NULL);
ASSERT(file_name != NULL);
std::wstring key_name(full_key_name);
HKEY h_key = GetRootKeyInfo(&key_name);
if (!h_key) {
return E_FAIL;
}
RegKey key;
HRESULT hr = key.Open(h_key, key_name.c_str(), KEY_WRITE);
if (FAILED(hr)) {
return hr;
}
AdjustCurrentProcessPrivilege(SE_RESTORE_NAME, true);
LONG res = ::RegRestoreKey(key.h_key_, file_name, REG_FORCE_RESTORE);
AdjustCurrentProcessPrivilege(SE_RESTORE_NAME, false);
return HRESULT_FROM_WIN32(res);
}
// check if the current key has the specified subkey
bool RegKey::HasSubkey(const wchar_t* key_name) const {
ASSERT(key_name != NULL);
RegKey key;
HRESULT hr = key.Open(h_key_, key_name, KEY_READ);
key.Close();
return hr == S_OK;
}
// static flush key
HRESULT RegKey::FlushKey(const wchar_t* full_key_name) {
ASSERT(full_key_name != NULL);
HRESULT hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
// get the root HKEY
std::wstring key_name(full_key_name);
HKEY h_key = GetRootKeyInfo(&key_name);
if (h_key != NULL) {
LONG res = ::RegFlushKey(h_key);
hr = HRESULT_FROM_WIN32(res);
}
return hr;
}
// static SET helper
HRESULT RegKey::SetValueStaticHelper(const wchar_t* full_key_name,
const wchar_t* value_name,
DWORD type,
LPVOID value,
DWORD byte_count) {
ASSERT(full_key_name != NULL);
HRESULT hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
// get the root HKEY
std::wstring key_name(full_key_name);
HKEY h_key = GetRootKeyInfo(&key_name);
if (h_key != NULL) {
RegKey key;
hr = key.Create(h_key, key_name.c_str());
if (hr == S_OK) {
switch (type) {
case REG_DWORD:
hr = key.SetValue(value_name, *(static_cast<DWORD*>(value)));
break;
case REG_QWORD:
hr = key.SetValue(value_name, *(static_cast<DWORD64*>(value)));
break;
case REG_SZ:
hr = key.SetValue(value_name, static_cast<const wchar_t*>(value));
break;
case REG_BINARY:
hr = key.SetValue(value_name, static_cast<const uint8*>(value),
byte_count);
break;
case REG_MULTI_SZ:
hr = key.SetValue(value_name, static_cast<const uint8*>(value),
byte_count, type);
break;
default:
ASSERT(false);
hr = HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH);
break;
}
// close the key after writing
HRESULT temp_hr = key.Close();
if (hr == S_OK) {
hr = temp_hr;
}
}
}
return hr;
}
// static GET helper
HRESULT RegKey::GetValueStaticHelper(const wchar_t* full_key_name,
const wchar_t* value_name,
DWORD type,
LPVOID value,
DWORD* byte_count) {
ASSERT(full_key_name != NULL);
HRESULT hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
// get the root HKEY
std::wstring key_name(full_key_name);
HKEY h_key = GetRootKeyInfo(&key_name);
if (h_key != NULL) {
RegKey key;
hr = key.Open(h_key, key_name.c_str(), KEY_READ);
if (hr == S_OK) {
switch (type) {
case REG_DWORD:
hr = key.GetValue(value_name, reinterpret_cast<DWORD*>(value));
break;
case REG_QWORD:
hr = key.GetValue(value_name, reinterpret_cast<DWORD64*>(value));
break;
case REG_SZ:
hr = key.GetValue(value_name, reinterpret_cast<wchar_t**>(value));
break;
case REG_MULTI_SZ:
hr = key.GetValue(value_name, reinterpret_cast<
std::vector<std::wstring>*>(value));
break;
case REG_BINARY:
hr = key.GetValue(value_name, reinterpret_cast<uint8**>(value),
byte_count);
break;
default:
ASSERT(false);
hr = HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH);
break;
}
// close the key after writing
HRESULT temp_hr = key.Close();
if (hr == S_OK) {
hr = temp_hr;
}
}
}
return hr;
}
// GET helper
HRESULT RegKey::GetValueHelper(const wchar_t* value_name,
DWORD* type,
uint8** value,
DWORD* byte_count) const {
ASSERT(byte_count != NULL);
ASSERT(value != NULL);
ASSERT(type != NULL);
// init return buffer
*value = NULL;
// get the size of the return data buffer
LONG res = ::SHQueryValueEx(h_key_, value_name, NULL, type, NULL, byte_count);
HRESULT hr = HRESULT_FROM_WIN32(res);
if (hr == S_OK) {
// if the value length is 0, nothing to do
if (*byte_count != 0) {
// allocate the buffer
*value = new byte[*byte_count];
ASSERT(*value != NULL);
// make the call again to get the data
res = ::SHQueryValueEx(h_key_, value_name, NULL,
type, *value, byte_count);
hr = HRESULT_FROM_WIN32(res);
ASSERT(hr == S_OK);
}
}
return hr;
}
// Int32 Get
HRESULT RegKey::GetValue(const wchar_t* value_name, DWORD* value) const {
ASSERT(value != NULL);
DWORD type = 0;
DWORD byte_count = sizeof(DWORD);
LONG res = ::SHQueryValueEx(h_key_, value_name, NULL, &type,
value, &byte_count);
HRESULT hr = HRESULT_FROM_WIN32(res);
ASSERT((hr != S_OK) || (type == REG_DWORD));
ASSERT((hr != S_OK) || (byte_count == sizeof(DWORD)));
return hr;
}
// Int64 Get
HRESULT RegKey::GetValue(const wchar_t* value_name, DWORD64* value) const {
ASSERT(value != NULL);
DWORD type = 0;
DWORD byte_count = sizeof(DWORD64);
LONG res = ::SHQueryValueEx(h_key_, value_name, NULL, &type,
value, &byte_count);
HRESULT hr = HRESULT_FROM_WIN32(res);
ASSERT((hr != S_OK) || (type == REG_QWORD));
ASSERT((hr != S_OK) || (byte_count == sizeof(DWORD64)));
return hr;
}
// String Get
HRESULT RegKey::GetValue(const wchar_t* value_name, wchar_t** value) const {
ASSERT(value != NULL);
DWORD byte_count = 0;
DWORD type = 0;
// first get the size of the string buffer
LONG res = ::SHQueryValueEx(h_key_, value_name, NULL,
&type, NULL, &byte_count);
HRESULT hr = HRESULT_FROM_WIN32(res);
if (hr == S_OK) {
// allocate room for the string and a terminating \0
*value = new wchar_t[(byte_count / sizeof(wchar_t)) + 1];
if ((*value) != NULL) {
if (byte_count != 0) {
// make the call again
res = ::SHQueryValueEx(h_key_, value_name, NULL, &type,
*value, &byte_count);
hr = HRESULT_FROM_WIN32(res);
} else {
(*value)[0] = L'\0';
}
ASSERT((hr != S_OK) || (type == REG_SZ) ||
(type == REG_MULTI_SZ) || (type == REG_EXPAND_SZ));
} else {
hr = E_OUTOFMEMORY;
}
}
return hr;
}
// get a string value
HRESULT RegKey::GetValue(const wchar_t* value_name, std::wstring* value) const {
ASSERT(value != NULL);
DWORD byte_count = 0;
DWORD type = 0;
// first get the size of the string buffer
LONG res = ::SHQueryValueEx(h_key_, value_name, NULL,
&type, NULL, &byte_count);
HRESULT hr = HRESULT_FROM_WIN32(res);
if (hr == S_OK) {
if (byte_count != 0) {
// Allocate some memory and make the call again
value->resize(byte_count / sizeof(wchar_t) + 1);
res = ::SHQueryValueEx(h_key_, value_name, NULL, &type,
&value->at(0), &byte_count);
hr = HRESULT_FROM_WIN32(res);
value->resize(wcslen(value->data()));
} else {
value->clear();
}
ASSERT((hr != S_OK) || (type == REG_SZ) ||
(type == REG_MULTI_SZ) || (type == REG_EXPAND_SZ));
}
return hr;
}
// convert REG_MULTI_SZ bytes to string array
HRESULT RegKey::MultiSZBytesToStringArray(const uint8* buffer,
DWORD byte_count,
std::vector<std::wstring>* value) {
ASSERT(buffer != NULL);
ASSERT(value != NULL);
const wchar_t* data = reinterpret_cast<const wchar_t*>(buffer);
DWORD data_len = byte_count / sizeof(wchar_t);
value->clear();
if (data_len > 1) {
// must be terminated by two null characters
if (data[data_len - 1] != 0 || data[data_len - 2] != 0) {
return E_INVALIDARG;
}
// put null-terminated strings into arrays
while (*data) {
std::wstring str(data);
value->push_back(str);
data += str.length() + 1;
}
}
return S_OK;
}
// get a std::vector<std::wstring> value from REG_MULTI_SZ type
HRESULT RegKey::GetValue(const wchar_t* value_name,
std::vector<std::wstring>* value) const {
ASSERT(value != NULL);
DWORD byte_count = 0;
DWORD type = 0;
uint8* buffer = 0;
// first get the size of the buffer
HRESULT hr = GetValueHelper(value_name, &type, &buffer, &byte_count);
ASSERT((hr != S_OK) || (type == REG_MULTI_SZ));
if (SUCCEEDED(hr)) {
hr = MultiSZBytesToStringArray(buffer, byte_count, value);
}
return hr;
}
// Binary data Get
HRESULT RegKey::GetValue(const wchar_t* value_name,
uint8** value,
DWORD* byte_count) const {
ASSERT(byte_count != NULL);
ASSERT(value != NULL);
DWORD type = 0;
HRESULT hr = GetValueHelper(value_name, &type, value, byte_count);
ASSERT((hr != S_OK) || (type == REG_MULTI_SZ) || (type == REG_BINARY));
return hr;
}
// Raw data get
HRESULT RegKey::GetValue(const wchar_t* value_name,
uint8** value,
DWORD* byte_count,
DWORD*type) const {
ASSERT(type != NULL);
ASSERT(byte_count != NULL);
ASSERT(value != NULL);
return GetValueHelper(value_name, type, value, byte_count);
}
// Int32 set
HRESULT RegKey::SetValue(const wchar_t* value_name, DWORD value) const {
ASSERT(h_key_ != NULL);
LONG res = ::RegSetValueEx(h_key_, value_name, NULL, REG_DWORD,
reinterpret_cast<const uint8*>(&value),
sizeof(DWORD));
return HRESULT_FROM_WIN32(res);
}
// Int64 set
HRESULT RegKey::SetValue(const wchar_t* value_name, DWORD64 value) const {
ASSERT(h_key_ != NULL);
LONG res = ::RegSetValueEx(h_key_, value_name, NULL, REG_QWORD,
reinterpret_cast<const uint8*>(&value),
sizeof(DWORD64));
return HRESULT_FROM_WIN32(res);
}
// String set
HRESULT RegKey::SetValue(const wchar_t* value_name,
const wchar_t* value) const {
ASSERT(value != NULL);
ASSERT(h_key_ != NULL);
LONG res = ::RegSetValueEx(h_key_, value_name, NULL, REG_SZ,
reinterpret_cast<const uint8*>(value),
(lstrlen(value) + 1) * sizeof(wchar_t));
return HRESULT_FROM_WIN32(res);
}
// Binary data set
HRESULT RegKey::SetValue(const wchar_t* value_name,
const uint8* value,
DWORD byte_count) const {
ASSERT(h_key_ != NULL);
// special case - if 'value' is NULL make sure byte_count is zero
if (value == NULL) {
byte_count = 0;
}
LONG res = ::RegSetValueEx(h_key_, value_name, NULL,
REG_BINARY, value, byte_count);
return HRESULT_FROM_WIN32(res);
}
// Raw data set
HRESULT RegKey::SetValue(const wchar_t* value_name,
const uint8* value,
DWORD byte_count,
DWORD type) const {
ASSERT(value != NULL);
ASSERT(h_key_ != NULL);
LONG res = ::RegSetValueEx(h_key_, value_name, NULL, type, value, byte_count);
return HRESULT_FROM_WIN32(res);
}
bool RegKey::HasKey(const wchar_t* full_key_name) {
ASSERT(full_key_name != NULL);
// get the root HKEY
std::wstring key_name(full_key_name);
HKEY h_key = GetRootKeyInfo(&key_name);
if (h_key != NULL) {
RegKey key;
HRESULT hr = key.Open(h_key, key_name.c_str(), KEY_READ);
key.Close();
return S_OK == hr;
}
return false;
}
// static version of HasValue
bool RegKey::HasValue(const wchar_t* full_key_name, const wchar_t* value_name) {
ASSERT(full_key_name != NULL);
bool has_value = false;
// get the root HKEY
std::wstring key_name(full_key_name);
HKEY h_key = GetRootKeyInfo(&key_name);
if (h_key != NULL) {
RegKey key;
if (key.Open(h_key, key_name.c_str(), KEY_READ) == S_OK) {
has_value = key.HasValue(value_name);
key.Close();
}
}
return has_value;
}
HRESULT RegKey::GetValueType(const wchar_t* full_key_name,
const wchar_t* value_name,
DWORD* value_type) {
ASSERT(full_key_name != NULL);
ASSERT(value_type != NULL);
*value_type = REG_NONE;
std::wstring key_name(full_key_name);
HKEY h_key = GetRootKeyInfo(&key_name);
RegKey key;
HRESULT hr = key.Open(h_key, key_name.c_str(), KEY_READ);
if (SUCCEEDED(hr)) {
LONG res = ::SHQueryValueEx(key.h_key_, value_name, NULL, value_type,
NULL, NULL);
if (res != ERROR_SUCCESS) {
hr = HRESULT_FROM_WIN32(res);
}
}
return hr;
}
HRESULT RegKey::DeleteKey(const wchar_t* full_key_name) {
ASSERT(full_key_name != NULL);
return DeleteKey(full_key_name, true);
}
HRESULT RegKey::DeleteKey(const wchar_t* full_key_name, bool recursively) {
ASSERT(full_key_name != NULL);
// need to open the parent key first
// get the root HKEY
std::wstring key_name(full_key_name);
HKEY h_key = GetRootKeyInfo(&key_name);
// get the parent key
std::wstring parent_key(GetParentKeyInfo(&key_name));
RegKey key;
HRESULT hr = key.Open(h_key, parent_key.c_str());
if (hr == S_OK) {
hr = recursively ? key.RecurseDeleteSubKey(key_name.c_str())
: key.DeleteSubKey(key_name.c_str());
} else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)) {
hr = S_FALSE;
}
key.Close();
return hr;
}
HRESULT RegKey::DeleteValue(const wchar_t* full_key_name,
const wchar_t* value_name) {
ASSERT(full_key_name != NULL);
HRESULT hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
// get the root HKEY
std::wstring key_name(full_key_name);
HKEY h_key = GetRootKeyInfo(&key_name);
if (h_key != NULL) {
RegKey key;
hr = key.Open(h_key, key_name.c_str());
if (hr == S_OK) {
hr = key.DeleteValue(value_name);
key.Close();
}
}
return hr;
}
HRESULT RegKey::RecurseDeleteSubKey(const wchar_t* key_name) {
ASSERT(key_name != NULL);
RegKey key;
HRESULT hr = key.Open(h_key_, key_name);
if (hr == S_OK) {
// enumerate all subkeys of this key and recursivelly delete them
FILETIME time = {0};
wchar_t key_name_buf[kMaxKeyNameChars] = {0};
DWORD key_name_buf_size = kMaxKeyNameChars;
while (hr == S_OK &&
::RegEnumKeyEx(key.h_key_, 0, key_name_buf, &key_name_buf_size,
NULL, NULL, NULL, &time) == ERROR_SUCCESS) {
hr = key.RecurseDeleteSubKey(key_name_buf);
// restore the buffer size
key_name_buf_size = kMaxKeyNameChars;
}
// close the top key
key.Close();
}
if (hr == S_OK) {
// the key has no more children keys
// delete the key and all of its values
hr = DeleteSubKey(key_name);
}
return hr;
}
HKEY RegKey::GetRootKeyInfo(std::wstring* full_key_name) {
ASSERT(full_key_name != NULL);
HKEY h_key = NULL;
// get the root HKEY
int index = full_key_name->find(L'\\');
std::wstring root_key;
if (index == -1) {
root_key = *full_key_name;
*full_key_name = L"";
} else {
root_key = full_key_name->substr(0, index);
*full_key_name = full_key_name->substr(index + 1,
full_key_name->length() - index - 1);
}
for (std::wstring::iterator iter = root_key.begin();
iter != root_key.end(); ++iter) {
*iter = toupper(*iter);
}
if (!root_key.compare(L"HKLM") ||
!root_key.compare(L"HKEY_LOCAL_MACHINE")) {
h_key = HKEY_LOCAL_MACHINE;
} else if (!root_key.compare(L"HKCU") ||
!root_key.compare(L"HKEY_CURRENT_USER")) {
h_key = HKEY_CURRENT_USER;
} else if (!root_key.compare(L"HKU") ||
!root_key.compare(L"HKEY_USERS")) {
h_key = HKEY_USERS;
} else if (!root_key.compare(L"HKCR") ||
!root_key.compare(L"HKEY_CLASSES_ROOT")) {
h_key = HKEY_CLASSES_ROOT;
}
return h_key;
}
// Returns true if this key name is 'safe' for deletion
// (doesn't specify a key root)
bool RegKey::SafeKeyNameForDeletion(const wchar_t* key_name) {
ASSERT(key_name != NULL);
std::wstring key(key_name);
HKEY root_key = GetRootKeyInfo(&key);
if (!root_key) {
key = key_name;
}
if (key.empty()) {
return false;
}
bool found_subkey = false, backslash_found = false;
for (size_t i = 0 ; i < key.length() ; ++i) {
if (key[i] == L'\\') {
backslash_found = true;
} else if (backslash_found) {
found_subkey = true;
break;
}
}
return (root_key == HKEY_USERS) ? found_subkey : true;
}
std::wstring RegKey::GetParentKeyInfo(std::wstring* key_name) {
ASSERT(key_name != NULL);
// get the parent key
int index = key_name->rfind(L'\\');
std::wstring parent_key;
if (index == -1) {
parent_key = L"";
} else {
parent_key = key_name->substr(0, index);
*key_name = key_name->substr(index + 1, key_name->length() - index - 1);
}
return parent_key;
}
// get the number of values for this key
uint32 RegKey::GetValueCount() {
DWORD num_values = 0;
LONG res = ::RegQueryInfoKey(
h_key_, // key handle
NULL, // buffer for class name
NULL, // size of class string
NULL, // reserved
NULL, // number of subkeys
NULL, // longest subkey size
NULL, // longest class string
&num_values, // number of values for this key
NULL, // longest value name
NULL, // longest value data
NULL, // security descriptor
NULL); // last write time
ASSERT(res == ERROR_SUCCESS);
return num_values;
}
// Enumerators for the value_names for this key
// Called to get the value name for the given value name index
// Use GetValueCount() to get the total value_name count for this key
// Returns failure if no key at the specified index
HRESULT RegKey::GetValueNameAt(int index, std::wstring* value_name,
DWORD* type) {
ASSERT(value_name != NULL);
LONG res = ERROR_SUCCESS;
wchar_t value_name_buf[kMaxValueNameChars] = {0};
DWORD value_name_buf_size = kMaxValueNameChars;
res = ::RegEnumValue(h_key_, index, value_name_buf, &value_name_buf_size,
NULL, type, NULL, NULL);
if (res == ERROR_SUCCESS) {
value_name->assign(value_name_buf);
}
return HRESULT_FROM_WIN32(res);
}
uint32 RegKey::GetSubkeyCount() {
// number of values for key
DWORD num_subkeys = 0;
LONG res = ::RegQueryInfoKey(
h_key_, // key handle
NULL, // buffer for class name
NULL, // size of class string
NULL, // reserved
&num_subkeys, // number of subkeys
NULL, // longest subkey size
NULL, // longest class string
NULL, // number of values for this key
NULL, // longest value name
NULL, // longest value data
NULL, // security descriptor
NULL); // last write time
ASSERT(res == ERROR_SUCCESS);
return num_subkeys;
}
HRESULT RegKey::GetSubkeyNameAt(int index, std::wstring* key_name) {
ASSERT(key_name != NULL);
LONG res = ERROR_SUCCESS;
wchar_t key_name_buf[kMaxKeyNameChars] = {0};
DWORD key_name_buf_size = kMaxKeyNameChars;
res = ::RegEnumKeyEx(h_key_, index, key_name_buf, &key_name_buf_size,
NULL, NULL, NULL, NULL);
if (res == ERROR_SUCCESS) {
key_name->assign(key_name_buf);
}
return HRESULT_FROM_WIN32(res);
}
// Is the key empty: having no sub-keys and values
bool RegKey::IsKeyEmpty(const wchar_t* full_key_name) {
ASSERT(full_key_name != NULL);
bool is_empty = true;
// Get the root HKEY
std::wstring key_name(full_key_name);
HKEY h_key = GetRootKeyInfo(&key_name);
// Open the key to check
if (h_key != NULL) {
RegKey key;
HRESULT hr = key.Open(h_key, key_name.c_str(), KEY_READ);
if (SUCCEEDED(hr)) {
is_empty = key.GetSubkeyCount() == 0 && key.GetValueCount() == 0;
key.Close();
}
}
return is_empty;
}
bool AdjustCurrentProcessPrivilege(const TCHAR* privilege, bool to_enable) {
ASSERT(privilege != NULL);
bool ret = false;
HANDLE token;
if (::OpenProcessToken(::GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) {
LUID luid;
memset(&luid, 0, sizeof(luid));
if (::LookupPrivilegeValue(NULL, privilege, &luid)) {
TOKEN_PRIVILEGES privs;
privs.PrivilegeCount = 1;
privs.Privileges[0].Luid = luid;
privs.Privileges[0].Attributes = to_enable ? SE_PRIVILEGE_ENABLED : 0;
if (::AdjustTokenPrivileges(token, FALSE, &privs, 0, NULL, 0)) {
ret = true;
} else {
LOG_GLE(LS_ERROR) << "AdjustTokenPrivileges failed";
}
} else {
LOG_GLE(LS_ERROR) << "LookupPrivilegeValue failed";
}
CloseHandle(token);
} else {
LOG_GLE(LS_ERROR) << "OpenProcessToken(GetCurrentProcess) failed";
}
return ret;
}
} // namespace talk_base