|
|
/*++
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; }
|