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

  1. /*++
  2. Copyright (C) Microsoft Corporation
  3. Module Name:
  4. utils.cpp
  5. Abstract:
  6. This module implements some utilities classes
  7. Author:
  8. William Hsieh (williamh) created
  9. Revision History:
  10. --*/
  11. #include "devmgr.h"
  12. //
  13. // CPropSheetData implementation
  14. //
  15. // Every device or class has a CPropSheetData as a member.
  16. // When m_hWnd contains a valid window handle, it indicates the device/class
  17. // has a active property sheet. This helps us to do this:
  18. // (1). We are sure that there is only one property sheet can be created
  19. // for the device/class at time in a single console no matter how many
  20. // IComponents(snapins, windows) are running in the same console.
  21. // For example, when users asks for the properties for the device/class
  22. // we can bring the active one to the foreground without creating a
  23. // new one.
  24. // (2). We can warn the user that a removal of the device is not allowed
  25. // when the device has an active property sheet.
  26. // (3). We can warn the user that there are property sheets active
  27. // when a "refresh" is requsted.
  28. CPropSheetData::CPropSheetData()
  29. {
  30. memset(&m_psh, 0, sizeof(m_psh));
  31. m_MaxPages = 0;
  32. m_lConsoleHandle = 0;
  33. m_hWnd = NULL;
  34. }
  35. // This function creates(or initialize) the propery sheet data header.
  36. //
  37. // INPUT: hInst -- the module instance handle
  38. // hwndParent -- parent window handle
  39. // MaxPages -- max pages allowed for this property sheet.
  40. // lConsoleHandle -- MMC property change notify handle.
  41. //
  42. // OUTPUT: TRUE if succeeded.
  43. // FALSE if failed(mostly, memory allocation error). GetLastError
  44. // will report the error code.
  45. //
  46. BOOL
  47. CPropSheetData::Create(
  48. HINSTANCE hInst,
  49. HWND hwndParent,
  50. UINT MaxPages,
  51. LONG_PTR lConsoleHandle
  52. )
  53. {
  54. // nobody should try to create the property sheet while it is
  55. // still alive.
  56. ASSERT (NULL == m_hWnd);
  57. if (MaxPages > 64 || NULL == hInst)
  58. {
  59. SetLastError(ERROR_INVALID_PARAMETER);
  60. return FALSE;
  61. }
  62. // if not page array is allocated or the existing
  63. // array is too small, allocate a new array.
  64. if (!m_psh.phpage || m_MaxPages < MaxPages)
  65. {
  66. if (m_MaxPages)
  67. {
  68. ASSERT(m_psh.phpage);
  69. delete [] m_psh.phpage;
  70. m_psh.phpage = NULL;
  71. }
  72. m_psh.phpage = new HPROPSHEETPAGE[MaxPages];
  73. m_MaxPages = MaxPages;
  74. }
  75. // initialize the header
  76. m_psh.nPages = 0;
  77. m_psh.dwSize = sizeof(m_psh);
  78. m_psh.dwFlags = PSH_PROPTITLE | PSH_NOAPPLYNOW;
  79. m_psh.hwndParent = hwndParent;
  80. m_psh.hInstance = hInst;
  81. m_psh.pszCaption = NULL;
  82. m_lConsoleHandle = lConsoleHandle;
  83. return TRUE;
  84. }
  85. // This function inserts the given HPROPSHEETPAGE to the
  86. // specific location.
  87. //
  88. // INPUT: hPage -- the page to be inserted.
  89. // Position -- the location to be inserted.
  90. // Position < 0, then append the page
  91. //
  92. // OUTPUT: TRUE if the page is inserted successfully.
  93. // FALSE if the page is not inserted. GetLastError will
  94. // return the error code.
  95. //
  96. BOOL
  97. CPropSheetData::InsertPage(
  98. HPROPSHEETPAGE hPage,
  99. int Position
  100. )
  101. {
  102. if (NULL == hPage || (Position > 0 && (UINT)Position >= m_MaxPages))
  103. {
  104. SetLastError(ERROR_INVALID_PARAMETER);
  105. return FALSE;
  106. }
  107. // make sure we have space for a new page.
  108. if (m_psh.nPages >= m_MaxPages)
  109. {
  110. SetLastError(ERROR_BUFFER_OVERFLOW);
  111. return FALSE;
  112. }
  113. if (Position < 0 || (UINT)Position >= m_psh.nPages)
  114. {
  115. // append the page. This also include the very first page.
  116. // Most pages are appened.
  117. m_psh.phpage[m_psh.nPages++] = hPage;
  118. }
  119. else
  120. {
  121. ASSERT(m_psh.nPages);
  122. // move the page around so that we
  123. // can insert the new page to the
  124. // specific location.
  125. // At this moment, we know we have space to accomodate the
  126. // new page(so we can assume that &m_psh.phpage[m_psh.nPage]
  127. // is valid. Also, we are here because there is at least one
  128. // pages in the array.
  129. for (int i = m_psh.nPages; i > Position; i--)
  130. m_psh.phpage[i] = m_psh.phpage[i - 1];
  131. m_psh.phpage[Position] = hPage;
  132. m_psh.nPages++;
  133. }
  134. return TRUE;
  135. }
  136. //
  137. // This function receives notification from its attached
  138. // property pages about their window(dialog) creation
  139. // It takes a chance to record the property sheet window handle
  140. // which we can use to dismiss the property sheet or bring it
  141. // to the foreground.
  142. // INPUT:
  143. // hWnd -- the property page's window handle
  144. //
  145. // OUTPUT:
  146. // NONE
  147. //
  148. void
  149. CPropSheetData::PageCreateNotify(HWND hWnd)
  150. {
  151. ASSERT(hWnd);
  152. hWnd = ::GetParent(hWnd);
  153. if (!m_hWnd)
  154. m_hWnd = hWnd;
  155. }
  156. //
  157. // This function receives notification from its attached
  158. // property pages about their window(dialog) destroy.
  159. // When all attached pages are gone, this function
  160. // reset its internal states and free memory allocation
  161. // WARNING!!!! Do not delete the object when the attached
  162. // window handle counts reaches 0 because we can be reused --
  163. // the reason we have a separate Create functions.
  164. //
  165. // INPUT:
  166. // hWnd -- the property page's window handle
  167. //
  168. // OUTPUT:
  169. // NONE
  170. //
  171. void
  172. CPropSheetData::PageDestroyNotify(HWND hWnd)
  173. {
  174. UNREFERENCED_PARAMETER(hWnd);
  175. m_hWnd = NULL;
  176. delete [] m_psh.phpage;
  177. m_psh.phpage = NULL;
  178. m_MaxPages = 0;
  179. memset(&m_psh, 0, sizeof(m_psh));
  180. if (m_lConsoleHandle)
  181. MMCFreeNotifyHandle(m_lConsoleHandle);
  182. m_lConsoleHandle = 0;
  183. if (!m_listProvider.IsEmpty())
  184. {
  185. POSITION pos = m_listProvider.GetHeadPosition();
  186. while (NULL != pos)
  187. {
  188. delete m_listProvider.GetNext(pos);
  189. }
  190. m_listProvider.RemoveAll();
  191. }
  192. }
  193. CPropSheetData::~CPropSheetData()
  194. {
  195. if (m_lConsoleHandle)
  196. MMCFreeNotifyHandle(m_lConsoleHandle);
  197. if (!m_listProvider.IsEmpty())
  198. {
  199. POSITION pos = m_listProvider.GetHeadPosition();
  200. while (NULL != pos)
  201. {
  202. delete m_listProvider.GetNext(pos);
  203. }
  204. m_listProvider.RemoveAll();
  205. }
  206. if (m_psh.phpage)
  207. delete [] m_psh.phpage;
  208. }
  209. BOOL
  210. CPropSheetData::PropertyChangeNotify(
  211. LPARAM lParam
  212. )
  213. {
  214. if (m_lConsoleHandle)
  215. {
  216. MMCPropertyChangeNotify(m_lConsoleHandle, lParam);
  217. return TRUE;
  218. }
  219. return FALSE;
  220. }
  221. //
  222. // CDialog implementation
  223. //
  224. INT_PTR CALLBACK
  225. CDialog::DialogWndProc(
  226. HWND hDlg,
  227. UINT uMsg,
  228. WPARAM wParam,
  229. LPARAM lParam
  230. )
  231. {
  232. CDialog* pThis = (CDialog *) GetWindowLongPtr(hDlg, DWLP_USER);
  233. BOOL Result;
  234. switch (uMsg)
  235. {
  236. case WM_INITDIALOG:
  237. pThis = (CDialog *)lParam;
  238. ASSERT(pThis);
  239. SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)pThis);
  240. pThis->m_hDlg = hDlg;
  241. Result = pThis->OnInitDialog();
  242. break;
  243. case WM_COMMAND:
  244. if (pThis) {
  245. pThis->OnCommand(wParam, lParam);
  246. }
  247. Result = FALSE;
  248. break;
  249. case WM_NOTIFY:
  250. if (pThis) {
  251. Result = pThis->OnNotify((LPNMHDR)lParam);
  252. } else {
  253. Result = FALSE;
  254. }
  255. break;
  256. case WM_DESTROY:
  257. if (pThis) {
  258. Result = pThis->OnDestroy();
  259. } else {
  260. Result = FALSE;
  261. }
  262. break;
  263. case WM_HELP:
  264. if (pThis) {
  265. pThis->OnHelp((LPHELPINFO)lParam);
  266. }
  267. Result = FALSE;
  268. break;
  269. case WM_CONTEXTMENU:
  270. if (pThis) {
  271. pThis->OnContextMenu((HWND)wParam, LOWORD(lParam), HIWORD(lParam));
  272. }
  273. Result = FALSE;
  274. break;
  275. default:
  276. Result = FALSE;
  277. break;
  278. }
  279. return Result;
  280. }
  281. //
  282. // class String implementation
  283. //
  284. String::String()
  285. {
  286. m_pData = new StringData;
  287. if (!m_pData)
  288. throw &g_MemoryException;
  289. }
  290. String::String(
  291. const String& strSrc
  292. )
  293. {
  294. m_pData = strSrc.m_pData;
  295. m_pData->AddRef();
  296. }
  297. String::String(
  298. int Len
  299. )
  300. {
  301. StringData* pNewData = new StringData;
  302. TCHAR* ptszNew = new TCHAR[Len + 1];
  303. if (pNewData && ptszNew)
  304. {
  305. pNewData->Len = 0;
  306. pNewData->ptsz = ptszNew;
  307. m_pData = pNewData;
  308. }
  309. else
  310. {
  311. delete pNewData;
  312. delete [] ptszNew;
  313. throw &g_MemoryException;
  314. }
  315. }
  316. String::String(
  317. LPCTSTR lptsz
  318. )
  319. {
  320. int Len = lstrlen(lptsz);
  321. StringData* pNewData = new StringData;
  322. TCHAR* ptszNew = new TCHAR[Len + 1];
  323. if (pNewData && ptszNew)
  324. {
  325. StringCchCopy(ptszNew, Len+1, lptsz);
  326. pNewData->Len = Len;
  327. pNewData->ptsz = ptszNew;
  328. m_pData = pNewData;
  329. }
  330. else
  331. {
  332. delete pNewData;
  333. delete [] ptszNew;
  334. throw &g_MemoryException;
  335. }
  336. }
  337. void
  338. String::Empty()
  339. {
  340. if (m_pData->Len)
  341. {
  342. StringData* pNewData = new StringData;
  343. if (pNewData)
  344. {
  345. m_pData->Release();
  346. m_pData = pNewData;
  347. }
  348. else
  349. {
  350. throw &g_MemoryException;
  351. }
  352. }
  353. }
  354. String&
  355. String::operator=(
  356. const String& strSrc
  357. )
  358. {
  359. // look out for aliasings !!!!
  360. if (this != &strSrc)
  361. {
  362. // add the reference count first before release the old one
  363. // in case our string data is the same as strSrc's.
  364. strSrc.m_pData->AddRef();
  365. m_pData->Release();
  366. m_pData = strSrc.m_pData;
  367. }
  368. return *this;
  369. }
  370. String&
  371. String::operator=(
  372. LPCTSTR ptsz
  373. )
  374. {
  375. // if we are pointing to the same string,
  376. // do nothing
  377. if (ptsz == m_pData->ptsz)
  378. return *this;
  379. //
  380. // str = NULL --> empty the string
  381. //
  382. if (!ptsz)
  383. {
  384. Empty();
  385. return *this;
  386. }
  387. // a new assignment, allocate a new string data
  388. StringData* pNewData = new StringData;
  389. int len = lstrlen(ptsz);
  390. TCHAR* ptszNew = new TCHAR[len + 1];
  391. if (pNewData && ptszNew)
  392. {
  393. StringCchCopy(ptszNew, len+1, ptsz);
  394. pNewData->Len = len;
  395. pNewData->ptsz = ptszNew;
  396. m_pData->Release();
  397. m_pData = pNewData;
  398. }
  399. else
  400. {
  401. //memory allocation failure
  402. delete pNewData;
  403. delete [] ptszNew;
  404. throw g_MemoryException;
  405. }
  406. return *this;
  407. }
  408. String&
  409. String::operator+=(
  410. const String& strSrc
  411. )
  412. {
  413. if (strSrc.GetLength())
  414. {
  415. int TotalLen = m_pData->Len + strSrc.GetLength();
  416. StringData* pNewData = new StringData;
  417. TCHAR* ptszNew = new TCHAR[TotalLen + 1];
  418. if (pNewData && ptszNew)
  419. {
  420. StringCchCopy(ptszNew, TotalLen+1, m_pData->ptsz);
  421. StringCchCat(ptszNew, TotalLen+1, (LPCTSTR)strSrc);
  422. ptszNew[TotalLen] = TEXT('\0');
  423. pNewData->Len = TotalLen;
  424. pNewData->ptsz = ptszNew;
  425. m_pData->Release();
  426. m_pData = pNewData;
  427. }
  428. else
  429. {
  430. delete pNewData;
  431. delete [] ptszNew;
  432. throw &g_MemoryException;
  433. }
  434. }
  435. return *this;
  436. }
  437. String&
  438. String::operator+=(
  439. LPCTSTR ptsz
  440. )
  441. {
  442. if (ptsz)
  443. {
  444. int len = lstrlen(ptsz);
  445. if (len)
  446. {
  447. StringData* pNewData = new StringData;
  448. TCHAR* ptszNew = new TCHAR[len + m_pData->Len + 1];
  449. if (ptszNew && pNewData)
  450. {
  451. StringCchCopy(ptszNew, len + m_pData->Len + 1, m_pData->ptsz);
  452. StringCchCat(ptszNew, len + m_pData->Len + 1, ptsz);
  453. ptszNew[len + m_pData->Len] = TEXT('\0');
  454. pNewData->Len = len + m_pData->Len;
  455. pNewData->ptsz = ptszNew;
  456. m_pData->Release();
  457. m_pData = pNewData;
  458. }
  459. else
  460. {
  461. delete pNewData;
  462. delete [] ptszNew;
  463. throw &g_MemoryException;
  464. }
  465. }
  466. }
  467. return *this;
  468. }
  469. TCHAR&
  470. String::operator[](
  471. int Index
  472. )
  473. {
  474. ASSERT(Index < m_pData->Len);
  475. // make a separate copy of the string data
  476. TCHAR* ptszNew = new TCHAR[m_pData->Len + 1];
  477. StringData* pNewData = new StringData;
  478. if (ptszNew && pNewData)
  479. {
  480. StringCchCopy(ptszNew, m_pData->Len + 1, m_pData->ptsz);
  481. pNewData->ptsz = ptszNew;
  482. pNewData->Len = m_pData->Len;
  483. m_pData->Release();
  484. m_pData = pNewData;
  485. return ptszNew[Index];
  486. }
  487. else
  488. {
  489. delete pNewData;
  490. delete [] ptszNew;
  491. throw &g_MemoryException;
  492. }
  493. }
  494. String::operator LPTSTR()
  495. {
  496. StringData* pNewData = new StringData;
  497. if (pNewData)
  498. {
  499. if (m_pData->Len)
  500. {
  501. TCHAR* ptszNew = new TCHAR[m_pData->Len + 1];
  502. if (ptszNew)
  503. {
  504. StringCchCopy(ptszNew, m_pData->Len + 1, m_pData->ptsz);
  505. pNewData->ptsz = ptszNew;
  506. }
  507. else
  508. {
  509. delete pNewData;
  510. throw &g_MemoryException;
  511. }
  512. }
  513. pNewData->Len = m_pData->Len;
  514. m_pData->Release();
  515. m_pData = pNewData;
  516. return m_pData->ptsz;
  517. }
  518. else
  519. {
  520. throw &g_MemoryException ;
  521. }
  522. }
  523. //
  524. // This is a friend function to String
  525. // Remember that we can NOT return a reference or a pointer.
  526. // This function must return "by-value"
  527. String
  528. operator+(
  529. const String& str1,
  530. const String& str2
  531. )
  532. {
  533. int TotalLen = str1.GetLength() + str2.GetLength();
  534. String strThis(TotalLen);
  535. StringCchCopy(strThis.m_pData->ptsz, TotalLen+1, str1);
  536. StringCchCat(strThis.m_pData->ptsz, TotalLen+1, str2);
  537. strThis.m_pData->Len = TotalLen;
  538. return strThis;
  539. }
  540. BOOL
  541. String::LoadString(
  542. HINSTANCE hInstance,
  543. int ResourceId
  544. )
  545. {
  546. // we have no idea how long the string will be.
  547. // The strategy here is to allocate a stack-based buffer which
  548. // is large enough for most cases. If the buffer is too small,
  549. // we then use heap-based buffer and increment the buffer size
  550. // on each try.
  551. TCHAR tszTemp[256];
  552. long FinalSize, BufferSize;
  553. BufferSize = ARRAYLEN(tszTemp);
  554. TCHAR* HeapBuffer = NULL;
  555. // first try
  556. FinalSize = ::LoadString(hInstance, ResourceId, tszTemp, BufferSize);
  557. //
  558. // LoadString returns the size of the string it loaded, not including the
  559. // NULL termiated char. So if the returned len is one less then the
  560. // provided buffer size, our buffer is too small.
  561. //
  562. if (FinalSize < (BufferSize - 1))
  563. {
  564. // we got what we want
  565. HeapBuffer = tszTemp;
  566. }
  567. else
  568. {
  569. // the stack based buffer is too small, we have to switch to heap
  570. // based.
  571. BufferSize = ARRAYLEN(tszTemp);
  572. // should 32k chars big enough????
  573. while (BufferSize < 0x8000)
  574. {
  575. BufferSize += 256;
  576. // make sure there is no memory leak
  577. ASSERT(NULL == HeapBuffer);
  578. // allocate a new buffer
  579. HeapBuffer = new TCHAR[BufferSize];
  580. if (HeapBuffer)
  581. {
  582. // got a new buffer, another try...
  583. FinalSize = ::LoadString(hInstance, ResourceId, HeapBuffer,
  584. BufferSize);
  585. if (FinalSize < (BufferSize - 1))
  586. {
  587. //got it!
  588. break;
  589. }
  590. }
  591. else
  592. {
  593. throw &g_MemoryException;
  594. }
  595. // discard the buffer
  596. delete [] HeapBuffer;
  597. HeapBuffer = NULL;
  598. }
  599. }
  600. if (HeapBuffer)
  601. {
  602. TCHAR* ptszNew = new TCHAR[FinalSize + 1];
  603. StringData* pNewData = new StringData;
  604. if (pNewData && ptszNew)
  605. {
  606. StringCchCopy(ptszNew, FinalSize + 1, HeapBuffer);
  607. // release the old string data because we will have a new one
  608. m_pData->Release();
  609. m_pData = pNewData;
  610. m_pData->ptsz = ptszNew;
  611. m_pData->Len = FinalSize;
  612. if (HeapBuffer != tszTemp) {
  613. delete [] HeapBuffer;
  614. }
  615. return TRUE;
  616. }
  617. else
  618. {
  619. delete [] ptszNew;
  620. delete pNewData;
  621. if (HeapBuffer != tszTemp) {
  622. delete [] HeapBuffer;
  623. }
  624. throw &g_MemoryException;
  625. }
  626. }
  627. return FALSE;
  628. }
  629. //
  630. // This function creates an full-qualified machine name for the
  631. // local computer.
  632. //
  633. BOOL
  634. String::GetComputerName()
  635. {
  636. TCHAR tszTemp[MAX_PATH];
  637. // the GetComputerName api only return the name only.
  638. // we must prepend the UNC signature.
  639. tszTemp[0] = _T('\\');
  640. tszTemp[1] = _T('\\');
  641. ULONG NameLength = ARRAYLEN(tszTemp) - 2;
  642. if (::GetComputerName(tszTemp + 2, &NameLength))
  643. {
  644. int Len = lstrlen(tszTemp);
  645. StringData* pNewData = new StringData;
  646. TCHAR* ptszNew = new TCHAR[Len + 1];
  647. if (pNewData && ptszNew)
  648. {
  649. pNewData->Len = Len;
  650. StringCchCopy(ptszNew, Len + 1, tszTemp);
  651. pNewData->ptsz = ptszNew;
  652. m_pData->Release();
  653. m_pData = pNewData;
  654. return TRUE;
  655. }
  656. else
  657. {
  658. delete pNewData;
  659. delete []ptszNew;
  660. throw &g_MemoryException;
  661. }
  662. }
  663. return FALSE;
  664. }
  665. BOOL
  666. String::GetSystemWindowsDirectory()
  667. {
  668. TCHAR tszTemp[MAX_PATH];
  669. if (::GetSystemWindowsDirectory(tszTemp, ARRAYLEN(tszTemp))) {
  670. int Len = lstrlen(tszTemp);
  671. StringData* pNewData = new StringData;
  672. TCHAR* ptszNew = new TCHAR[Len + 1];
  673. if (pNewData && ptszNew) {
  674. pNewData->Len = Len;
  675. StringCchCopy(ptszNew, Len + 1, tszTemp);
  676. pNewData->ptsz = ptszNew;
  677. m_pData->Release();
  678. m_pData = pNewData;
  679. return TRUE;
  680. } else {
  681. delete pNewData;
  682. delete []ptszNew;
  683. throw &g_MemoryException;
  684. }
  685. }
  686. return FALSE;
  687. }
  688. BOOL
  689. String::GetSystemDirectory()
  690. {
  691. TCHAR tszTemp[MAX_PATH];
  692. if (::GetSystemDirectory(tszTemp, ARRAYLEN(tszTemp))) {
  693. int Len = lstrlen(tszTemp);
  694. StringData* pNewData = new StringData;
  695. TCHAR* ptszNew = new TCHAR[Len + 1];
  696. if (pNewData && ptszNew) {
  697. pNewData->Len = Len;
  698. StringCchCopy(ptszNew, Len + 1, tszTemp);
  699. pNewData->ptsz = ptszNew;
  700. m_pData->Release();
  701. m_pData = pNewData;
  702. return TRUE;
  703. } else {
  704. delete pNewData;
  705. delete []ptszNew;
  706. throw &g_MemoryException;
  707. }
  708. }
  709. return FALSE;
  710. }
  711. void
  712. String::Format(
  713. LPCTSTR FormatString,
  714. ...
  715. )
  716. {
  717. // according to wsprintf specification, the max buffer size is
  718. // 1024
  719. TCHAR* pBuffer = new TCHAR[1024];
  720. if (pBuffer)
  721. {
  722. va_list arglist;
  723. va_start(arglist, FormatString);
  724. StringCchVPrintf(pBuffer, 1024, FormatString, arglist);
  725. va_end(arglist);
  726. int len = lstrlen(pBuffer);
  727. if (len)
  728. {
  729. TCHAR* ptszNew = new TCHAR[len + 1];
  730. StringData* pNewData = new StringData;
  731. if (pNewData && ptszNew)
  732. {
  733. pNewData->Len = len;
  734. StringCchCopy(ptszNew, len + 1, pBuffer);
  735. pNewData->ptsz = ptszNew;
  736. m_pData->Release();
  737. m_pData = pNewData;
  738. delete [] pBuffer;
  739. return;
  740. }
  741. else
  742. {
  743. delete [] pBuffer;
  744. delete [] ptszNew;
  745. delete pNewData;
  746. throw &g_MemoryException;
  747. }
  748. }
  749. }
  750. throw &g_MemoryException;
  751. }
  752. //
  753. // templates
  754. //
  755. template <class T>
  756. inline void ContructElements(T* pElements, int Count)
  757. {
  758. ASSERT(Count > 0);
  759. memset((void*)pElements, Count * sizeof(T));
  760. for (; Count; pElments++, Count--)
  761. {
  762. // call the class's ctor
  763. // note the placement.
  764. new((void*)pElements) T;
  765. }
  766. }
  767. //
  768. // CCommandLine implementation
  769. //
  770. // code adapted from C startup code -- see stdargv.c
  771. // It walks through the given CmdLine and calls ParseParam
  772. // when an argument is encountered.
  773. // An argument must in this format:
  774. // </command arg_to_command> or <-command arg_to_command>
  775. void
  776. CCommandLine::ParseCommandLine(
  777. LPCTSTR CmdLine
  778. )
  779. {
  780. LPCTSTR p;
  781. LPTSTR args, pDst;
  782. BOOL bInQuote;
  783. BOOL bCopyTheChar;
  784. int nSlash;
  785. p = CmdLine;
  786. args = new TCHAR[lstrlen(CmdLine) + 1];
  787. if (!args)
  788. return;
  789. for (;;)
  790. {
  791. // skip blanks
  792. while (_T(' ') == *p || _T('\t') == *p)
  793. p++;
  794. // nothing left, bail
  795. if (_T('\0') == *p)
  796. break;
  797. // 2N backslashes + '\"' ->N backslashes plus string delimiter
  798. // 2N + 1 baclslashes + '\"' ->N backslashes plus literal '\"'
  799. // N backslashes -> N backslashes
  800. nSlash = 0;
  801. bInQuote = FALSE;
  802. pDst = args;
  803. for (;;)
  804. {
  805. bCopyTheChar = TRUE;
  806. //count how may backslashes
  807. while(_T('\\') == *p)
  808. {
  809. p++;
  810. nSlash++;
  811. }
  812. if (_T('\"') == *p)
  813. {
  814. if (0 == (nSlash % 2))
  815. {
  816. // 2N backslashes plus '\"' ->N baskslashes plus
  817. // delimiter
  818. if (bInQuote)
  819. // double quote inside quoted string
  820. // skip the first and copy the second.
  821. if (_T('\"') == p[1])
  822. p++;
  823. else
  824. bCopyTheChar = FALSE;
  825. else
  826. bCopyTheChar = FALSE;
  827. // toggle quoted status
  828. bInQuote = !bInQuote;
  829. }
  830. nSlash /= 2;
  831. }
  832. while (nSlash)
  833. {
  834. *pDst++ = _T('\\');
  835. nSlash--;
  836. }
  837. if (_T('\0') == *p || (!bInQuote && (_T(' ') == *p || _T('\t') == *p)))
  838. {
  839. break;
  840. }
  841. // copy char to args
  842. if (bCopyTheChar)
  843. {
  844. *pDst++ = *p;
  845. }
  846. p++;
  847. }
  848. // we have a complete argument now. Null terminates it and
  849. // let the derived class parse the argument.
  850. *pDst = _T('\0');
  851. // skip blanks to see if this is the last argument
  852. while (_T(' ') == *p || _T('\t') == *p)
  853. p++;
  854. BOOL bFlag;
  855. bFlag = (_T('/') == *args || _T('-') == *args);
  856. pDst = (bFlag) ? args + 1 : args;
  857. ParseParam(pDst, bFlag);
  858. }
  859. delete [] args;
  860. }
  861. //
  862. // CSafeRegistry implementation
  863. //
  864. BOOL
  865. CSafeRegistry::Open(
  866. HKEY hKeyAncestor,
  867. LPCTSTR KeyName,
  868. REGSAM Access
  869. )
  870. {
  871. DWORD LastError;
  872. // we shouldn't have a valid key -- or memory leak
  873. // Also, a key name must be provided -- or open nothing
  874. ASSERT(!m_hKey && KeyName);
  875. LastError = ::RegOpenKeyEx(hKeyAncestor, KeyName, 0, Access, &m_hKey);
  876. SetLastError(LastError);
  877. return ERROR_SUCCESS == LastError;
  878. }
  879. BOOL
  880. CSafeRegistry::Create(
  881. HKEY hKeyAncestor,
  882. LPCTSTR KeyName,
  883. REGSAM Access,
  884. DWORD* pDisposition,
  885. DWORD Options,
  886. LPSECURITY_ATTRIBUTES pSecurity
  887. )
  888. {
  889. ASSERT(KeyName && !m_hKey);
  890. DWORD Disposition;
  891. DWORD LastError;
  892. LastError = ::RegCreateKeyEx(hKeyAncestor, KeyName, 0, TEXT(""),
  893. Options, Access, pSecurity,
  894. &m_hKey, &Disposition
  895. );
  896. SetLastError(LastError);
  897. if (ERROR_SUCCESS == LastError && pDisposition)
  898. {
  899. *pDisposition = Disposition;
  900. }
  901. if (ERROR_SUCCESS != LastError)
  902. m_hKey = NULL;
  903. return ERROR_SUCCESS == LastError;
  904. }
  905. BOOL
  906. CSafeRegistry::SetValue(
  907. LPCTSTR ValueName,
  908. DWORD Type,
  909. const PBYTE pData,
  910. DWORD DataLen
  911. )
  912. {
  913. ASSERT(m_hKey);
  914. DWORD LastError;
  915. LastError = ::RegSetValueEx(m_hKey, ValueName, 0, Type, pData, DataLen);
  916. SetLastError(LastError);
  917. return ERROR_SUCCESS == LastError;
  918. }
  919. BOOL
  920. CSafeRegistry::SetValue(
  921. LPCTSTR ValueName,
  922. LPCTSTR Value
  923. )
  924. {
  925. return SetValue(ValueName,
  926. REG_SZ,
  927. (PBYTE)Value,
  928. (lstrlen(Value) + 1) * sizeof(TCHAR)
  929. );
  930. }
  931. BOOL
  932. CSafeRegistry::GetValue(
  933. LPCTSTR ValueName,
  934. DWORD* pType,
  935. const PBYTE pData,
  936. DWORD* pDataLen
  937. )
  938. {
  939. ASSERT(m_hKey);
  940. DWORD LastError;
  941. LastError = ::RegQueryValueEx(m_hKey, ValueName, NULL, pType, pData,
  942. pDataLen);
  943. SetLastError(LastError);
  944. return ERROR_SUCCESS == LastError;
  945. }
  946. BOOL
  947. CSafeRegistry::GetValue(
  948. LPCTSTR ValueName,
  949. String& str
  950. )
  951. {
  952. DWORD Type, Size;
  953. Size = 0;
  954. BOOL Result = FALSE;
  955. // check size before Type because when the size is zero, type contains
  956. // undefined data.
  957. if (GetValue(ValueName, &Type, NULL, &Size) && Size && REG_SZ == Type)
  958. {
  959. // we do not want to throw an exception here.
  960. // so guard it
  961. try
  962. {
  963. BufferPtr<BYTE> BufferPtr(Size);
  964. Result = GetValue(ValueName, &Type, BufferPtr, &Size);
  965. if (Result)
  966. str = (LPCTSTR)(BYTE*)BufferPtr;
  967. }
  968. catch(CMemoryException* e)
  969. {
  970. e->Delete();
  971. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  972. Result = FALSE;
  973. }
  974. }
  975. return Result;
  976. }
  977. BOOL
  978. CSafeRegistry::EnumerateSubkey(
  979. DWORD Index,
  980. LPTSTR Buffer,
  981. DWORD* BufferSize
  982. )
  983. {
  984. DWORD LastError;
  985. FILETIME LastWrite;
  986. LastError = ::RegEnumKeyEx(m_hKey, Index, Buffer, BufferSize,
  987. NULL, NULL, NULL, &LastWrite);
  988. SetLastError(LastError);
  989. return ERROR_SUCCESS == LastError;
  990. }
  991. BOOL
  992. CSafeRegistry::DeleteValue(
  993. LPCTSTR ValueName
  994. )
  995. {
  996. ASSERT(m_hKey);
  997. DWORD LastError = ::RegDeleteValue(m_hKey, ValueName);
  998. SetLastError(LastError);
  999. return ERROR_SUCCESS == LastError;
  1000. }
  1001. BOOL
  1002. CSafeRegistry::DeleteSubkey(
  1003. LPCTSTR SubkeyName
  1004. )
  1005. {
  1006. ASSERT(m_hKey);
  1007. CSafeRegistry regSubkey;
  1008. TCHAR KeyName[MAX_PATH];
  1009. FILETIME LastWrite;
  1010. DWORD KeyNameLen;
  1011. for (;;)
  1012. {
  1013. KeyNameLen = ARRAYLEN(KeyName);
  1014. // always uses index 0(the first subkey)
  1015. if (!regSubkey.Open(m_hKey, SubkeyName, KEY_WRITE | KEY_ENUMERATE_SUB_KEYS) ||
  1016. ERROR_SUCCESS != ::RegEnumKeyEx(regSubkey, 0, KeyName,
  1017. &KeyNameLen, NULL, NULL, NULL,
  1018. &LastWrite) ||
  1019. !regSubkey.DeleteSubkey(KeyName))
  1020. {
  1021. break;
  1022. }
  1023. // close the key so that we will re-open it on each loop
  1024. // -- we have deleted one subkey and without closing
  1025. // the key, the index to RegEnumKeyEx will be confusing
  1026. regSubkey.Close();
  1027. }
  1028. // now delete the subkey
  1029. ::RegDeleteKey(m_hKey, SubkeyName);
  1030. return TRUE;
  1031. }