You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1516 lines
51 KiB
1516 lines
51 KiB
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation
|
|
|
|
Abstract:
|
|
|
|
@doc
|
|
@module registry.cxx | Implementation of CVssRegistryKey
|
|
@end
|
|
|
|
Author:
|
|
|
|
Adi Oltean [aoltean] 03/14/2001
|
|
|
|
Revision History:
|
|
|
|
Name Date Comments
|
|
aoltean 03/14/2001 Created
|
|
|
|
--*/
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Includes
|
|
#include "stdafx.hxx"
|
|
|
|
#include "memory"
|
|
#include <safeboot.h>
|
|
|
|
#include "vs_inc.hxx"
|
|
#include "vs_reg.hxx"
|
|
|
|
#include "vs_reg.hxx"
|
|
|
|
#include "vss.h"
|
|
#include "vswriter.h"
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// Standard foo for file name aliasing. This code block must be after
|
|
// all includes of VSS header files.
|
|
//
|
|
#ifdef VSS_FILE_ALIAS
|
|
#undef VSS_FILE_ALIAS
|
|
#endif
|
|
#define VSS_FILE_ALIAS "REGREGSC"
|
|
//
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CVssRegistryKey implementation
|
|
|
|
|
|
// Creates the registry key.
|
|
// Throws an error if the key already exists
|
|
void CVssRegistryKey::Create(
|
|
IN HKEY hAncestorKey,
|
|
IN LPCWSTR pwszPathFormat,
|
|
IN ...
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssRegistryKey::Create" );
|
|
|
|
BS_ASSERT(hAncestorKey);
|
|
BS_ASSERT(pwszPathFormat);
|
|
|
|
// Build the path to the key
|
|
WCHAR wszKeyPath[x_nVssMaxRegBuffer];
|
|
va_list marker;
|
|
va_start( marker, pwszPathFormat );
|
|
ft.hr = StringCchVPrintfW( STRING_CCH_PARAM(wszKeyPath), pwszPathFormat, marker );
|
|
va_end( marker );
|
|
if (ft.HrFailed())
|
|
ft.TranslateGenericError(VSSDBG_GEN, ft.hr, L"StringCchVPrintfW()");
|
|
|
|
// Create the key
|
|
BS_ASSERT( (m_samDesired & KEY_WRITE) == KEY_WRITE);
|
|
DWORD dwDisposition = 0;
|
|
HKEY hRegKey = NULL;
|
|
LONG lRes = ::RegCreateKeyExW(
|
|
hAncestorKey, // IN HKEY hKey,
|
|
wszKeyPath, // IN LPCWSTR lpSubKey,
|
|
0, // IN DWORD Reserved,
|
|
REG_NONE, // IN LPWSTR lpClass,
|
|
m_dwKeyCreateOptions, // IN DWORD dwOptions,
|
|
m_samDesired, // IN REGSAM samDesired,
|
|
NULL, // IN LPSECURITY_ATTRIBUTES lpSecurityAttributes,
|
|
&hRegKey, // OUT PHKEY phkResult,
|
|
&dwDisposition // OUT LPDWORD lpdwDisposition
|
|
);
|
|
if ( lRes != ERROR_SUCCESS )
|
|
ft.TranslateGenericError(VSSDBG_GEN, HRESULT_FROM_WIN32(lRes), L"RegCreateKeyExW(%ld,%s,...)",
|
|
hAncestorKey, wszKeyPath);
|
|
|
|
// Check whether we created or opened the key
|
|
switch ( dwDisposition )
|
|
{
|
|
case REG_CREATED_NEW_KEY:
|
|
if (!m_awszKeyPath.CopyFrom(wszKeyPath)) {
|
|
::RegCloseKey( hRegKey );
|
|
ft.Throw( VSSDBG_GEN, E_OUTOFMEMORY, L"Memory allocation error");
|
|
}
|
|
Close();
|
|
m_hRegKey = hRegKey;
|
|
break;
|
|
case REG_OPENED_EXISTING_KEY:
|
|
::RegCloseKey( hRegKey );
|
|
ft.Throw( VSSDBG_GEN, VSS_E_OBJECT_ALREADY_EXISTS, L"Key %s already exists", wszKeyPath);
|
|
default:
|
|
BS_ASSERT( false );
|
|
if (hRegKey && (hRegKey != INVALID_HANDLE_VALUE))
|
|
::RegCloseKey( hRegKey );
|
|
ft.TranslateGenericError(VSSDBG_GEN, E_UNEXPECTED, L"RegCreateKeyExW(%ld,%s,...,[%lu])",
|
|
hAncestorKey, wszKeyPath, dwDisposition);
|
|
}
|
|
}
|
|
|
|
|
|
// Opens a registry key.
|
|
bool CVssRegistryKey::Open(
|
|
IN HKEY hAncestorKey,
|
|
IN LPCWSTR pwszPathFormat,
|
|
IN ...
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssRegistryKey::Open" );
|
|
|
|
BS_ASSERT(hAncestorKey);
|
|
BS_ASSERT(pwszPathFormat);
|
|
|
|
// Build the path to the key
|
|
WCHAR wszKeyPath[x_nVssMaxRegBuffer];
|
|
va_list marker;
|
|
va_start( marker, pwszPathFormat );
|
|
ft.hr = StringCchVPrintfW( STRING_CCH_PARAM(wszKeyPath), pwszPathFormat, marker );
|
|
va_end( marker );
|
|
if (ft.HrFailed())
|
|
ft.TranslateGenericError(VSSDBG_GEN, ft.hr, L"StringCchVPrintfW()");
|
|
|
|
// Open the key
|
|
HKEY hRegKey = NULL;
|
|
LONG lRes = ::RegOpenKeyExW(
|
|
hAncestorKey, // IN HKEY hKey,
|
|
wszKeyPath, // IN LPCWSTR lpSubKey,
|
|
0, // IN DWORD dwOptions,
|
|
m_samDesired, // IN REGSAM samDesired,
|
|
&hRegKey // OUT PHKEY phkResult
|
|
);
|
|
if ( lRes == ERROR_FILE_NOT_FOUND )
|
|
return false;
|
|
|
|
if ( lRes != ERROR_SUCCESS )
|
|
ft.TranslateGenericError(VSSDBG_GEN, HRESULT_FROM_WIN32(lRes), L"RegOpenKeyExW(%ld,%s,...)",
|
|
hAncestorKey, wszKeyPath);
|
|
|
|
if (!m_awszKeyPath.CopyFrom(wszKeyPath)) {
|
|
::RegCloseKey( hRegKey );
|
|
ft.Throw( VSSDBG_GEN, E_OUTOFMEMORY, L"Memory allocation error");
|
|
}
|
|
|
|
Close();
|
|
m_hRegKey = hRegKey;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// Recursively deletes a subkey.
|
|
// Throws an error if the subkey does not exist
|
|
void CVssRegistryKey::DeleteSubkey(
|
|
IN LPCWSTR pwszPathFormat,
|
|
IN ...
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssRegistryKey::DeleteSubkey" );
|
|
|
|
BS_ASSERT(pwszPathFormat);
|
|
|
|
// Build the path to the key
|
|
WCHAR wszKeyPath[x_nVssMaxRegBuffer];
|
|
va_list marker;
|
|
va_start( marker, pwszPathFormat );
|
|
ft.hr = StringCchVPrintfW( STRING_CCH_PARAM(wszKeyPath), pwszPathFormat, marker );
|
|
va_end( marker );
|
|
if (ft.HrFailed())
|
|
ft.TranslateGenericError(VSSDBG_GEN, ft.hr, L"StringCchVPrintfW()");
|
|
|
|
// Recursively delete the key
|
|
DWORD dwRes = ::SHDeleteKey(
|
|
m_hRegKey, // IN HKEY hKey,
|
|
wszKeyPath // IN LPCTSTR pszSubKey
|
|
);
|
|
if ( dwRes == ERROR_FILE_NOT_FOUND )
|
|
ft.Throw( VSSDBG_GEN, VSS_E_OBJECT_NOT_FOUND, L"Key with path %s\\%s not found",
|
|
m_awszKeyPath.GetRef(), wszKeyPath);
|
|
if ( dwRes != ERROR_SUCCESS )
|
|
ft.TranslateGenericError(VSSDBG_GEN, HRESULT_FROM_WIN32(dwRes), L"SHDeleteKey(%ld[%s],%s)",
|
|
m_hRegKey, m_awszKeyPath.GetRef(), wszKeyPath);
|
|
}
|
|
|
|
|
|
// Deletes a value.
|
|
// Throws an error if the value does not exist
|
|
void CVssRegistryKey::DeleteValue(
|
|
IN LPCWSTR pwszValueName
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssRegistryKey::DeleteValue" );
|
|
|
|
BS_ASSERT(pwszValueName);
|
|
|
|
// Recursively delete the key
|
|
LONG lRes = ::RegDeleteValue(
|
|
m_hRegKey, // IN HKEY hKey,
|
|
pwszValueName // IN LPCTSTR pwszValueName
|
|
);
|
|
if ( lRes == ERROR_FILE_NOT_FOUND )
|
|
ft.Throw( VSSDBG_GEN, VSS_E_OBJECT_NOT_FOUND, L"Value %s\\%s not found",
|
|
m_awszKeyPath.GetRef(), pwszValueName);
|
|
if ( lRes != ERROR_SUCCESS )
|
|
ft.TranslateGenericError(VSSDBG_GEN, HRESULT_FROM_WIN32(lRes), L"RegDeleteValue(%ld[%s],%s)",
|
|
m_hRegKey, m_awszKeyPath.GetRef(), pwszValueName);
|
|
}
|
|
|
|
|
|
// Adds a LONGLONG value to the registry key
|
|
void CVssRegistryKey::SetValue(
|
|
IN LPCWSTR pwszValueName,
|
|
IN LONGLONG llValue
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssRegistryKey::SetValue_LONGLONG" );
|
|
|
|
// Convert the value to a string
|
|
WCHAR wszValue[x_nVssMaxRegNumBuffer];
|
|
ft.hr = StringCchPrintfW( STRING_CCH_PARAM(wszValue),
|
|
L"%I64d", llValue );
|
|
if (ft.HrFailed())
|
|
ft.TranslateGenericError( VSSDBG_GEN, ft.hr, L"StringCchPrintfW()");
|
|
|
|
// Set the value as string
|
|
SetValue(pwszValueName, wszValue);
|
|
}
|
|
|
|
|
|
// Adds a REG_SZ value to the registry key
|
|
void CVssRegistryKey::SetValue(
|
|
IN LPCWSTR pwszValueName,
|
|
IN LPCWSTR pwszValue,
|
|
IN REGSAM eSzType /* = REG_SZ */
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssRegistryKey::SetValue_PWSZ" );
|
|
|
|
// Assert paramters
|
|
BS_ASSERT(pwszValueName);
|
|
BS_ASSERT(pwszValue);
|
|
|
|
BS_ASSERT(m_hRegKey);
|
|
|
|
BS_ASSERT((eSzType == REG_SZ) || (eSzType == REG_EXPAND_SZ));
|
|
|
|
// Set the value
|
|
DWORD dwLength = ::lstrlenW( pwszValue );
|
|
LONG lRes = ::RegSetValueExW(
|
|
m_hRegKey, // IN HKEY hKey,
|
|
pwszValueName, // IN LPCWSTR lpValueName,
|
|
0, // IN DWORD Reserved,
|
|
eSzType, // IN DWORD dwType,
|
|
(CONST BYTE*)pwszValue, // IN CONST BYTE* lpData,
|
|
(dwLength + 1) * sizeof(WCHAR) // IN DWORD cbData
|
|
);
|
|
if ( lRes != ERROR_SUCCESS )
|
|
ft.TranslateGenericError(VSSDBG_GEN, HRESULT_FROM_WIN32(lRes),
|
|
L"RegSetValueExW(0x%08lx,%s,0,REG_SZ,%s.%d)",
|
|
m_hRegKey, m_awszKeyPath.GetRef(), pwszValue, (dwLength + 1) * sizeof(WCHAR));
|
|
}
|
|
|
|
|
|
// Adds a REG_DWORD value to the registry key
|
|
void CVssRegistryKey::SetValue(
|
|
IN LPCWSTR pwszValueName,
|
|
IN DWORD dwValue
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssRegistryKey::SetValue_DWORD" );
|
|
|
|
// Assert paramters
|
|
BS_ASSERT(pwszValueName);
|
|
|
|
BS_ASSERT(m_hRegKey);
|
|
|
|
// Set the value
|
|
LONG lRes = ::RegSetValueExW(
|
|
m_hRegKey, // IN HKEY hKey,
|
|
pwszValueName, // IN LPCWSTR lpValueName,
|
|
0, // IN DWORD Reserved,
|
|
REG_DWORD, // IN DWORD dwType,
|
|
(CONST BYTE*)&dwValue, // IN CONST BYTE* lpData,
|
|
sizeof(DWORD) // IN DWORD cbData
|
|
);
|
|
if ( lRes != ERROR_SUCCESS )
|
|
ft.TranslateGenericError(VSSDBG_GEN, HRESULT_FROM_WIN32(lRes),
|
|
L"RegSetValueExW(0x%08lx,%s,0,REG_DWORD,%ld)",
|
|
m_hRegKey, m_awszKeyPath.GetRef(), dwValue);
|
|
}
|
|
|
|
|
|
// Adds a REG_MULTI_SZ value to the registry key
|
|
// WARNING: do not call this routine for a REG_SZ value!
|
|
// (Intentionally this is a different function so that it won't
|
|
// be confused with CVssRegistryKey::SetValue)
|
|
void CVssRegistryKey::SetMultiszValue(
|
|
IN LPCWSTR pwszValueName,
|
|
IN LPCWSTR pwszValue
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssRegistryKey::SetMultiszValue" );
|
|
|
|
// Assert paramters
|
|
BS_ASSERT(pwszValueName);
|
|
BS_ASSERT(pwszValue);
|
|
|
|
BS_ASSERT(m_hRegKey);
|
|
|
|
// Get the length of the multi-sz string, including the zero character for each string
|
|
DWORD dwLength = 0;
|
|
LPCWSTR pwszCurrent = pwszValue;
|
|
do
|
|
{
|
|
// Add the zero character
|
|
LONG dwCurrentLength = ::lstrlenW(pwszCurrent) + 1;
|
|
dwLength += dwCurrentLength;
|
|
pwszCurrent += dwCurrentLength;
|
|
}
|
|
while(pwszCurrent[0]);
|
|
|
|
// Add the final zero character
|
|
dwLength += 1;
|
|
|
|
// Set the value
|
|
LONG lRes = ::RegSetValueExW(
|
|
m_hRegKey, // IN HKEY hKey,
|
|
pwszValueName, // IN LPCWSTR lpValueName,
|
|
0, // IN DWORD Reserved,
|
|
REG_MULTI_SZ, // IN DWORD dwType,
|
|
(CONST BYTE*)pwszValue, // IN CONST BYTE* lpData,
|
|
(dwLength) * sizeof(WCHAR) // IN DWORD cbData
|
|
);
|
|
if ( lRes != ERROR_SUCCESS )
|
|
ft.TranslateGenericError(VSSDBG_GEN, HRESULT_FROM_WIN32(lRes),
|
|
L"RegSetValueExW(0x%08lx,%s,0,REG_SZ,%s.%d)",
|
|
m_hRegKey, m_awszKeyPath.GetRef(), pwszValue, (dwLength) * sizeof(WCHAR));
|
|
}
|
|
|
|
|
|
// Adds a binary value to the registry key
|
|
void CVssRegistryKey::SetBinaryValue(
|
|
IN LPCWSTR pwszValueName,
|
|
IN BYTE * pbData,
|
|
IN DWORD dwSize
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssRegistryKey::SetBinaryValue" );
|
|
|
|
// Assert paramters
|
|
BS_ASSERT(pwszValueName);
|
|
BS_ASSERT(pbData);
|
|
BS_ASSERT(dwSize);
|
|
|
|
BS_ASSERT(m_hRegKey);
|
|
|
|
// Set the value
|
|
LONG lRes = ::RegSetValueExW(
|
|
m_hRegKey, // IN HKEY hKey,
|
|
pwszValueName, // IN LPCWSTR lpValueName,
|
|
0, // IN DWORD Reserved,
|
|
REG_BINARY, // IN DWORD dwType,
|
|
pbData, // IN CONST BYTE* lpData,
|
|
dwSize // IN DWORD cbData
|
|
);
|
|
if ( lRes != ERROR_SUCCESS )
|
|
ft.TranslateGenericError(VSSDBG_GEN, HRESULT_FROM_WIN32(lRes),
|
|
L"RegSetValueExW(0x%08lx,%s,0,REG_BINARY,%p.%lu)",
|
|
m_hRegKey, m_awszKeyPath.GetRef(), pbData, dwSize);
|
|
}
|
|
|
|
|
|
// Reads a VSS_PWSZ value from the registry key
|
|
bool CVssRegistryKey::GetValue(
|
|
IN LPCWSTR pwszValueName,
|
|
OUT VSS_PWSZ & pwszValue,
|
|
IN bool bThrowIfNotFound /* = true */
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssRegistryKey::GetValue_PWSZ" );
|
|
|
|
// Assert parameters
|
|
BS_ASSERT(pwszValueName);
|
|
BS_ASSERT(pwszValue == NULL);
|
|
|
|
// Reset the OUT parameter
|
|
pwszValue = NULL;
|
|
|
|
// Get the value length (we suppose that doesn't change)
|
|
DWORD dwType = 0;
|
|
DWORD dwSizeInBytes = 0;
|
|
LONG lResult = ::RegQueryValueExW(
|
|
m_hRegKey,
|
|
pwszValueName,
|
|
NULL,
|
|
&dwType,
|
|
NULL,
|
|
&dwSizeInBytes);
|
|
if (lResult == ERROR_FILE_NOT_FOUND)
|
|
{
|
|
if (bThrowIfNotFound)
|
|
{
|
|
ft.LogGenericWarning(VSSDBG_GEN,
|
|
L"RegQueryValueExW(0x%08lx(%s),%s,0,[%lx],0,[%lu]) => ERROR_FILE_NOT_FOUND",
|
|
m_hRegKey, m_awszKeyPath.GetRef(), pwszValueName, dwType, dwSizeInBytes);
|
|
ft.Throw( VSSDBG_GEN, VSS_E_OBJECT_NOT_FOUND, L"Registry key not found");
|
|
}
|
|
else
|
|
return ft.Exit(false);
|
|
}
|
|
if ((lResult != ERROR_SUCCESS) && (lResult != ERROR_MORE_DATA))
|
|
ft.TranslateGenericError(VSSDBG_GEN, HRESULT_FROM_WIN32(lResult),
|
|
L"RegQueryValueExW(0x%08lx(%s),%s,0,[%lx],0,[%lu])",
|
|
m_hRegKey, m_awszKeyPath.GetRef(), pwszValueName, dwType, dwSizeInBytes);
|
|
|
|
// Check the type and the size
|
|
if (dwType != REG_SZ && dwType != REG_EXPAND_SZ)
|
|
ft.Throw(VSSDBG_GEN, E_UNEXPECTED, L"Unexpected type %lu for a string value 0x%08lx(%s),%s",
|
|
dwType, m_hRegKey, m_awszKeyPath.GetRef(), pwszValueName );
|
|
BS_ASSERT(dwSizeInBytes);
|
|
|
|
// Allocate the buffer
|
|
CVssAutoPWSZ awszValue;
|
|
DWORD dwSizeOfString = dwSizeInBytes/sizeof(WCHAR);
|
|
awszValue.Allocate(dwSizeOfString);
|
|
|
|
// Get the string contents
|
|
DWORD dwType2 = 0;
|
|
DWORD dwSizeInBytes2 = dwSizeOfString * (sizeof(WCHAR));
|
|
BS_ASSERT( dwSizeInBytes2 == dwSizeInBytes);
|
|
lResult = ::RegQueryValueExW(
|
|
m_hRegKey,
|
|
pwszValueName,
|
|
NULL,
|
|
&dwType2,
|
|
(LPBYTE)awszValue.GetRef(),
|
|
&dwSizeInBytes2);
|
|
if (lResult != ERROR_SUCCESS)
|
|
ft.TranslateGenericError(VSSDBG_GEN, HRESULT_FROM_WIN32(lResult),
|
|
L"RegQueryValueExW(0x%08lx(%s),%s,0,[%lx],0,[%lu])",
|
|
m_hRegKey, m_awszKeyPath.GetRef(), pwszValueName, dwType2, dwSizeInBytes2);
|
|
BS_ASSERT(dwType2 == REG_SZ || dwType2 == REG_EXPAND_SZ);
|
|
BS_ASSERT(dwSizeInBytes2 == dwSizeInBytes);
|
|
(awszValue.GetRef())[dwSizeOfString] = L'\0';
|
|
|
|
// Set the OUT parameter
|
|
pwszValue = awszValue.Detach();
|
|
|
|
return ft.Exit(true);
|
|
}
|
|
|
|
|
|
// Reads a LONGLONG value from the registry key
|
|
bool CVssRegistryKey::GetValue(
|
|
IN LPCWSTR pwszValueName,
|
|
OUT LONGLONG & llValue,
|
|
IN bool bThrowIfNotFound /* = true */
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssRegistryKey::GetValue_LONGLONG" );
|
|
|
|
// Assert parameters
|
|
BS_ASSERT(pwszValueName);
|
|
|
|
CVssAutoPWSZ awszValue;
|
|
bool bResult = GetValue(pwszValueName, awszValue.GetRef(), bThrowIfNotFound);
|
|
if (!bResult)
|
|
{
|
|
BS_ASSERT(!bThrowIfNotFound);
|
|
return ft.Exit(false);
|
|
}
|
|
|
|
BS_ASSERT(awszValue.GetRef());
|
|
BS_ASSERT(awszValue.GetRef()[0]);
|
|
|
|
// Read the LONGLONG string
|
|
llValue = ::_wtoi64(awszValue);
|
|
|
|
return ft.Exit(true);
|
|
}
|
|
|
|
|
|
// Reads a DWORD value from the registry key
|
|
bool CVssRegistryKey::GetValue(
|
|
IN LPCWSTR pwszValueName,
|
|
OUT DWORD & dwValue,
|
|
IN bool bThrowIfNotFound /* = true */
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssRegistryKey::GetValue_DWORD" );
|
|
|
|
// Assert parameters
|
|
BS_ASSERT(pwszValueName);
|
|
|
|
// Reset the OUT parameter
|
|
dwValue = 0;
|
|
|
|
// Get the value length (we suppose that doesn't change)
|
|
DWORD dwType = 0;
|
|
DWORD dwSizeInBytes = 0;
|
|
LONG lResult = ::RegQueryValueExW(
|
|
m_hRegKey,
|
|
pwszValueName,
|
|
NULL,
|
|
&dwType,
|
|
NULL,
|
|
&dwSizeInBytes);
|
|
if (lResult == ERROR_FILE_NOT_FOUND)
|
|
{
|
|
if (bThrowIfNotFound)
|
|
{
|
|
ft.LogGenericWarning(VSSDBG_GEN,
|
|
L"RegQueryValueExW(0x%08lx(%s),%s,0,[%lx],0,[%lu]) => ERROR_FILE_NOT_FOUND",
|
|
m_hRegKey, m_awszKeyPath.GetRef(), pwszValueName, dwType, dwSizeInBytes);
|
|
ft.Throw( VSSDBG_GEN, VSS_E_OBJECT_NOT_FOUND, L"Registry key not found");
|
|
}
|
|
else
|
|
return ft.Exit(false);
|
|
}
|
|
if ((lResult != ERROR_SUCCESS) && (lResult != ERROR_MORE_DATA))
|
|
ft.TranslateGenericError(VSSDBG_GEN, HRESULT_FROM_WIN32(lResult),
|
|
L"RegQueryValueExW(0x%08lx(%s),%s,0,[%lx],0,[%lu])",
|
|
m_hRegKey, m_awszKeyPath.GetRef(), pwszValueName, dwType, dwSizeInBytes);
|
|
|
|
// Check the type and the size
|
|
if (dwType != REG_DWORD)
|
|
ft.Throw(VSSDBG_GEN, E_UNEXPECTED, L"Unexpected type %lu for a DWORD value 0x%08lx(%s),%s",
|
|
dwType, m_hRegKey, m_awszKeyPath.GetRef(), pwszValueName );
|
|
BS_ASSERT(dwSizeInBytes == sizeof(DWORD));
|
|
|
|
// Get the string contents
|
|
DWORD dwType2 = 0;
|
|
DWORD dwSizeInBytes2 = dwSizeInBytes;
|
|
DWORD dwReadValue = 0;
|
|
lResult = ::RegQueryValueExW(
|
|
m_hRegKey,
|
|
pwszValueName,
|
|
NULL,
|
|
&dwType2,
|
|
(LPBYTE)&dwReadValue,
|
|
&dwSizeInBytes2);
|
|
if (lResult != ERROR_SUCCESS)
|
|
ft.TranslateGenericError(VSSDBG_GEN, HRESULT_FROM_WIN32(lResult),
|
|
L"RegQueryValueExW(0x%08lx(%s),%s,0,[%lx],0,[%lu])",
|
|
m_hRegKey, m_awszKeyPath.GetRef(), pwszValueName, dwType2, dwSizeInBytes2);
|
|
BS_ASSERT(dwType2 == REG_DWORD);
|
|
BS_ASSERT(dwSizeInBytes2 == dwSizeInBytes);
|
|
|
|
dwValue = dwReadValue;
|
|
|
|
return ft.Exit(true);
|
|
}
|
|
|
|
|
|
// Reads a REG_BINARY value from the registry key
|
|
bool CVssRegistryKey::GetBinaryValue(
|
|
IN LPCWSTR pwszValueName,
|
|
OUT BYTE* & pbData,
|
|
OUT DWORD & dwSize,
|
|
IN bool bThrowIfNotFound /* = true */
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssRegistryKey::GetBinaryValue" );
|
|
|
|
// Assert parameters
|
|
BS_ASSERT(pwszValueName);
|
|
BS_ASSERT(pbData == NULL);
|
|
BS_ASSERT(dwSize == NULL);
|
|
|
|
// Reset the OUT parameters
|
|
pbData = NULL;
|
|
dwSize = 0;
|
|
|
|
// Get the value length (we suppose that doesn't change)
|
|
DWORD dwType = 0;
|
|
DWORD dwSizeInBytes = 0;
|
|
LONG lResult = ::RegQueryValueExW(
|
|
m_hRegKey,
|
|
pwszValueName,
|
|
NULL,
|
|
&dwType,
|
|
NULL,
|
|
&dwSizeInBytes);
|
|
if (lResult == ERROR_FILE_NOT_FOUND)
|
|
{
|
|
if (bThrowIfNotFound)
|
|
{
|
|
ft.LogGenericWarning(VSSDBG_GEN,
|
|
L"RegQueryValueExW(0x%08lx(%s),%s,0,[%lx],0,[%lu]) => ERROR_FILE_NOT_FOUND",
|
|
m_hRegKey, m_awszKeyPath.GetRef(), pwszValueName, dwType, dwSizeInBytes);
|
|
ft.Throw( VSSDBG_GEN, VSS_E_OBJECT_NOT_FOUND, L"Registry key not found");
|
|
}
|
|
else
|
|
return ft.Exit(false);
|
|
}
|
|
if ((lResult != ERROR_SUCCESS) && (lResult != ERROR_MORE_DATA))
|
|
ft.TranslateGenericError(VSSDBG_GEN, HRESULT_FROM_WIN32(lResult),
|
|
L"RegQueryValueExW(0x%08lx(%s),%s,0,[%lx],0,[%lu])",
|
|
m_hRegKey, m_awszKeyPath.GetRef(), pwszValueName, dwType, dwSizeInBytes);
|
|
|
|
// Check the type and the size
|
|
if (dwType != REG_BINARY)
|
|
ft.Throw(VSSDBG_GEN, E_UNEXPECTED, L"Unexpected type %lu for a binary value 0x%08lx(%s),%s",
|
|
dwType, m_hRegKey, m_awszKeyPath.GetRef(), pwszValueName );
|
|
BS_ASSERT(dwSizeInBytes);
|
|
|
|
// Allocate the buffer
|
|
BS_ASSERT(dwSizeInBytes != 0);
|
|
std::auto_ptr<BYTE> pBuffer(new BYTE[dwSizeInBytes]);
|
|
if (pBuffer.get() == NULL)
|
|
ft.Throw(VSSDBG_GEN, E_OUTOFMEMORY, L"Memory allocation error");
|
|
|
|
// Get the binary value contents
|
|
DWORD dwType2 = 0;
|
|
DWORD dwSizeInBytes2 = dwSizeInBytes;
|
|
lResult = ::RegQueryValueExW(
|
|
m_hRegKey,
|
|
pwszValueName,
|
|
NULL,
|
|
&dwType2,
|
|
pBuffer.get(),
|
|
&dwSizeInBytes2);
|
|
if (lResult != ERROR_SUCCESS)
|
|
ft.TranslateGenericError(VSSDBG_GEN, HRESULT_FROM_WIN32(lResult),
|
|
L"RegQueryValueExW(0x%08lx(%s),%s,0,[%lx],0,[%lu])",
|
|
m_hRegKey, m_awszKeyPath.GetRef(), pwszValueName, dwType2, dwSizeInBytes2);
|
|
BS_ASSERT(dwType2 == REG_BINARY);
|
|
BS_ASSERT(dwSizeInBytes2 == dwSizeInBytes);
|
|
|
|
// Set the OUT parameters
|
|
pbData = pBuffer.release();
|
|
dwSize = dwSizeInBytes;
|
|
|
|
return ft.Exit(true);
|
|
}
|
|
|
|
|
|
// Closes the registry key
|
|
void CVssRegistryKey::Close()
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssRegistryKey::Close" );
|
|
|
|
if (m_hRegKey) {
|
|
// Close the opened key
|
|
LONG lRes = ::RegCloseKey( m_hRegKey );
|
|
if (lRes != ERROR_SUCCESS) {
|
|
BS_ASSERT(false);
|
|
ft.Trace( VSSDBG_GEN, L"%s: Error on closing key with name %s. lRes == 0x%08lx", (VSS_PWSZ)m_awszKeyPath, lRes );
|
|
}
|
|
m_hRegKey = NULL;
|
|
}
|
|
m_awszKeyPath.Clear();
|
|
}
|
|
|
|
|
|
// Standard constructor
|
|
CVssRegistryKey::CVssRegistryKey(
|
|
IN REGSAM samDesired, /* = KEY_ALL_ACCESS */
|
|
IN DWORD dwKeyCreateOptions /* = REG_OPTION_NON_VOLATILE */
|
|
)
|
|
{
|
|
m_hRegKey = NULL;
|
|
m_dwKeyCreateOptions = dwKeyCreateOptions;
|
|
m_samDesired = samDesired;
|
|
}
|
|
|
|
|
|
// Standard destructor
|
|
CVssRegistryKey::~CVssRegistryKey()
|
|
{
|
|
Close();
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CVssRegistryKeyIterator implementation
|
|
|
|
|
|
|
|
// Standard constructor
|
|
CVssRegistryKeyIterator::CVssRegistryKeyIterator()
|
|
{
|
|
// Initialize data members
|
|
Detach();
|
|
}
|
|
|
|
|
|
// Returns the name of the current key.
|
|
// The returned key is always non-NULL (or the function will throw E_UNEXPECTED).
|
|
VSS_PWSZ CVssRegistryKeyIterator::GetCurrentKeyName() throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssRegistryKeyIterator::GetCurrentKeyName" );
|
|
|
|
if (!m_bAttached || !m_awszSubKeyName.GetRef()) {
|
|
BS_ASSERT(false);
|
|
ft.Throw(VSSDBG_GEN, E_UNEXPECTED, L"Unexpected error: noninitialized iterator");
|
|
}
|
|
|
|
// Fill wszSubKeyName with the name of the subkey
|
|
FILETIME time;
|
|
DWORD dwSize = m_dwMaxSubKeyLen;
|
|
LONG lRes = ::RegEnumKeyExW(
|
|
m_hParentKey, // IN HKEY hKey,
|
|
m_dwCurrentKeyIndex, // IN DWORD dwIndex,
|
|
m_awszSubKeyName, // OUT LPWSTR lpName,
|
|
&dwSize, // IN OUT LPDWORD lpcbName,
|
|
NULL, // IN LPDWORD lpReserved,
|
|
NULL, // IN OUT LPWSTR lpClass,
|
|
NULL, // IN OUT LPDWORD lpcbClass,
|
|
&time); // OUT PFILETIME lpftLastWriteTime
|
|
switch(lRes)
|
|
{
|
|
case ERROR_SUCCESS:
|
|
BS_ASSERT(dwSize != 0);
|
|
break; // Go to Next key
|
|
default:
|
|
ft.TranslateGenericError( VSSDBG_GEN, HRESULT_FROM_WIN32(lRes), L"RegEnumKeyExW(%p,%lu,%p,%lu ...)",
|
|
m_hParentKey, m_dwCurrentKeyIndex, m_awszSubKeyName.GetRef(), dwSize);
|
|
case ERROR_NO_MORE_ITEMS:
|
|
BS_ASSERT(false);
|
|
ft.Throw(VSSDBG_GEN, E_UNEXPECTED, L"Unexpected error: dwIndex out of scope %lu %lu", m_dwCurrentKeyIndex, m_dwKeyCount);
|
|
}
|
|
|
|
return m_awszSubKeyName.GetRef();
|
|
}
|
|
|
|
|
|
// Standard constructor
|
|
void CVssRegistryKeyIterator::Attach(
|
|
IN CVssRegistryKey & key
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssRegistryKeyIterator::Attach" );
|
|
|
|
// Reset all members
|
|
Detach();
|
|
m_hParentKey = key.GetHandle();
|
|
BS_ASSERT(m_hParentKey);
|
|
|
|
// Get the number of subkeys and the max subkey length
|
|
DWORD dwKeyCount = 0;
|
|
DWORD dwMaxSubKeyLen = 0;
|
|
LONG lRes = ::RegQueryInfoKeyW(
|
|
m_hParentKey, // handle to key
|
|
NULL, // class buffer
|
|
NULL, // size of class buffer
|
|
NULL, // reserved
|
|
&dwKeyCount, // number of subkeys
|
|
&dwMaxSubKeyLen, // longest subkey name
|
|
NULL, // longest class string
|
|
NULL, // number of value entries
|
|
NULL, // longest value name
|
|
NULL, // longest value data
|
|
NULL, // descriptor length
|
|
NULL); // last write time
|
|
if (lRes != ERROR_SUCCESS)
|
|
ft.TranslateGenericError( VSSDBG_GEN, HRESULT_FROM_WIN32(lRes), L"RegQueryInfoKeyW(%p, ...)", m_hParentKey);
|
|
|
|
// Allocate the key name with a sufficient length.
|
|
// We assume that the key length cannot change during the ennumeration).
|
|
if (dwMaxSubKeyLen)
|
|
m_awszSubKeyName.Allocate(dwMaxSubKeyLen);
|
|
|
|
// Setting the number of subkeys
|
|
m_dwKeyCount = dwKeyCount;
|
|
m_dwMaxSubKeyLen = dwMaxSubKeyLen + 1;
|
|
|
|
// Attachment completed
|
|
m_bAttached = true;
|
|
}
|
|
|
|
|
|
void CVssRegistryKeyIterator::Detach()
|
|
{
|
|
// Initialize data members
|
|
m_hParentKey = NULL;
|
|
m_dwKeyCount = 0;
|
|
m_dwCurrentKeyIndex = 0;
|
|
m_dwMaxSubKeyLen = 0;
|
|
m_awszSubKeyName.Clear();
|
|
m_bAttached = false;
|
|
}
|
|
|
|
|
|
// Tells if the current key is still valid
|
|
bool CVssRegistryKeyIterator::IsEOF()
|
|
{
|
|
return (m_dwCurrentKeyIndex >= m_dwKeyCount);
|
|
}
|
|
|
|
|
|
// Return the number of subkeys at the moment of attaching
|
|
DWORD CVssRegistryKeyIterator::GetSubkeysCount()
|
|
{
|
|
return (m_dwKeyCount);
|
|
}
|
|
|
|
|
|
// Set the next key as being the current one in the enumeration
|
|
void CVssRegistryKeyIterator::MoveNext()
|
|
{
|
|
if (!IsEOF())
|
|
m_dwCurrentKeyIndex++;
|
|
else
|
|
BS_ASSERT(false);
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CVssRegistryValueIterator implementation
|
|
|
|
|
|
|
|
// Standard constructor
|
|
CVssRegistryValueIterator::CVssRegistryValueIterator()
|
|
{
|
|
// Initialize data members
|
|
Detach();
|
|
}
|
|
|
|
|
|
// Returns the name of the current value.
|
|
// The returned value is always non-NULL (or the function will throw E_UNEXPECTED).
|
|
VSS_PWSZ CVssRegistryValueIterator::GetCurrentValueName() throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssRegistryValueIterator::GetCurrentValueName" );
|
|
|
|
ReadCurrentValueDetails();
|
|
BS_ASSERT(m_bSeekDone);
|
|
return m_awszValueName.GetRef();
|
|
}
|
|
|
|
|
|
// Returns the type of the current value.
|
|
DWORD CVssRegistryValueIterator::GetCurrentValueType() throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssRegistryValueIterator::GetCurrentValueType" );
|
|
|
|
ReadCurrentValueDetails();
|
|
BS_ASSERT(m_bSeekDone);
|
|
return m_dwCurrentValueType;
|
|
}
|
|
|
|
|
|
// Reads a VSS_PWSZ value from the registry key
|
|
void CVssRegistryValueIterator::GetCurrentValueContent(
|
|
OUT VSS_PWSZ & pwszValue
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssRegistryValueIterator::GetCurrentValueContent" );
|
|
|
|
ReadCurrentValueDetails();
|
|
BS_ASSERT(m_bSeekDone);
|
|
|
|
if (m_dwCurrentValueType != REG_SZ) {
|
|
BS_ASSERT(false);
|
|
ft.Throw(VSSDBG_GEN, E_UNEXPECTED, L"Unexpected error: attempting to read a value of the wrong type");
|
|
}
|
|
|
|
// Allocate the output buffer
|
|
DWORD cchStringSize = m_cbValueDataSize / sizeof(WCHAR);
|
|
CVssAutoPWSZ awszBuffer;
|
|
awszBuffer.Allocate(cchStringSize + 1);
|
|
|
|
// Fill awszBuffer with the string contents
|
|
DWORD cchNameSize = m_cchMaxValueNameLen;
|
|
DWORD dwCurrentValueType = 0;
|
|
DWORD cbValueSize = m_cbValueDataSize;
|
|
LONG lRes = ::RegEnumValue(
|
|
m_hKey, // IN HKEY hKey,
|
|
m_dwCurrentValueIndex, // IN DWORD dwIndex,
|
|
m_awszValueName, // OUT LPWSTR lpName,
|
|
&cchNameSize, // IN OUT LPDWORD lpcbName,
|
|
NULL, // IN LPDWORD lpReserved,
|
|
&dwCurrentValueType, // IN OUT LPDWORD lpType,
|
|
(LPBYTE)awszBuffer.GetRef(),// IN OUT LPBYTE lpData,
|
|
&cbValueSize); // IN OUT LPDWORD lpcbData
|
|
switch(lRes)
|
|
{
|
|
case ERROR_SUCCESS:
|
|
BS_ASSERT(cchNameSize != 0);
|
|
BS_ASSERT(cbValueSize != 0);
|
|
if (dwCurrentValueType != m_dwCurrentValueType)
|
|
ft.Throw(VSSDBG_GEN, E_UNEXPECTED,
|
|
L"Unexpected error: current value type changed in the meantime %lu %lu",
|
|
dwCurrentValueType, m_dwCurrentValueType);
|
|
break;
|
|
case ERROR_NO_MORE_ITEMS:
|
|
BS_ASSERT(false);
|
|
ft.Throw(VSSDBG_GEN, E_UNEXPECTED, L"Unexpected error: dwIndex out of scope %lu %lu", m_dwCurrentValueIndex, m_dwValuesCount);
|
|
default:
|
|
ft.TranslateGenericError( VSSDBG_GEN, HRESULT_FROM_WIN32(lRes), L"RegEnumValue(%p,%lu,%p,%lu ...)",
|
|
m_hKey, m_dwCurrentValueIndex, m_awszValueName.GetRef(), cchNameSize);
|
|
}
|
|
|
|
awszBuffer.GetRef()[cchStringSize] = L'\0';
|
|
pwszValue = awszBuffer.Detach();
|
|
}
|
|
|
|
|
|
// Reads a DWORD value from the registry key
|
|
void CVssRegistryValueIterator::GetCurrentValueContent(
|
|
OUT DWORD & dwValue
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssRegistryValueIterator::GetCurrentValueContent" );
|
|
|
|
ReadCurrentValueDetails();
|
|
BS_ASSERT(m_bSeekDone);
|
|
|
|
if (m_dwCurrentValueType != REG_DWORD)
|
|
ft.Throw(VSSDBG_GEN, E_UNEXPECTED,
|
|
L"Unexpected error: attempting to read a value of the wrong type");
|
|
|
|
// Allocate the output buffer
|
|
if (m_cbValueDataSize != sizeof(DWORD))
|
|
ft.Throw(VSSDBG_GEN, E_UNEXPECTED,
|
|
L"Unexpected error: unexpected DWORD size [%ld, %ld]", m_cbValueDataSize, sizeof(DWORD));
|
|
|
|
|
|
// Fill dwInternalValue with the DWORD value
|
|
DWORD dwInternalValue = 0;
|
|
DWORD cchNameSize = m_cchMaxValueNameLen;
|
|
DWORD dwCurrentValueType = 0;
|
|
DWORD cbValueSize = m_cbValueDataSize;
|
|
LONG lRes = ::RegEnumValue(
|
|
m_hKey, // IN HKEY hKey,
|
|
m_dwCurrentValueIndex, // IN DWORD dwIndex,
|
|
m_awszValueName, // OUT LPWSTR lpName,
|
|
&cchNameSize, // IN OUT LPDWORD lpcbName,
|
|
NULL, // IN LPDWORD lpReserved,
|
|
&dwCurrentValueType, // IN OUT LPDWORD lpType,
|
|
(LPBYTE)&dwInternalValue, // IN OUT LPBYTE lpData,
|
|
&cbValueSize); // IN OUT LPDWORD lpcbData
|
|
switch(lRes)
|
|
{
|
|
case ERROR_SUCCESS:
|
|
BS_ASSERT(cchNameSize != 0);
|
|
BS_ASSERT(cbValueSize != 0);
|
|
if (dwCurrentValueType != m_dwCurrentValueType)
|
|
ft.Throw(VSSDBG_GEN, E_UNEXPECTED,
|
|
L"Unexpected error: current value type changed in the meantime %lu %lu",
|
|
dwCurrentValueType, m_dwCurrentValueType);
|
|
break;
|
|
case ERROR_NO_MORE_ITEMS:
|
|
BS_ASSERT(false);
|
|
ft.Throw(VSSDBG_GEN, E_UNEXPECTED, L"Unexpected error: dwIndex out of scope %lu %lu", m_dwCurrentValueIndex, m_dwValuesCount);
|
|
default:
|
|
ft.TranslateGenericError( VSSDBG_GEN, HRESULT_FROM_WIN32(lRes), L"RegEnumValue(%p,%lu,%p,%lu ...)",
|
|
m_hKey, m_dwCurrentValueIndex, m_awszValueName.GetRef(), cchNameSize);
|
|
}
|
|
|
|
dwValue = dwInternalValue;
|
|
}
|
|
|
|
|
|
// Reads a REG_BINARY value from the registry key
|
|
void CVssRegistryValueIterator::GetCurrentValueContent(
|
|
OUT PBYTE & pbValue, // Must be deleted with "delete[]"
|
|
OUT DWORD & cbSize
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssRegistryValueIterator::GetCurrentValueContent" );
|
|
|
|
ReadCurrentValueDetails();
|
|
BS_ASSERT(m_bSeekDone);
|
|
|
|
if (m_dwCurrentValueType != REG_BINARY) {
|
|
BS_ASSERT(false);
|
|
ft.Throw(VSSDBG_GEN, E_UNEXPECTED, L"Unexpected error: attempting to read a value of the wrong type");
|
|
}
|
|
|
|
// Allocate the output buffer
|
|
DWORD cbValueSize = m_cbValueDataSize;
|
|
CVssAutoCppPtr<PBYTE> awszBuffer;
|
|
awszBuffer.AllocateBytes(cbValueSize);
|
|
|
|
// Fill awszBuffer with the string contents
|
|
DWORD cchNameSize = m_cchMaxValueNameLen;
|
|
DWORD dwCurrentValueType = 0;
|
|
LONG lRes = ::RegEnumValue(
|
|
m_hKey, // IN HKEY hKey,
|
|
m_dwCurrentValueIndex, // IN DWORD dwIndex,
|
|
m_awszValueName, // OUT LPWSTR lpName,
|
|
&cchNameSize, // IN OUT LPDWORD lpcbName,
|
|
NULL, // IN LPDWORD lpReserved,
|
|
&dwCurrentValueType, // IN OUT LPDWORD lpType,
|
|
(LPBYTE)awszBuffer.Get(), // IN OUT LPBYTE lpData,
|
|
&cbValueSize); // IN OUT LPDWORD lpcbData
|
|
switch(lRes)
|
|
{
|
|
case ERROR_SUCCESS:
|
|
BS_ASSERT(cchNameSize != 0);
|
|
BS_ASSERT(cbValueSize != 0);
|
|
if (dwCurrentValueType != m_dwCurrentValueType)
|
|
ft.Throw(VSSDBG_GEN, E_UNEXPECTED,
|
|
L"Unexpected error: current value type changed in the meantime %lu %lu",
|
|
dwCurrentValueType, m_dwCurrentValueType);
|
|
break;
|
|
case ERROR_NO_MORE_ITEMS:
|
|
BS_ASSERT(false);
|
|
ft.Throw(VSSDBG_GEN, E_UNEXPECTED, L"Unexpected error: dwIndex out of scope %lu %lu", m_dwCurrentValueIndex, m_dwValuesCount);
|
|
default:
|
|
ft.TranslateGenericError( VSSDBG_GEN, HRESULT_FROM_WIN32(lRes), L"RegEnumValue(%p,%lu,%p,%lu ...)",
|
|
m_hKey, m_dwCurrentValueIndex, m_awszValueName.GetRef(), cchNameSize);
|
|
}
|
|
|
|
pbValue = awszBuffer.Detach();
|
|
cbSize = cbValueSize;
|
|
}
|
|
|
|
|
|
// Returns the name and the type of the current value.
|
|
void CVssRegistryValueIterator::ReadCurrentValueDetails() throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssRegistryValueIterator::ReadCurrentValueDetails" );
|
|
|
|
if (!m_bAttached || !m_awszValueName.GetRef()) {
|
|
BS_ASSERT(false);
|
|
ft.Throw(VSSDBG_GEN, E_UNEXPECTED, L"Unexpected error: noninitialized iterator");
|
|
}
|
|
|
|
// Do not load twice the same value
|
|
if (m_bSeekDone)
|
|
return;
|
|
|
|
// Fill wszSubKeyName with the name of the subkey
|
|
DWORD cchNameSize = m_cchMaxValueNameLen;
|
|
LONG lRes = ::RegEnumValue(
|
|
m_hKey, // IN HKEY hKey,
|
|
m_dwCurrentValueIndex, // IN DWORD dwIndex,
|
|
m_awszValueName, // OUT LPWSTR lpName,
|
|
&cchNameSize, // IN OUT LPDWORD lpcbName,
|
|
NULL, // IN LPDWORD lpReserved,
|
|
&m_dwCurrentValueType, // IN OUT LPDWORD lpType,
|
|
NULL, // IN OUT LPBYTE lpData,
|
|
&m_cbValueDataSize); // IN OUT LPDWORD lpcbData
|
|
switch(lRes)
|
|
{
|
|
case ERROR_SUCCESS:
|
|
BS_ASSERT(cchNameSize != 0);
|
|
BS_ASSERT(m_cbValueDataSize != 0);
|
|
break; // Go to Next key
|
|
default:
|
|
ft.TranslateGenericError( VSSDBG_GEN, HRESULT_FROM_WIN32(lRes), L"RegEnumValue(%p,%lu,%p,%lu ...)",
|
|
m_hKey, m_dwCurrentValueIndex, m_awszValueName.GetRef(), cchNameSize);
|
|
case ERROR_NO_MORE_ITEMS:
|
|
BS_ASSERT(false);
|
|
ft.Throw(VSSDBG_GEN, E_UNEXPECTED, L"Unexpected error: dwIndex out of scope %lu %lu", m_dwCurrentValueIndex, m_dwValuesCount);
|
|
}
|
|
|
|
m_bSeekDone = true;
|
|
}
|
|
|
|
|
|
// Attaches the iterator to a registry key
|
|
void CVssRegistryValueIterator::Attach(
|
|
IN CVssRegistryKey & key
|
|
) throw(HRESULT)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssRegistryValueIterator::Attach" );
|
|
|
|
// Reset all members
|
|
Detach();
|
|
m_hKey = key.GetHandle();
|
|
BS_ASSERT(m_hKey);
|
|
|
|
// Get the number of values and the max value name length
|
|
DWORD dwValuesCount = 0;
|
|
DWORD dwMaxValueNameLen = 0;
|
|
LONG lRes = ::RegQueryInfoKeyW(
|
|
m_hKey, // handle to key
|
|
NULL, // class buffer
|
|
NULL, // size of class buffer
|
|
NULL, // reserved
|
|
NULL, // number of subkeys
|
|
NULL, // longest subkey name
|
|
NULL, // longest class string
|
|
&dwValuesCount, // number of value entries
|
|
&dwMaxValueNameLen, // longest value name
|
|
NULL, // longest value data
|
|
NULL, // descriptor length
|
|
NULL); // last write time
|
|
if (lRes != ERROR_SUCCESS)
|
|
ft.TranslateGenericError( VSSDBG_GEN, HRESULT_FROM_WIN32(lRes), L"RegQueryInfoKeyW(%p, ...)", m_hKey);
|
|
|
|
// Allocate the key name with a sufficient length.
|
|
// We assume that the key length cannot change during the ennumeration).
|
|
if (dwMaxValueNameLen)
|
|
m_awszValueName.Allocate(dwMaxValueNameLen);
|
|
|
|
// Setting the number of subkeys
|
|
m_dwValuesCount = dwValuesCount;
|
|
m_cchMaxValueNameLen = dwMaxValueNameLen + 1;
|
|
|
|
// Attachment completed
|
|
m_bAttached = true;
|
|
}
|
|
|
|
|
|
void CVssRegistryValueIterator::Detach()
|
|
{
|
|
// Initialize data members
|
|
m_hKey = NULL;
|
|
m_dwValuesCount = 0;
|
|
m_dwCurrentValueIndex = 0;
|
|
m_cchMaxValueNameLen = 0;
|
|
m_awszValueName.Clear();
|
|
m_dwCurrentValueType = 0;
|
|
m_cbValueDataSize = 0;
|
|
m_bSeekDone = false;
|
|
m_bAttached = false;
|
|
}
|
|
|
|
|
|
// Tells if the current key is still valid
|
|
bool CVssRegistryValueIterator::IsEOF()
|
|
{
|
|
return (m_dwCurrentValueIndex >= m_dwValuesCount);
|
|
}
|
|
|
|
|
|
// Return the number of subkeys at the moment of attaching
|
|
DWORD CVssRegistryValueIterator::GetValuesCount()
|
|
{
|
|
return (m_dwValuesCount);
|
|
}
|
|
|
|
|
|
// Set the next key as being the current one in the enumeration
|
|
void CVssRegistryValueIterator::MoveNext()
|
|
{
|
|
if (!IsEOF())
|
|
{
|
|
m_bSeekDone = false;
|
|
m_dwCurrentValueType = 0;
|
|
m_cbValueDataSize = 0;
|
|
m_dwCurrentValueIndex++;
|
|
}
|
|
else
|
|
BS_ASSERT(false);
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CVssDiag implementation
|
|
|
|
|
|
void CVssDiag::Initialize(
|
|
IN LPCWSTR pwszStaticContext
|
|
)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssDiag::Initialize" );
|
|
|
|
try
|
|
{
|
|
ft.Trace( VSSDBG_GEN, L"Parameters %s", pwszStaticContext);
|
|
|
|
if ((pwszStaticContext == NULL) || (pwszStaticContext[0] == L'\0')) {
|
|
BS_ASSERT(false); // Programming error
|
|
ft.Throw(VSSDBG_GEN, E_UNEXPECTED, L" NULL or empty parameter");
|
|
}
|
|
|
|
// If not initialized, return
|
|
if (m_bInitialized)
|
|
return;
|
|
|
|
// If not enabled, return
|
|
if(!m_key.Open(HKEY_LOCAL_MACHINE, x_wszVssDiagPath)) {
|
|
ft.Trace(VSSDBG_GEN, L"Diagnose not enabled (%s)", pwszStaticContext);
|
|
return;
|
|
}
|
|
|
|
// Try to read the "Enabled" flag. If not present, return.
|
|
CVssAutoPWSZ awszValue;
|
|
if (m_key.GetValue(L"", awszValue.GetRef(), false))
|
|
{
|
|
if (!awszValue.GetRef()) {
|
|
BS_ASSERT(false);
|
|
return;
|
|
}
|
|
|
|
if (wcscmp(awszValue, x_wszVssDiagEnabledValue) != 0)
|
|
return;
|
|
|
|
// Open the key, if exists. If not, create it.
|
|
if (!m_key.Open(HKEY_LOCAL_MACHINE, L"%s\\%s", x_wszVssDiagPath, pwszStaticContext))
|
|
m_key.Create(HKEY_LOCAL_MACHINE, L"%s\\%s", x_wszVssDiagPath, pwszStaticContext);
|
|
|
|
ft.Trace(VSSDBG_GEN, L"Diagnose enabled for (%s)", pwszStaticContext);
|
|
|
|
// Zero out the queued diag data
|
|
::VssZeroOut(m_QueuedDiagData);
|
|
|
|
// Mark the object as "initialized"
|
|
m_bInitialized = true;
|
|
}
|
|
}
|
|
VSS_STANDARD_CATCH(ft)
|
|
}
|
|
|
|
|
|
void CVssDiag::RecordWriterEvent(
|
|
IN VSS_OPERATION eOperation,
|
|
IN DWORD dwEventContext,
|
|
IN DWORD dwCurrentState,
|
|
IN HRESULT hrLastError,
|
|
IN GUID guidSnapshotSetID /* = GUID_NULL */
|
|
)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssDiag::RecordWriterEvent" );
|
|
|
|
RecordGenericEvent(
|
|
eOperation,
|
|
dwEventContext,
|
|
dwCurrentState,
|
|
hrLastError,
|
|
guidSnapshotSetID
|
|
);
|
|
}
|
|
|
|
|
|
void CVssDiag::RecordGenericEvent(
|
|
IN DWORD dwEventID,
|
|
IN DWORD dwEventContext,
|
|
IN DWORD dwCurrentState,
|
|
IN HRESULT hrLastError,
|
|
IN GUID guidSnapshotSetID /* = GUID_NULL */
|
|
)
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssDiag::RecordGenericEvent" );
|
|
|
|
try
|
|
{
|
|
ft.Trace( VSSDBG_GEN, L"Parameters %ld, %ld, %ld, 0x%08lx, " WSTR_GUID_FMT,
|
|
dwEventID, dwEventContext, dwCurrentState, hrLastError,
|
|
GUID_PRINTF_ARG(guidSnapshotSetID));
|
|
|
|
// If not initialized, return
|
|
if (!m_bInitialized)
|
|
return;
|
|
|
|
if (dwEventContext & CVssDiag::VSS_DIAG_IGNORE_LEAVE)
|
|
return;
|
|
|
|
// Get new event parameters
|
|
bool bInOperation = !!(dwEventContext & VSS_DIAG_ENTER_OPERATION);
|
|
LPCWSTR pwszEventName = GetStringFromOperation(bInOperation, dwEventID);
|
|
|
|
// Some events do not require logging
|
|
if (NULL == pwszEventName)
|
|
return;
|
|
|
|
ft.Trace(VSSDBG_GEN, L"Event name: %s", pwszEventName);
|
|
|
|
// Fill out event data
|
|
CVssDiagData data;
|
|
|
|
data.m_dwSize = sizeof(data); // For future compatibility
|
|
data.m_dwReserved = 0;
|
|
|
|
CVsFileTime filetime;
|
|
data.m_llTimestamp = filetime;
|
|
|
|
data.m_dwProcessID = GetCurrentProcessId();
|
|
data.m_dwThreadID = GetCurrentThreadId();
|
|
|
|
data.m_dwEventID = dwEventID;
|
|
data.m_dwEventContext = dwEventContext;
|
|
|
|
data.m_dwCurrentState = dwCurrentState;
|
|
data.m_hrLastErrorCode = hrLastError;
|
|
|
|
data.m_guidSnapshotSetID = guidSnapshotSetID;
|
|
|
|
data.m_pReserved1 = NULL;
|
|
data.m_pReserved2 = NULL;
|
|
|
|
// If we are in queued mode, then add the element to the queue
|
|
if (IsQueuedMode(dwEventID, dwEventContext))
|
|
{
|
|
if (m_dwQueuedElements < x_nMaxQueuedDiagData)
|
|
{
|
|
m_QueuedDiagData[m_dwQueuedElements].m_pwszEventName = pwszEventName;
|
|
m_QueuedDiagData[m_dwQueuedElements].m_diag = data;
|
|
m_dwQueuedElements++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Flush the queue
|
|
FlushQueue();
|
|
|
|
// Write the current data as a binary block
|
|
m_key.SetBinaryValue(pwszEventName, (BYTE *) &data, sizeof(data));
|
|
}
|
|
}
|
|
VSS_STANDARD_CATCH(ft)
|
|
}
|
|
|
|
|
|
// Flush the queued elements in registry
|
|
void CVssDiag::FlushQueue()
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssDiag::FlushQueue" );
|
|
|
|
try
|
|
{
|
|
DWORD dwQueuedElements = m_dwQueuedElements;
|
|
m_dwQueuedElements = 0;
|
|
for ( DWORD dwIndex = 0; dwIndex < dwQueuedElements; dwIndex++)
|
|
{
|
|
// Write the data as a binary block
|
|
LPCWSTR pwszEventName = m_QueuedDiagData[dwIndex].m_pwszEventName;
|
|
CVssDiagData* pDiagData = &(m_QueuedDiagData[dwIndex].m_diag);
|
|
m_key.SetBinaryValue(pwszEventName, (BYTE *) pDiagData, sizeof(CVssDiagData));
|
|
}
|
|
}
|
|
VSS_STANDARD_CATCH(ft)
|
|
}
|
|
|
|
|
|
// Size of the L"VSS_IN_" string, without the zero character
|
|
const x_VssPrefixSize = SIZEOF_ARRAY(L"VSS_IN_") - 1;
|
|
|
|
// Define for simplifying hte case statement below
|
|
#define VSS_OPERATION_CASE_STMT(operation) \
|
|
case operation: \
|
|
if (bInOperation) \
|
|
return x_VssPrefixSize + VSS_WSTRINGIZE(operation) L" (Enter)" ; \
|
|
else \
|
|
return x_VssPrefixSize + VSS_WSTRINGIZE(operation) L" (Leave)" ;
|
|
|
|
|
|
// Define for simplifying hte case statement below
|
|
#define VSS_WRITERSTATE_CASE_STMT(state) \
|
|
case state: \
|
|
if (bInOperation) \
|
|
return VSS_WSTRINGIZE(state) L" (SetCurrentState)" ; \
|
|
else \
|
|
return NULL;
|
|
|
|
|
|
// Define for simplifying hte case statement below
|
|
#define VSS_HRESULT_CASE_STMT(hresult) \
|
|
case hresult: \
|
|
if (bInOperation) \
|
|
return VSS_WSTRINGIZE(hresult) L" (SetCurrentFailure)" ; \
|
|
else \
|
|
return NULL;
|
|
|
|
|
|
// Convert a writer status into a string
|
|
LPCWSTR CVssDiag::GetStringFromOperation(
|
|
IN bool bInOperation,
|
|
IN DWORD dwOperation
|
|
)
|
|
{
|
|
switch (dwOperation)
|
|
{
|
|
// Writer operations
|
|
VSS_OPERATION_CASE_STMT(VSS_IN_IDENTIFY)
|
|
VSS_OPERATION_CASE_STMT(VSS_IN_PREPAREBACKUP)
|
|
VSS_OPERATION_CASE_STMT(VSS_IN_PREPARESNAPSHOT)
|
|
VSS_OPERATION_CASE_STMT(VSS_IN_FREEZE)
|
|
VSS_OPERATION_CASE_STMT(VSS_IN_THAW)
|
|
VSS_OPERATION_CASE_STMT(VSS_IN_POSTSNAPSHOT)
|
|
VSS_OPERATION_CASE_STMT(VSS_IN_BACKUPCOMPLETE)
|
|
VSS_OPERATION_CASE_STMT(VSS_IN_PRERESTORE)
|
|
VSS_OPERATION_CASE_STMT(VSS_IN_POSTRESTORE)
|
|
VSS_OPERATION_CASE_STMT(VSS_IN_GETSTATE)
|
|
VSS_OPERATION_CASE_STMT(VSS_IN_ABORT)
|
|
VSS_OPERATION_CASE_STMT(VSS_IN_BACKUPSHUTDOWN)
|
|
VSS_OPERATION_CASE_STMT(VSS_IN_BKGND_FREEZE_THREAD)
|
|
|
|
// Other Event IDs
|
|
VSS_OPERATION_CASE_STMT(VSS_IN_OPEN_VOLUME_HANDLE)
|
|
VSS_OPERATION_CASE_STMT(VSS_IN_IOCTL_FLUSH_AND_HOLD)
|
|
VSS_OPERATION_CASE_STMT(VSS_IN_IOCTL_RELEASE)
|
|
|
|
// Writer State changes.
|
|
// These values are coming from SetCurrentState
|
|
VSS_WRITERSTATE_CASE_STMT(VSS_WS_UNKNOWN)
|
|
VSS_WRITERSTATE_CASE_STMT(VSS_WS_STABLE)
|
|
VSS_WRITERSTATE_CASE_STMT(VSS_WS_WAITING_FOR_FREEZE)
|
|
VSS_WRITERSTATE_CASE_STMT(VSS_WS_WAITING_FOR_THAW)
|
|
VSS_WRITERSTATE_CASE_STMT(VSS_WS_WAITING_FOR_POST_SNAPSHOT)
|
|
VSS_WRITERSTATE_CASE_STMT(VSS_WS_WAITING_FOR_BACKUP_COMPLETE)
|
|
VSS_WRITERSTATE_CASE_STMT(VSS_WS_FAILED_AT_IDENTIFY)
|
|
VSS_WRITERSTATE_CASE_STMT(VSS_WS_FAILED_AT_PREPARE_BACKUP)
|
|
VSS_WRITERSTATE_CASE_STMT(VSS_WS_FAILED_AT_PREPARE_SNAPSHOT)
|
|
VSS_WRITERSTATE_CASE_STMT(VSS_WS_FAILED_AT_FREEZE)
|
|
VSS_WRITERSTATE_CASE_STMT(VSS_WS_FAILED_AT_THAW)
|
|
VSS_WRITERSTATE_CASE_STMT(VSS_WS_FAILED_AT_POST_SNAPSHOT)
|
|
VSS_WRITERSTATE_CASE_STMT(VSS_WS_FAILED_AT_BACKUP_COMPLETE)
|
|
VSS_WRITERSTATE_CASE_STMT(VSS_WS_FAILED_AT_PRE_RESTORE)
|
|
VSS_WRITERSTATE_CASE_STMT(VSS_WS_FAILED_AT_POST_RESTORE)
|
|
VSS_WRITERSTATE_CASE_STMT(VSS_WS_FAILED_AT_BACKUPSHUTDOWN)
|
|
|
|
// Writer error codes
|
|
// These values are coming from SetCurrentFailure
|
|
VSS_HRESULT_CASE_STMT(VSS_S_OK)
|
|
VSS_HRESULT_CASE_STMT(VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT)
|
|
VSS_HRESULT_CASE_STMT(VSS_E_WRITERERROR_OUTOFRESOURCES)
|
|
VSS_HRESULT_CASE_STMT(VSS_E_WRITERERROR_TIMEOUT)
|
|
VSS_HRESULT_CASE_STMT(VSS_E_WRITERERROR_RETRYABLE)
|
|
VSS_HRESULT_CASE_STMT(VSS_E_WRITERERROR_NONRETRYABLE)
|
|
VSS_HRESULT_CASE_STMT(VSS_E_WRITERERROR_RECOVERY_FAILED)
|
|
VSS_HRESULT_CASE_STMT(VSS_E_WRITER_NOT_RESPONDING)
|
|
default:
|
|
{
|
|
static WCHAR wszBuffer[80];
|
|
::StringCchPrintfW(STRING_CCH_PARAM(wszBuffer),
|
|
L"UNKNOWN_EVENT[0x%08lx] %s",
|
|
dwOperation, bInOperation? L"(Enter)": L"(Leave)");
|
|
return wszBuffer;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Returns true if the diagnose must operate in queued mode
|
|
// (i.e. without writing to registry)
|
|
bool CVssDiag::IsQueuedMode(
|
|
IN DWORD dwEventID,
|
|
IN DWORD dwEventContext
|
|
)
|
|
{
|
|
switch (dwEventID)
|
|
{
|
|
case VSS_IN_IOCTL_FLUSH_AND_HOLD:
|
|
// Always in Queued mode (since we don't know the order for parallel F&H ioctls)
|
|
return true;
|
|
|
|
case VSS_IN_IOCTL_RELEASE:
|
|
// If we are in ENTER then we are still in queued mode
|
|
return !!(dwEventContext & VSS_DIAG_ENTER_OPERATION);
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
bool CVssMachineInformation::IsDuringSetup()
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssMachineInformation::IsDuringSetup" );
|
|
|
|
CRegKey cRegKeySetup;
|
|
DWORD dwRes = cRegKeySetup.Open(HKEY_LOCAL_MACHINE, x_SetupKey, KEY_READ);
|
|
if (dwRes == ERROR_SUCCESS)
|
|
{
|
|
DWORD dwValue;
|
|
dwRes = cRegKeySetup.QueryValue(dwValue, x_SystemSetupInProgress);
|
|
if (dwRes == ERROR_SUCCESS && dwValue > 0)
|
|
return true;
|
|
dwRes = cRegKeySetup.QueryValue(dwValue, x_UpgradeInProgress);
|
|
if (dwRes == ERROR_SUCCESS && dwValue > 0)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool CVssMachineInformation::IsDuringSafeMode()
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_GEN, L"CVssMachineInformation::IsDuringSafeMode" );
|
|
|
|
CRegKey cRegKeySetup;
|
|
DWORD dwRes = cRegKeySetup.Open(HKEY_LOCAL_MACHINE, x_SafebootKey, KEY_READ);
|
|
if (dwRes == ERROR_SUCCESS)
|
|
{
|
|
DWORD dwValue;
|
|
dwRes = cRegKeySetup.QueryValue(dwValue, x_SafebootOptionValue);
|
|
if (dwRes == ERROR_SUCCESS)
|
|
{
|
|
ft.Trace(VSSDBG_GEN, L"SafeBoot option 0x%08lx", dwValue);
|
|
switch(dwValue)
|
|
{
|
|
case SAFEBOOT_MINIMAL:
|
|
case SAFEBOOT_NETWORK:
|
|
return true;
|
|
case SAFEBOOT_DSREPAIR:
|
|
// Writers are allowed in DS Repair
|
|
// (That's the only way for the AD Writer to do its job on restore).
|
|
return false;
|
|
default:
|
|
ft.Trace(VSSDBG_GEN, L"Unrecognized safe mode option %ud", dwValue);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|