#ifndef __UTILS__H #define __UTILS__H /*++ Copyright (C) 1997-1999 Microsoft Corporation Module Name: utils.h Abstract: This module declares utilities classes Author: William Hsieh (williamh) created Revision History: --*/ // // Memory allocation exception class // class CMemoryException { public: CMemoryException(BOOL Global) { m_Global = Global; m_Message[0] = _T('\0'); m_Caption[0] = _T('\0'); m_Options = MB_OK | MB_ICONHAND; } BOOL SetMessage(LPCTSTR Message) { if (!Message || lstrlen(Message) >= ARRAYLEN(m_Message)) { return FALSE; } lstrcpy(m_Message, Message); return TRUE; } BOOL SetCaption(LPCTSTR Caption) { if (!Caption || lstrlen(Caption) >= ARRAYLEN(m_Caption)) { return FALSE; } lstrcpy(m_Caption, Caption); return TRUE; } BOOL SetOptions(DWORD Options) { m_Options = Options; return TRUE; } void ReportError(HWND hwndParent = NULL) { MessageBox(hwndParent, m_Message, m_Caption, m_Options); } void Delete() { if (!m_Global) { delete this; } } private: TCHAR m_Message[128]; TCHAR m_Caption[128]; DWORD m_Options; BOOL m_Global; }; inline int MAX(int Value1, int Value2) { return (Value1 >= Value2) ? Value1 : Value2; } // // data buffer control class for String class // class StringData { public: StringData() : Ref(1), ptsz(NULL), Len(0) {} ~StringData() { delete [] ptsz; } long AddRef() { Ref++; return Ref; } long Release() { ASSERT(Ref); if (!(--Ref)) { delete this; return 0; } return Ref; } TCHAR* ptsz; long Len; private: long Ref; }; class CBlock { public: CBlock(CBlock* BlockHead, UINT unitCount, UINT unitSize) { data = new BYTE[unitCount * unitSize]; if (data) { if (BlockHead) { m_Next = BlockHead->m_Next; BlockHead->m_Next = this; } else { m_Next = NULL; } } else { throw &g_MemoryException; } } ~CBlock() { if (data) delete [] data; if (m_Next) delete m_Next; } void* data; private: CBlock* m_Next; }; // // Text string class // class String { public: // constructors String(); String(LPCTSTR lptsz); String(const String& strSrc); ~String() { m_pData->Release(); } //operators TCHAR& operator[](int Index); operator LPTSTR(); const TCHAR& operator[](int Index) const { ASSERT(Index < m_pData->Len && m_pData->ptsz); return m_pData->ptsz[Index]; } operator LPCTSTR () const { return m_pData->ptsz; } String& operator=(const String& strSrc); String& operator=(LPCTSTR ptsz); String& operator+=(const String& strSrc); String& operator+=(LPCTSTR prsz); friend String operator+(const String& str1, const String& str2); int GetLength() const { return m_pData->Len; } BOOL IsEmpty() const { return (0 == m_pData->Len); } int Compare(const String& strSrc) const { return lstrcmp(m_pData->ptsz, strSrc.m_pData->ptsz); } int CompareNoCase(const String& strSrc) const { return lstrcmpi(m_pData->ptsz, strSrc.m_pData->ptsz); } void Empty(); BOOL LoadString(HINSTANCE hInstance, int ResourceId); BOOL GetComputerName(); BOOL GetSystemWindowsDirectory(); void Format(LPCTSTR FormatString, ...); StringData* m_pData; protected: String(int Len); }; // // Command line parsing class // class CCommandLine { public: void ParseCommandLine(LPCTSTR cmdline); virtual void ParseParam(LPCTSTR Param, BOOL bFlag, BOOL bLast) = 0; }; // // Safe registry class // class CSafeRegistry { public: CSafeRegistry(HKEY hKey = NULL) : m_hKey(hKey) {} ~CSafeRegistry() { if (m_hKey) { RegCloseKey(m_hKey); } } operator HKEY() { return m_hKey; } BOOL Open(HKEY hKeyAncestor, LPCTSTR KeyName, REGSAM Access = KEY_ALL_ACCESS); void Close() { if (m_hKey) { RegCloseKey(m_hKey); } m_hKey = NULL; } BOOL Create(HKEY hKeyAncestor, LPCTSTR KeyName, REGSAM Access = KEY_ALL_ACCESS, DWORD * pDisposition = NULL, DWORD Options = 0, LPSECURITY_ATTRIBUTES pSecurity = NULL); BOOL SetValue(LPCTSTR ValueName, DWORD Type, PBYTE pData, DWORD DataLen); BOOL SetValue(LPCTSTR ValueName, LPCTSTR Value); BOOL GetValue(LPCTSTR ValueName, DWORD* pType, PBYTE Buffer, DWORD* BufferLen); BOOL GetValue(LPCTSTR ValueName, String& str); BOOL DeleteValue(LPCTSTR ValueName); BOOL DeleteSubkey(LPCTSTR SubkeyName); BOOL EnumerateSubkey(DWORD Index, LPTSTR Buffer, DWORD* BufferSize); private: HKEY m_hKey; }; // define iteration context. To be used by CLIST struct tagPosition{ }; typedef tagPosition* POSITION; template inline void ConstructElements(TYPE* pElements, int Count) { memset((void*)&pElements, 0, Count * sizeof(TYPE)); for (; Count; Count--, pElements++) { // call the contructor -- note the placement ::new((void*)pElements) TYPE; } } template inline void DestructElements(TYPE* pElements, int Count) { for (; Count; Count--, pElements++) { pElements->~TYPE(); } } // // TEMPLATEs // // // CList template, adapted from MFC // template class CList { protected: struct CNode { CNode* pNext; CNode* pPrev; TYPE data; }; public: // Construction CList(int nBlockSize = 10); // Attributes (head and tail) // count of elements int GetCount() const; BOOL IsEmpty() const; // peek at head or tail TYPE& GetHead(); TYPE GetHead() const; TYPE& GetTail(); TYPE GetTail() const; // Operations // get head or tail (and remove it) - don't call on empty list ! TYPE RemoveHead(); TYPE RemoveTail(); // add before head or after tail POSITION AddHead(ARG_TYPE newElement); POSITION AddTail(ARG_TYPE newElement); // add another list of elements before head or after tail void AddHead(CList* pNewList); void AddTail(CList* pNewList); // remove all elements void RemoveAll(); // iteration POSITION GetHeadPosition() const; POSITION GetTailPosition() const; TYPE& GetNext(POSITION& rPosition); // return *Position++ TYPE GetNext(POSITION& rPosition) const; // return *Position++ TYPE& GetPrev(POSITION& rPosition); // return *Position-- TYPE GetPrev(POSITION& rPosition) const; // return *Position-- // getting/modifying an element at a given position TYPE& GetAt(POSITION position); TYPE GetAt(POSITION position) const; void SetAt(POSITION pos, ARG_TYPE newElement); void RemoveAt(POSITION position); // inserting before or after a given position POSITION InsertBefore(POSITION position, ARG_TYPE newElement); POSITION InsertAfter(POSITION position, ARG_TYPE newElement); POSITION FindIndex(int nIndex) const; // get the 'nIndex'th element (may return NULL) // Implementation protected: CNode* m_pNodeHead; CNode* m_pNodeTail; int m_nCount; CNode* m_pNodeFree; CBlock* m_pBlocks; int m_nBlockSize; CNode* NewNode(CNode*, CNode*); void FreeNode(CNode*); public: ~CList(); }; ///////////////////////////////////////////////////////////////////////////// // CList inline functions template inline int CList::GetCount() const { return m_nCount; } template inline BOOL CList::IsEmpty() const { return m_nCount == 0; } template inline TYPE& CList::GetHead() { ASSERT(m_pNodeHead != NULL); return m_pNodeHead->data; } template inline TYPE CList::GetHead() const { ASSERT(m_pNodeHead != NULL); return m_pNodeHead->data; } template inline TYPE& CList::GetTail() { ASSERT(m_pNodeTail != NULL); return m_pNodeTail->data; } template inline TYPE CList::GetTail() const { ASSERT(m_pNodeTail != NULL); return m_pNodeTail->data; } template inline POSITION CList::GetHeadPosition() const { return (POSITION) m_pNodeHead; } template inline POSITION CList::GetTailPosition() const { return (POSITION) m_pNodeTail; } template inline TYPE& CList::GetNext(POSITION& rPosition) // return *Position++ { CNode* pNode = (CNode*) rPosition; rPosition = (POSITION) pNode->pNext; return pNode->data; } template inline TYPE CList::GetNext(POSITION& rPosition) const // return *Position++ { CNode* pNode = (CNode*) rPosition; rPosition = (POSITION) pNode->pNext; return pNode->data; } template inline TYPE& CList::GetPrev(POSITION& rPosition) // return *Position-- { CNode* pNode = (CNode*) rPosition; rPosition = (POSITION) pNode->pPrev; return pNode->data; } template inline TYPE CList::GetPrev(POSITION& rPosition) const // return *Position-- { CNode* pNode = (CNode*) rPosition; rPosition = (POSITION) pNode->pPrev; return pNode->data; } template inline TYPE& CList::GetAt(POSITION position) { CNode* pNode = (CNode*) position; return pNode->data; } template inline TYPE CList::GetAt(POSITION position) const { CNode* pNode = (CNode*) position; return pNode->data; } template inline void CList::SetAt(POSITION pos, ARG_TYPE newElement) { CNode* pNode = (CNode*) pos; pNode->data = newElement; } template CList::CList(int nBlockSize) { ASSERT(nBlockSize > 0); m_nCount = 0; m_pNodeHead = m_pNodeTail = m_pNodeFree = NULL; m_pBlocks = NULL; m_nBlockSize = nBlockSize; } template void CList::RemoveAll() { // destroy elements CNode* pNode; for (pNode = m_pNodeHead; pNode != NULL; pNode = pNode->pNext) { DestructElements(&pNode->data, 1); } m_nCount = 0; m_pNodeHead = m_pNodeTail = m_pNodeFree = NULL; delete m_pBlocks; m_pBlocks = NULL; } template CList::~CList() { RemoveAll(); ASSERT(m_nCount == 0); } ///////////////////////////////////////////////////////////////////////////// // Node helpers // template CList::CNode* CList::NewNode(CList::CNode* pPrev, CList::CNode* pNext) { if (m_pNodeFree == NULL) { // add another block CBlock* pNewBlock = new CBlock(m_pBlocks, m_nBlockSize, sizeof(CNode)); if (!pNewBlock) { throw &g_MemoryException; } if (m_pBlocks == NULL) { m_pBlocks = pNewBlock; } // chain them into free list CNode* pNode = (CNode*) pNewBlock->data; // free in reverse order to make it easier to debug pNode += m_nBlockSize - 1; for (int i = m_nBlockSize-1; i >= 0; i--, pNode--) { pNode->pNext = m_pNodeFree; m_pNodeFree = pNode; } } ASSERT(m_pNodeFree != NULL); // we must have something CList::CNode* pNode = m_pNodeFree; m_pNodeFree = m_pNodeFree->pNext; pNode->pPrev = pPrev; pNode->pNext = pNext; m_nCount++; ASSERT(m_nCount > 0); // make sure we don't overflow ConstructElements(&pNode->data, 1); return pNode; } template void CList::FreeNode(CList::CNode* pNode) { DestructElements(&pNode->data, 1); pNode->pNext = m_pNodeFree; m_pNodeFree = pNode; m_nCount--; ASSERT(m_nCount >= 0); // make sure we don't underflow // if no more elements, cleanup completely if (m_nCount == 0) RemoveAll(); } template POSITION CList::AddHead(ARG_TYPE newElement) { CNode* pNewNode = NewNode(NULL, m_pNodeHead); pNewNode->data = newElement; if (m_pNodeHead != NULL) { m_pNodeHead->pPrev = pNewNode; } else { m_pNodeTail = pNewNode; } m_pNodeHead = pNewNode; return (POSITION) pNewNode; } template POSITION CList::AddTail(ARG_TYPE newElement) { CNode* pNewNode = NewNode(m_pNodeTail, NULL); pNewNode->data = newElement; if (m_pNodeTail != NULL) { m_pNodeTail->pNext = pNewNode; } else { m_pNodeHead = pNewNode; } m_pNodeTail = pNewNode; return (POSITION) pNewNode; } template void CList::AddHead(CList* pNewList) { ASSERT(pNewList != NULL); // add a list of same elements to head (maintain order) POSITION pos = pNewList->GetTailPosition(); while (pos != NULL) { AddHead(pNewList->GetPrev(pos)); } } template void CList::AddTail(CList* pNewList) { ASSERT(pNewList != NULL); // add a list of same elements POSITION pos = pNewList->GetHeadPosition(); while (pos != NULL) { AddTail(pNewList->GetNext(pos)); } } template TYPE CList::RemoveHead() { ASSERT(m_pNodeHead != NULL); // don't call on empty list !!! CNode* pOldNode = m_pNodeHead; TYPE returnValue = pOldNode->data; m_pNodeHead = pOldNode->pNext; if (m_pNodeHead != NULL) { m_pNodeHead->pPrev = NULL; } else { m_pNodeTail = NULL; } FreeNode(pOldNode); return returnValue; } template TYPE CList::RemoveTail() { ASSERT(m_pNodeTail != NULL); // don't call on empty list !!! CNode* pOldNode = m_pNodeTail; TYPE returnValue = pOldNode->data; m_pNodeTail = pOldNode->pPrev; if (m_pNodeTail != NULL) { m_pNodeTail->pNext = NULL; } else { m_pNodeHead = NULL; } FreeNode(pOldNode); return returnValue; } template POSITION CList::InsertBefore(POSITION position, ARG_TYPE newElement) { if (position == NULL) { return AddHead(newElement); // insert before nothing -> head of the list } // Insert it before position CNode* pOldNode = (CNode*) position; CNode* pNewNode = NewNode(pOldNode->pPrev, pOldNode); pNewNode->data = newElement; if (pOldNode->pPrev != NULL) { pOldNode->pPrev->pNext = pNewNode; } else { ASSERT(pOldNode == m_pNodeHead); m_pNodeHead = pNewNode; } pOldNode->pPrev = pNewNode; return (POSITION) pNewNode; } template POSITION CList::InsertAfter(POSITION position, ARG_TYPE newElement) { if (position == NULL) { return AddTail(newElement); // insert after nothing -> tail of the list } // Insert it before position CNode* pOldNode = (CNode*) position; CNode* pNewNode = NewNode(pOldNode, pOldNode->pNext); pNewNode->data = newElement; if (pOldNode->pNext != NULL) { pOldNode->pNext->pPrev = pNewNode; } else { ASSERT(pOldNode == m_pNodeTail); m_pNodeTail = pNewNode; } pOldNode->pNext = pNewNode; return (POSITION) pNewNode; } template void CList::RemoveAt(POSITION position) { CNode* pOldNode = (CNode*) position; // remove pOldNode from list if (pOldNode == m_pNodeHead) { m_pNodeHead = pOldNode->pNext; } else { pOldNode->pPrev->pNext = pOldNode->pNext; } if (pOldNode == m_pNodeTail) { m_pNodeTail = pOldNode->pPrev; } else { pOldNode->pNext->pPrev = pOldNode->pPrev; } FreeNode(pOldNode); } template POSITION CList::FindIndex(int nIndex) const { ASSERT(nIndex >= 0); if (nIndex >= m_nCount) { return NULL; // went too far } CNode* pNode = m_pNodeHead; while (nIndex--) { pNode = pNode->pNext; } return (POSITION) pNode; } // NOTE: // dereferencing operator -> is not supported in this template // because this is designed to allocate intrinsic data types only // template class BufferPtr { public: BufferPtr(UINT Size) : m_pBase(NULL), m_Size(Size) { ASSERT(Size); m_pBase = new T[Size]; m_pCur = m_pBase; if (!m_pBase) { throw &g_MemoryException; } } BufferPtr() { m_pBase = NULL; m_pCur = NULL; m_Size = 0; } ~BufferPtr() { if (m_pBase) { delete [] m_pBase; } } // casting operator operator T*() { return m_pCur; } operator T&() { ASSERT(m_pCur < m_pBase + m_Size); return *m_pCur; } operator void*() { return m_pCur; } T& operator*() { ASSERT(m_pCur < m_pBase + m_Size); return *m_pCur; } // increment/decrement T* operator+(UINT Inc) { ASSERT(m_pBase + m_Size > m_pCur + Inc); return (m_pBase + Inc); } T* operator-(UINT Dec) { ASSERT(m_pBase >= m_pCur - Dec); m_pCur -= Dec; return m_pCur; } //prefix T* operator++() { ASSERT(m_pBase + m_Size > m_pCur - 1); return ++m_pCur; } //postfix T* operator++(int inc) { pCur ASSERT(m_pBase + m_Size > m_pCur); return m_pCur++; } //prefix T* operator--() { ASSERT(m_pCur > m_pBase); return --m_pCur; } //postfix T* operator--(int inc) { ASSERT(m_pCur > m_pBase); return m_pCur--; } T** operator&() { return &m_pBase; } // subscripting T& operator[](UINT Index) { ASSERT(Index < m_Size); return m_pBase[Index]; } void Attach(T* pT, UINT Size = 1) { ASSERT(!m_pBase); m_pBase = pT; m_pCur = m_pBase; m_Size = Size; } void Detach() { m_pBase = NULL; } UINT GetSize() { return m_Size; } private: T* m_pBase; T* m_pCur; UINT m_Size; }; template class SafePtr { public: SafePtr(T* p) { __p = p; } SafePtr() { __p = NULL; } ~SafePtr() { if (__p) { delete __p; } } void Attach(T* p) { ASSERT(NULL == __p); __p = p; } void Detach() { __p = NULL; } T* operator->() { ASSERT(__p); return __p; } T& operator*() { ASSERT(__p); return *__p; } operator T*() { return __p; } operator T&() { ASSERT(__p); return *__p; } private: T* __p; }; class CPropPageProvider; class CPropSheetData { public: CPropSheetData(); ~CPropSheetData(); virtual BOOL Create(HINSTANCE hInst, HWND hwndParent, UINT MaxPages, LONG_PTR lConsoleHandle = 0); BOOL InsertPage(HPROPSHEETPAGE hPage, int Index = -1); INT_PTR DoSheet() { return ::PropertySheet(&m_psh); } HWND GetWindowHandle() { return m_hWnd; } void PageCreateNotify(HWND hWnd); void PageDestroyNotify(HWND hWnd); PROPSHEETHEADER m_psh; BOOL PropertyChangeNotify(long lParam); void AddProvider(CPropPageProvider* pProvider) { m_listProvider.AddTail(pProvider); } protected: UINT m_MaxPages; LONG_PTR m_lConsoleHandle; HWND m_hWnd; private: CList m_listProvider; }; class CDialog { public: CDialog(int TemplateId) : m_hDlg(NULL), m_TemplateId(TemplateId) {} virtual ~CDialog() {} static INT_PTR CALLBACK DialogWndProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); INT_PTR DoModal(HWND hwndParent, LPARAM lParam ) { return DialogBoxParam(g_hInstance, MAKEINTRESOURCE(m_TemplateId), hwndParent, DialogWndProc, lParam); } void DoModaless(HWND hwndParent, LPARAM lParam) { m_hDlg = CreateDialogParam(g_hInstance, MAKEINTRESOURCE(m_TemplateId), hwndParent, DialogWndProc, lParam); } virtual BOOL OnInitDialog() { return TRUE; } virtual void OnCommand(WPARAM wParam, LPARAM lParam) {} virtual BOOL OnNotify(LPNMHDR pnmhdr) { return FALSE; } virtual BOOL OnDestroy() { return FALSE; } virtual BOOL OnHelp(LPHELPINFO pHelpInfo) { return FALSE; } virtual BOOL OnContextMenu(HWND hWnd, WORD xPos, WORD yPos) { return FALSE; } HWND GetControl(int idControl) { return GetDlgItem(m_hDlg, idControl); } operator HWND() { return m_hDlg; } HWND m_hDlg; private: int m_TemplateId; }; class CFileHandle { public: CFileHandle(HANDLE hFile = INVALID_HANDLE_VALUE) : m_hFile(hFile) {} ~CFileHandle() { if (INVALID_HANDLE_VALUE != m_hFile) CloseHandle(m_hFile); } void Open(HANDLE hFile) { ASSERT(INVALID_HANDLE_VALUE == m_hFile); m_hFile = hFile; } void Close() { if (INVALID_HANDLE_VALUE != m_hFile) CloseHandle(m_hFile); } HANDLE hFile() { return m_hFile; } private: HANDLE m_hFile; }; class CLogFile { public: CLogFile() : m_hFile(INVALID_HANDLE_VALUE) {} ~CLogFile() { Close(); } BOOL Create(LPCTSTR LogFileName) { if (!LogFileName) { return FALSE; } m_strLogFileName = LogFileName; m_hFile = CreateFile(LogFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); return INVALID_HANDLE_VALUE != m_hFile; } void Close() { if (m_hFile) { CloseHandle(m_hFile); m_hFile = INVALID_HANDLE_VALUE; } } LPCTSTR LogFileName() { return m_strLogFileName.IsEmpty() ? NULL : (LPCTSTR)m_strLogFileName; } void Delete() { Close(); if (m_strLogFileName) DeleteFile(m_strLogFileName); } BOOL LogLastError(LPCTSTR FunctionName); BOOL Logf(LPCTSTR Format, ...); BOOL Log(LPCTSTR Text); private: HANDLE m_hFile; String m_strLogFileName; }; STDAPI_(CONFIGRET) GetLocationInformation( DEVNODE dn, LPTSTR Location, ULONG LocationLen, // In characters HMACHINE hMachine ); #endif // __UTILS_H_