Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1209 lines
28 KiB

/*++
Copyright (C) Microsoft Corporation
Module Name:
utils.cpp
Abstract:
This module implements some utilities classes
Author:
William Hsieh (williamh) created
Revision History:
--*/
#include "devmgr.h"
//
// CPropSheetData implementation
//
// Every device or class has a CPropSheetData as a member.
// When m_hWnd contains a valid window handle, it indicates the device/class
// has a active property sheet. This helps us to do this:
// (1). We are sure that there is only one property sheet can be created
// for the device/class at time in a single console no matter how many
// IComponents(snapins, windows) are running in the same console.
// For example, when users asks for the properties for the device/class
// we can bring the active one to the foreground without creating a
// new one.
// (2). We can warn the user that a removal of the device is not allowed
// when the device has an active property sheet.
// (3). We can warn the user that there are property sheets active
// when a "refresh" is requsted.
CPropSheetData::CPropSheetData()
{
memset(&m_psh, 0, sizeof(m_psh));
m_MaxPages = 0;
m_lConsoleHandle = 0;
m_hWnd = NULL;
}
// This function creates(or initialize) the propery sheet data header.
//
// INPUT: hInst -- the module instance handle
// hwndParent -- parent window handle
// MaxPages -- max pages allowed for this property sheet.
// lConsoleHandle -- MMC property change notify handle.
//
// OUTPUT: TRUE if succeeded.
// FALSE if failed(mostly, memory allocation error). GetLastError
// will report the error code.
//
BOOL
CPropSheetData::Create(
HINSTANCE hInst,
HWND hwndParent,
UINT MaxPages,
LONG_PTR lConsoleHandle
)
{
// nobody should try to create the property sheet while it is
// still alive.
ASSERT (NULL == m_hWnd);
if (MaxPages > 64 || NULL == hInst)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
// if not page array is allocated or the existing
// array is too small, allocate a new array.
if (!m_psh.phpage || m_MaxPages < MaxPages)
{
if (m_MaxPages)
{
ASSERT(m_psh.phpage);
delete [] m_psh.phpage;
m_psh.phpage = NULL;
}
m_psh.phpage = new HPROPSHEETPAGE[MaxPages];
m_MaxPages = MaxPages;
}
// initialize the header
m_psh.nPages = 0;
m_psh.dwSize = sizeof(m_psh);
m_psh.dwFlags = PSH_PROPTITLE | PSH_NOAPPLYNOW;
m_psh.hwndParent = hwndParent;
m_psh.hInstance = hInst;
m_psh.pszCaption = NULL;
m_lConsoleHandle = lConsoleHandle;
return TRUE;
}
// This function inserts the given HPROPSHEETPAGE to the
// specific location.
//
// INPUT: hPage -- the page to be inserted.
// Position -- the location to be inserted.
// Position < 0, then append the page
//
// OUTPUT: TRUE if the page is inserted successfully.
// FALSE if the page is not inserted. GetLastError will
// return the error code.
//
BOOL
CPropSheetData::InsertPage(
HPROPSHEETPAGE hPage,
int Position
)
{
if (NULL == hPage || (Position > 0 && (UINT)Position >= m_MaxPages))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
// make sure we have space for a new page.
if (m_psh.nPages >= m_MaxPages)
{
SetLastError(ERROR_BUFFER_OVERFLOW);
return FALSE;
}
if (Position < 0 || (UINT)Position >= m_psh.nPages)
{
// append the page. This also include the very first page.
// Most pages are appened.
m_psh.phpage[m_psh.nPages++] = hPage;
}
else
{
ASSERT(m_psh.nPages);
// move the page around so that we
// can insert the new page to the
// specific location.
// At this moment, we know we have space to accomodate the
// new page(so we can assume that &m_psh.phpage[m_psh.nPage]
// is valid. Also, we are here because there is at least one
// pages in the array.
for (int i = m_psh.nPages; i > Position; i--)
m_psh.phpage[i] = m_psh.phpage[i - 1];
m_psh.phpage[Position] = hPage;
m_psh.nPages++;
}
return TRUE;
}
//
// This function receives notification from its attached
// property pages about their window(dialog) creation
// It takes a chance to record the property sheet window handle
// which we can use to dismiss the property sheet or bring it
// to the foreground.
// INPUT:
// hWnd -- the property page's window handle
//
// OUTPUT:
// NONE
//
void
CPropSheetData::PageCreateNotify(HWND hWnd)
{
ASSERT(hWnd);
hWnd = ::GetParent(hWnd);
if (!m_hWnd)
m_hWnd = hWnd;
}
//
// This function receives notification from its attached
// property pages about their window(dialog) destroy.
// When all attached pages are gone, this function
// reset its internal states and free memory allocation
// WARNING!!!! Do not delete the object when the attached
// window handle counts reaches 0 because we can be reused --
// the reason we have a separate Create functions.
//
// INPUT:
// hWnd -- the property page's window handle
//
// OUTPUT:
// NONE
//
void
CPropSheetData::PageDestroyNotify(HWND hWnd)
{
UNREFERENCED_PARAMETER(hWnd);
m_hWnd = NULL;
delete [] m_psh.phpage;
m_psh.phpage = NULL;
m_MaxPages = 0;
memset(&m_psh, 0, sizeof(m_psh));
if (m_lConsoleHandle)
MMCFreeNotifyHandle(m_lConsoleHandle);
m_lConsoleHandle = 0;
if (!m_listProvider.IsEmpty())
{
POSITION pos = m_listProvider.GetHeadPosition();
while (NULL != pos)
{
delete m_listProvider.GetNext(pos);
}
m_listProvider.RemoveAll();
}
}
CPropSheetData::~CPropSheetData()
{
if (m_lConsoleHandle)
MMCFreeNotifyHandle(m_lConsoleHandle);
if (!m_listProvider.IsEmpty())
{
POSITION pos = m_listProvider.GetHeadPosition();
while (NULL != pos)
{
delete m_listProvider.GetNext(pos);
}
m_listProvider.RemoveAll();
}
if (m_psh.phpage)
delete [] m_psh.phpage;
}
BOOL
CPropSheetData::PropertyChangeNotify(
LPARAM lParam
)
{
if (m_lConsoleHandle)
{
MMCPropertyChangeNotify(m_lConsoleHandle, lParam);
return TRUE;
}
return FALSE;
}
//
// CDialog implementation
//
INT_PTR CALLBACK
CDialog::DialogWndProc(
HWND hDlg,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
CDialog* pThis = (CDialog *) GetWindowLongPtr(hDlg, DWLP_USER);
BOOL Result;
switch (uMsg)
{
case WM_INITDIALOG:
pThis = (CDialog *)lParam;
ASSERT(pThis);
SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)pThis);
pThis->m_hDlg = hDlg;
Result = pThis->OnInitDialog();
break;
case WM_COMMAND:
if (pThis) {
pThis->OnCommand(wParam, lParam);
}
Result = FALSE;
break;
case WM_NOTIFY:
if (pThis) {
Result = pThis->OnNotify((LPNMHDR)lParam);
} else {
Result = FALSE;
}
break;
case WM_DESTROY:
if (pThis) {
Result = pThis->OnDestroy();
} else {
Result = FALSE;
}
break;
case WM_HELP:
if (pThis) {
pThis->OnHelp((LPHELPINFO)lParam);
}
Result = FALSE;
break;
case WM_CONTEXTMENU:
if (pThis) {
pThis->OnContextMenu((HWND)wParam, LOWORD(lParam), HIWORD(lParam));
}
Result = FALSE;
break;
default:
Result = FALSE;
break;
}
return Result;
}
//
// class String implementation
//
String::String()
{
m_pData = new StringData;
if (!m_pData)
throw &g_MemoryException;
}
String::String(
const String& strSrc
)
{
m_pData = strSrc.m_pData;
m_pData->AddRef();
}
String::String(
int Len
)
{
StringData* pNewData = new StringData;
TCHAR* ptszNew = new TCHAR[Len + 1];
if (pNewData && ptszNew)
{
pNewData->Len = 0;
pNewData->ptsz = ptszNew;
m_pData = pNewData;
}
else
{
delete pNewData;
delete [] ptszNew;
throw &g_MemoryException;
}
}
String::String(
LPCTSTR lptsz
)
{
int Len = lstrlen(lptsz);
StringData* pNewData = new StringData;
TCHAR* ptszNew = new TCHAR[Len + 1];
if (pNewData && ptszNew)
{
StringCchCopy(ptszNew, Len+1, lptsz);
pNewData->Len = Len;
pNewData->ptsz = ptszNew;
m_pData = pNewData;
}
else
{
delete pNewData;
delete [] ptszNew;
throw &g_MemoryException;
}
}
void
String::Empty()
{
if (m_pData->Len)
{
StringData* pNewData = new StringData;
if (pNewData)
{
m_pData->Release();
m_pData = pNewData;
}
else
{
throw &g_MemoryException;
}
}
}
String&
String::operator=(
const String& strSrc
)
{
// look out for aliasings !!!!
if (this != &strSrc)
{
// add the reference count first before release the old one
// in case our string data is the same as strSrc's.
strSrc.m_pData->AddRef();
m_pData->Release();
m_pData = strSrc.m_pData;
}
return *this;
}
String&
String::operator=(
LPCTSTR ptsz
)
{
// if we are pointing to the same string,
// do nothing
if (ptsz == m_pData->ptsz)
return *this;
//
// str = NULL --> empty the string
//
if (!ptsz)
{
Empty();
return *this;
}
// a new assignment, allocate a new string data
StringData* pNewData = new StringData;
int len = lstrlen(ptsz);
TCHAR* ptszNew = new TCHAR[len + 1];
if (pNewData && ptszNew)
{
StringCchCopy(ptszNew, len+1, ptsz);
pNewData->Len = len;
pNewData->ptsz = ptszNew;
m_pData->Release();
m_pData = pNewData;
}
else
{
//memory allocation failure
delete pNewData;
delete [] ptszNew;
throw g_MemoryException;
}
return *this;
}
String&
String::operator+=(
const String& strSrc
)
{
if (strSrc.GetLength())
{
int TotalLen = m_pData->Len + strSrc.GetLength();
StringData* pNewData = new StringData;
TCHAR* ptszNew = new TCHAR[TotalLen + 1];
if (pNewData && ptszNew)
{
StringCchCopy(ptszNew, TotalLen+1, m_pData->ptsz);
StringCchCat(ptszNew, TotalLen+1, (LPCTSTR)strSrc);
ptszNew[TotalLen] = TEXT('\0');
pNewData->Len = TotalLen;
pNewData->ptsz = ptszNew;
m_pData->Release();
m_pData = pNewData;
}
else
{
delete pNewData;
delete [] ptszNew;
throw &g_MemoryException;
}
}
return *this;
}
String&
String::operator+=(
LPCTSTR ptsz
)
{
if (ptsz)
{
int len = lstrlen(ptsz);
if (len)
{
StringData* pNewData = new StringData;
TCHAR* ptszNew = new TCHAR[len + m_pData->Len + 1];
if (ptszNew && pNewData)
{
StringCchCopy(ptszNew, len + m_pData->Len + 1, m_pData->ptsz);
StringCchCat(ptszNew, len + m_pData->Len + 1, ptsz);
ptszNew[len + m_pData->Len] = TEXT('\0');
pNewData->Len = len + m_pData->Len;
pNewData->ptsz = ptszNew;
m_pData->Release();
m_pData = pNewData;
}
else
{
delete pNewData;
delete [] ptszNew;
throw &g_MemoryException;
}
}
}
return *this;
}
TCHAR&
String::operator[](
int Index
)
{
ASSERT(Index < m_pData->Len);
// make a separate copy of the string data
TCHAR* ptszNew = new TCHAR[m_pData->Len + 1];
StringData* pNewData = new StringData;
if (ptszNew && pNewData)
{
StringCchCopy(ptszNew, m_pData->Len + 1, m_pData->ptsz);
pNewData->ptsz = ptszNew;
pNewData->Len = m_pData->Len;
m_pData->Release();
m_pData = pNewData;
return ptszNew[Index];
}
else
{
delete pNewData;
delete [] ptszNew;
throw &g_MemoryException;
}
}
String::operator LPTSTR()
{
StringData* pNewData = new StringData;
if (pNewData)
{
if (m_pData->Len)
{
TCHAR* ptszNew = new TCHAR[m_pData->Len + 1];
if (ptszNew)
{
StringCchCopy(ptszNew, m_pData->Len + 1, m_pData->ptsz);
pNewData->ptsz = ptszNew;
}
else
{
delete pNewData;
throw &g_MemoryException;
}
}
pNewData->Len = m_pData->Len;
m_pData->Release();
m_pData = pNewData;
return m_pData->ptsz;
}
else
{
throw &g_MemoryException ;
}
}
//
// This is a friend function to String
// Remember that we can NOT return a reference or a pointer.
// This function must return "by-value"
String
operator+(
const String& str1,
const String& str2
)
{
int TotalLen = str1.GetLength() + str2.GetLength();
String strThis(TotalLen);
StringCchCopy(strThis.m_pData->ptsz, TotalLen+1, str1);
StringCchCat(strThis.m_pData->ptsz, TotalLen+1, str2);
strThis.m_pData->Len = TotalLen;
return strThis;
}
BOOL
String::LoadString(
HINSTANCE hInstance,
int ResourceId
)
{
// we have no idea how long the string will be.
// The strategy here is to allocate a stack-based buffer which
// is large enough for most cases. If the buffer is too small,
// we then use heap-based buffer and increment the buffer size
// on each try.
TCHAR tszTemp[256];
long FinalSize, BufferSize;
BufferSize = ARRAYLEN(tszTemp);
TCHAR* HeapBuffer = NULL;
// first try
FinalSize = ::LoadString(hInstance, ResourceId, tszTemp, BufferSize);
//
// LoadString returns the size of the string it loaded, not including the
// NULL termiated char. So if the returned len is one less then the
// provided buffer size, our buffer is too small.
//
if (FinalSize < (BufferSize - 1))
{
// we got what we want
HeapBuffer = tszTemp;
}
else
{
// the stack based buffer is too small, we have to switch to heap
// based.
BufferSize = ARRAYLEN(tszTemp);
// should 32k chars big enough????
while (BufferSize < 0x8000)
{
BufferSize += 256;
// make sure there is no memory leak
ASSERT(NULL == HeapBuffer);
// allocate a new buffer
HeapBuffer = new TCHAR[BufferSize];
if (HeapBuffer)
{
// got a new buffer, another try...
FinalSize = ::LoadString(hInstance, ResourceId, HeapBuffer,
BufferSize);
if (FinalSize < (BufferSize - 1))
{
//got it!
break;
}
}
else
{
throw &g_MemoryException;
}
// discard the buffer
delete [] HeapBuffer;
HeapBuffer = NULL;
}
}
if (HeapBuffer)
{
TCHAR* ptszNew = new TCHAR[FinalSize + 1];
StringData* pNewData = new StringData;
if (pNewData && ptszNew)
{
StringCchCopy(ptszNew, FinalSize + 1, HeapBuffer);
// release the old string data because we will have a new one
m_pData->Release();
m_pData = pNewData;
m_pData->ptsz = ptszNew;
m_pData->Len = FinalSize;
if (HeapBuffer != tszTemp) {
delete [] HeapBuffer;
}
return TRUE;
}
else
{
delete [] ptszNew;
delete pNewData;
if (HeapBuffer != tszTemp) {
delete [] HeapBuffer;
}
throw &g_MemoryException;
}
}
return FALSE;
}
//
// This function creates an full-qualified machine name for the
// local computer.
//
BOOL
String::GetComputerName()
{
TCHAR tszTemp[MAX_PATH];
// the GetComputerName api only return the name only.
// we must prepend the UNC signature.
tszTemp[0] = _T('\\');
tszTemp[1] = _T('\\');
ULONG NameLength = ARRAYLEN(tszTemp) - 2;
if (::GetComputerName(tszTemp + 2, &NameLength))
{
int Len = lstrlen(tszTemp);
StringData* pNewData = new StringData;
TCHAR* ptszNew = new TCHAR[Len + 1];
if (pNewData && ptszNew)
{
pNewData->Len = Len;
StringCchCopy(ptszNew, Len + 1, tszTemp);
pNewData->ptsz = ptszNew;
m_pData->Release();
m_pData = pNewData;
return TRUE;
}
else
{
delete pNewData;
delete []ptszNew;
throw &g_MemoryException;
}
}
return FALSE;
}
BOOL
String::GetSystemWindowsDirectory()
{
TCHAR tszTemp[MAX_PATH];
if (::GetSystemWindowsDirectory(tszTemp, ARRAYLEN(tszTemp))) {
int Len = lstrlen(tszTemp);
StringData* pNewData = new StringData;
TCHAR* ptszNew = new TCHAR[Len + 1];
if (pNewData && ptszNew) {
pNewData->Len = Len;
StringCchCopy(ptszNew, Len + 1, tszTemp);
pNewData->ptsz = ptszNew;
m_pData->Release();
m_pData = pNewData;
return TRUE;
} else {
delete pNewData;
delete []ptszNew;
throw &g_MemoryException;
}
}
return FALSE;
}
BOOL
String::GetSystemDirectory()
{
TCHAR tszTemp[MAX_PATH];
if (::GetSystemDirectory(tszTemp, ARRAYLEN(tszTemp))) {
int Len = lstrlen(tszTemp);
StringData* pNewData = new StringData;
TCHAR* ptszNew = new TCHAR[Len + 1];
if (pNewData && ptszNew) {
pNewData->Len = Len;
StringCchCopy(ptszNew, Len + 1, tszTemp);
pNewData->ptsz = ptszNew;
m_pData->Release();
m_pData = pNewData;
return TRUE;
} else {
delete pNewData;
delete []ptszNew;
throw &g_MemoryException;
}
}
return FALSE;
}
void
String::Format(
LPCTSTR FormatString,
...
)
{
// according to wsprintf specification, the max buffer size is
// 1024
TCHAR* pBuffer = new TCHAR[1024];
if (pBuffer)
{
va_list arglist;
va_start(arglist, FormatString);
StringCchVPrintf(pBuffer, 1024, FormatString, arglist);
va_end(arglist);
int len = lstrlen(pBuffer);
if (len)
{
TCHAR* ptszNew = new TCHAR[len + 1];
StringData* pNewData = new StringData;
if (pNewData && ptszNew)
{
pNewData->Len = len;
StringCchCopy(ptszNew, len + 1, pBuffer);
pNewData->ptsz = ptszNew;
m_pData->Release();
m_pData = pNewData;
delete [] pBuffer;
return;
}
else
{
delete [] pBuffer;
delete [] ptszNew;
delete pNewData;
throw &g_MemoryException;
}
}
}
throw &g_MemoryException;
}
//
// templates
//
template <class T>
inline void ContructElements(T* pElements, int Count)
{
ASSERT(Count > 0);
memset((void*)pElements, Count * sizeof(T));
for (; Count; pElments++, Count--)
{
// call the class's ctor
// note the placement.
new((void*)pElements) T;
}
}
//
// CCommandLine implementation
//
// code adapted from C startup code -- see stdargv.c
// It walks through the given CmdLine and calls ParseParam
// when an argument is encountered.
// An argument must in this format:
// </command arg_to_command> or <-command arg_to_command>
void
CCommandLine::ParseCommandLine(
LPCTSTR CmdLine
)
{
LPCTSTR p;
LPTSTR args, pDst;
BOOL bInQuote;
BOOL bCopyTheChar;
int nSlash;
p = CmdLine;
args = new TCHAR[lstrlen(CmdLine) + 1];
if (!args)
return;
for (;;)
{
// skip blanks
while (_T(' ') == *p || _T('\t') == *p)
p++;
// nothing left, bail
if (_T('\0') == *p)
break;
// 2N backslashes + '\"' ->N backslashes plus string delimiter
// 2N + 1 baclslashes + '\"' ->N backslashes plus literal '\"'
// N backslashes -> N backslashes
nSlash = 0;
bInQuote = FALSE;
pDst = args;
for (;;)
{
bCopyTheChar = TRUE;
//count how may backslashes
while(_T('\\') == *p)
{
p++;
nSlash++;
}
if (_T('\"') == *p)
{
if (0 == (nSlash % 2))
{
// 2N backslashes plus '\"' ->N baskslashes plus
// delimiter
if (bInQuote)
// double quote inside quoted string
// skip the first and copy the second.
if (_T('\"') == p[1])
p++;
else
bCopyTheChar = FALSE;
else
bCopyTheChar = FALSE;
// toggle quoted status
bInQuote = !bInQuote;
}
nSlash /= 2;
}
while (nSlash)
{
*pDst++ = _T('\\');
nSlash--;
}
if (_T('\0') == *p || (!bInQuote && (_T(' ') == *p || _T('\t') == *p)))
{
break;
}
// copy char to args
if (bCopyTheChar)
{
*pDst++ = *p;
}
p++;
}
// we have a complete argument now. Null terminates it and
// let the derived class parse the argument.
*pDst = _T('\0');
// skip blanks to see if this is the last argument
while (_T(' ') == *p || _T('\t') == *p)
p++;
BOOL bFlag;
bFlag = (_T('/') == *args || _T('-') == *args);
pDst = (bFlag) ? args + 1 : args;
ParseParam(pDst, bFlag);
}
delete [] args;
}
//
// CSafeRegistry implementation
//
BOOL
CSafeRegistry::Open(
HKEY hKeyAncestor,
LPCTSTR KeyName,
REGSAM Access
)
{
DWORD LastError;
// we shouldn't have a valid key -- or memory leak
// Also, a key name must be provided -- or open nothing
ASSERT(!m_hKey && KeyName);
LastError = ::RegOpenKeyEx(hKeyAncestor, KeyName, 0, Access, &m_hKey);
SetLastError(LastError);
return ERROR_SUCCESS == LastError;
}
BOOL
CSafeRegistry::Create(
HKEY hKeyAncestor,
LPCTSTR KeyName,
REGSAM Access,
DWORD* pDisposition,
DWORD Options,
LPSECURITY_ATTRIBUTES pSecurity
)
{
ASSERT(KeyName && !m_hKey);
DWORD Disposition;
DWORD LastError;
LastError = ::RegCreateKeyEx(hKeyAncestor, KeyName, 0, TEXT(""),
Options, Access, pSecurity,
&m_hKey, &Disposition
);
SetLastError(LastError);
if (ERROR_SUCCESS == LastError && pDisposition)
{
*pDisposition = Disposition;
}
if (ERROR_SUCCESS != LastError)
m_hKey = NULL;
return ERROR_SUCCESS == LastError;
}
BOOL
CSafeRegistry::SetValue(
LPCTSTR ValueName,
DWORD Type,
const PBYTE pData,
DWORD DataLen
)
{
ASSERT(m_hKey);
DWORD LastError;
LastError = ::RegSetValueEx(m_hKey, ValueName, 0, Type, pData, DataLen);
SetLastError(LastError);
return ERROR_SUCCESS == LastError;
}
BOOL
CSafeRegistry::SetValue(
LPCTSTR ValueName,
LPCTSTR Value
)
{
return SetValue(ValueName,
REG_SZ,
(PBYTE)Value,
(lstrlen(Value) + 1) * sizeof(TCHAR)
);
}
BOOL
CSafeRegistry::GetValue(
LPCTSTR ValueName,
DWORD* pType,
const PBYTE pData,
DWORD* pDataLen
)
{
ASSERT(m_hKey);
DWORD LastError;
LastError = ::RegQueryValueEx(m_hKey, ValueName, NULL, pType, pData,
pDataLen);
SetLastError(LastError);
return ERROR_SUCCESS == LastError;
}
BOOL
CSafeRegistry::GetValue(
LPCTSTR ValueName,
String& str
)
{
DWORD Type, Size;
Size = 0;
BOOL Result = FALSE;
// check size before Type because when the size is zero, type contains
// undefined data.
if (GetValue(ValueName, &Type, NULL, &Size) && Size && REG_SZ == Type)
{
// we do not want to throw an exception here.
// so guard it
try
{
BufferPtr<BYTE> BufferPtr(Size);
Result = GetValue(ValueName, &Type, BufferPtr, &Size);
if (Result)
str = (LPCTSTR)(BYTE*)BufferPtr;
}
catch(CMemoryException* e)
{
e->Delete();
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
Result = FALSE;
}
}
return Result;
}
BOOL
CSafeRegistry::EnumerateSubkey(
DWORD Index,
LPTSTR Buffer,
DWORD* BufferSize
)
{
DWORD LastError;
FILETIME LastWrite;
LastError = ::RegEnumKeyEx(m_hKey, Index, Buffer, BufferSize,
NULL, NULL, NULL, &LastWrite);
SetLastError(LastError);
return ERROR_SUCCESS == LastError;
}
BOOL
CSafeRegistry::DeleteValue(
LPCTSTR ValueName
)
{
ASSERT(m_hKey);
DWORD LastError = ::RegDeleteValue(m_hKey, ValueName);
SetLastError(LastError);
return ERROR_SUCCESS == LastError;
}
BOOL
CSafeRegistry::DeleteSubkey(
LPCTSTR SubkeyName
)
{
ASSERT(m_hKey);
CSafeRegistry regSubkey;
TCHAR KeyName[MAX_PATH];
FILETIME LastWrite;
DWORD KeyNameLen;
for (;;)
{
KeyNameLen = ARRAYLEN(KeyName);
// always uses index 0(the first subkey)
if (!regSubkey.Open(m_hKey, SubkeyName, KEY_WRITE | KEY_ENUMERATE_SUB_KEYS) ||
ERROR_SUCCESS != ::RegEnumKeyEx(regSubkey, 0, KeyName,
&KeyNameLen, NULL, NULL, NULL,
&LastWrite) ||
!regSubkey.DeleteSubkey(KeyName))
{
break;
}
// close the key so that we will re-open it on each loop
// -- we have deleted one subkey and without closing
// the key, the index to RegEnumKeyEx will be confusing
regSubkey.Close();
}
// now delete the subkey
::RegDeleteKey(m_hKey, SubkeyName);
return TRUE;
}