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.

1054 lines
23 KiB

  1. /*++
  2. Copyright (C) 2001 Microsoft Corporation
  3. All rights reserved.
  4. Module Name:
  5. util.cxx
  6. Abstract:
  7. Contains several utility functions.
  8. Author:
  9. Albert Ting (AlbertT) 25-Sept-1996 pAllocRead()
  10. Felix Maxa (AMaxa) 11-Sept-2001 Moved pAllocRead() from alloc.*xx to util.*xx and
  11. added the rest of the functions.
  12. --*/
  13. #include "precomp.hxx"
  14. #pragma hdrstop
  15. #include "clusinfo.hxx"
  16. PCWSTR g_pszSpoolerResource = L"Print Spooler";
  17. /*++
  18. Routine Name:
  19. pAllocRead
  20. Routine Description:
  21. Generic realloc code for any api that can fail with ERROR_INSUFFICIENT_BUFFER.
  22. Arguments:
  23. Return Value:
  24. Last Error:
  25. --*/
  26. PBYTE
  27. pAllocRead(
  28. HANDLE hUserData,
  29. ALLOC_FUNC AllocFunc,
  30. DWORD dwLenHint,
  31. PDWORD pdwLen OPTIONAL
  32. )
  33. {
  34. ALLOC_DATA AllocData;
  35. PBYTE pBufferOut = NULL;
  36. DWORD dwLastError;
  37. DWORD cbActual;
  38. if( pdwLen ){
  39. *pdwLen = 0;
  40. }
  41. if( !dwLenHint ){
  42. DBGMSG( DBG_ERROR, ( "ReallocRead: dwLenHint = 0\n" ));
  43. SetLastError( ERROR_INVALID_PARAMETER );
  44. return FALSE;
  45. }
  46. AllocData.pBuffer = NULL;
  47. AllocData.cbBuffer = dwLenHint;
  48. for( ; ; ){
  49. cbActual = AllocData.cbBuffer;
  50. AllocData.pBuffer = (PBYTE)LocalAlloc( LMEM_FIXED, cbActual );
  51. if( !AllocData.pBuffer ){
  52. break;
  53. }
  54. if( !AllocFunc( hUserData, &AllocData )){
  55. //
  56. // Call failed.
  57. //
  58. dwLastError = GetLastError();
  59. LocalFree( (HLOCAL)AllocData.pBuffer );
  60. if( dwLastError != ERROR_INSUFFICIENT_BUFFER &&
  61. dwLastError != ERROR_MORE_DATA ){
  62. break;
  63. }
  64. } else {
  65. pBufferOut = AllocData.pBuffer;
  66. if( pdwLen ){
  67. *pdwLen = cbActual;
  68. }
  69. break;
  70. }
  71. }
  72. return pBufferOut;
  73. }
  74. /*++
  75. Name:
  76. GetSubkeyBuffer
  77. Description:
  78. Allocates a buffer that can accomodate the larges subkey under hKey.
  79. This function is adapted from the library in CSR
  80. Arguments:
  81. hKey - registry key
  82. ppBuffer - pointer to where to store pointer to WCHAR
  83. pnSize - pointer to where to store the size in WCHARs of the ppBuffer
  84. Return Value:
  85. ERROR_SUCCESS - a buffer was allocated and needs to be freed by the caller
  86. other win32 error - an error occurred
  87. --*/
  88. LONG
  89. GetSubkeyBuffer(
  90. IN HKEY hKey,
  91. IN PWSTR *ppBuffer,
  92. IN DWORD *pnSize
  93. )
  94. {
  95. LONG Status = RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, pnSize, NULL, NULL, NULL, NULL, NULL, NULL);
  96. if (Status == ERROR_SUCCESS)
  97. {
  98. *pnSize = *pnSize + 1;
  99. *ppBuffer = new WCHAR[*pnSize];
  100. if (!*ppBuffer)
  101. {
  102. Status = ERROR_NOT_ENOUGH_MEMORY;
  103. }
  104. }
  105. return Status;
  106. }
  107. /*++
  108. Name:
  109. DeleteKeyRecursive
  110. Description:
  111. Deletes a regsitry key and all its subkeys. This function is copied from the library in CSR.
  112. Arguments:
  113. kHey - registry key
  114. pszSubkey - subkey to be deleted
  115. Return Value:
  116. ERROR_SUCCESS - the subkey was deleted
  117. other Win32 error - an error occurred
  118. --*/
  119. LONG
  120. DeleteKeyRecursive(
  121. IN HKEY hKey,
  122. IN PCWSTR pszSubkey
  123. )
  124. {
  125. HKEY hSubkey = NULL;
  126. LONG Status = ERROR_SUCCESS;
  127. Status = RegOpenKeyEx(hKey, pszSubkey, 0, KEY_ALL_ACCESS, &hSubkey);
  128. while (Status == ERROR_SUCCESS)
  129. {
  130. PWSTR pBuffer = NULL;
  131. DWORD nBufferSize = 0;
  132. Status = GetSubkeyBuffer(hSubkey, &pBuffer, &nBufferSize);
  133. if (Status == ERROR_SUCCESS)
  134. {
  135. Status = RegEnumKeyEx(hSubkey, 0, pBuffer, &nBufferSize, 0, 0, 0, 0);
  136. if (Status == ERROR_SUCCESS)
  137. {
  138. Status = DeleteKeyRecursive(hSubkey, pBuffer);
  139. }
  140. delete [] pBuffer;
  141. }
  142. }
  143. if (hSubkey)
  144. {
  145. RegCloseKey(hSubkey);
  146. }
  147. if (Status == ERROR_NO_MORE_ITEMS)
  148. {
  149. Status = ERROR_SUCCESS;
  150. }
  151. if (Status == ERROR_SUCCESS)
  152. {
  153. Status = RegDeleteKey (hKey, pszSubkey);
  154. }
  155. return Status;
  156. }
  157. /*++
  158. Description:
  159. This routine concatenates a set of null terminated strings
  160. into the provided buffer. The last argument must be a NULL
  161. to signify the end of the argument list.
  162. This function is copied from the spllib in printscan
  163. Arguments:
  164. pszBuffer - pointer buffer where to place the concatenated
  165. string.
  166. cchBuffer - character count of the provided buffer including
  167. the null terminator.
  168. ... - variable number of string to concatenate.
  169. Returns:
  170. ERROR_SUCCESS if new concatenated string is returned,
  171. or ERROR_XXX if an error occurred.
  172. Notes:
  173. The caller must pass valid strings as arguments to this routine,
  174. if an integer or other parameter is passed the routine will either
  175. crash or fail abnormally. Since this is an internal routine
  176. we are not in try except block for performance reasons.
  177. --*/
  178. DWORD
  179. WINAPIV
  180. StrNCatBuff(
  181. IN PWSTR pszBuffer,
  182. IN UINT cchBuffer,
  183. ...
  184. )
  185. {
  186. DWORD dwRetval = ERROR_INVALID_PARAMETER;
  187. PCWSTR pszTemp = NULL;
  188. PWSTR pszDest = NULL;
  189. va_list pArgs;
  190. //
  191. // Validate the pointer where to return the buffer.
  192. //
  193. if (pszBuffer && cchBuffer)
  194. {
  195. //
  196. // Assume success.
  197. //
  198. dwRetval = ERROR_SUCCESS;
  199. //
  200. // Get pointer to argument frame.
  201. //
  202. va_start(pArgs, cchBuffer);
  203. //
  204. // Get temp destination pointer.
  205. //
  206. pszDest = pszBuffer;
  207. //
  208. // Insure we have space for the null terminator.
  209. //
  210. cchBuffer--;
  211. //
  212. // Collect all the arguments.
  213. //
  214. for ( ; ; )
  215. {
  216. //
  217. // Get pointer to the next argument.
  218. //
  219. pszTemp = va_arg(pArgs, PCWSTR);
  220. if (!pszTemp)
  221. {
  222. break;
  223. }
  224. //
  225. // Copy the data into the destination buffer.
  226. //
  227. for ( ; cchBuffer; cchBuffer-- )
  228. {
  229. if (!(*pszDest = *pszTemp))
  230. {
  231. break;
  232. }
  233. pszDest++, pszTemp++;
  234. }
  235. //
  236. // If were unable to write all the strings to the buffer,
  237. // set the error code and nuke the incomplete copied strings.
  238. //
  239. if (!cchBuffer && pszTemp && *pszTemp)
  240. {
  241. dwRetval = ERROR_INVALID_PARAMETER;
  242. *pszBuffer = L'\0';
  243. break;
  244. }
  245. }
  246. //
  247. // Terminate the buffer always.
  248. //
  249. *pszDest = L'\0';
  250. va_end(pArgs);
  251. }
  252. //
  253. // Set the last error in case the caller forgets to.
  254. //
  255. if (dwRetval != ERROR_SUCCESS)
  256. {
  257. SetLastError(dwRetval);
  258. }
  259. return dwRetval;
  260. }
  261. TStringArray::
  262. TStringArray(
  263. VOID
  264. ) : m_Count(0),
  265. m_pArray(NULL)
  266. {
  267. }
  268. TStringArray::
  269. ~TStringArray(
  270. VOID
  271. )
  272. {
  273. for (DWORD i = 0; i < m_Count; i++)
  274. {
  275. delete [] m_pArray[i];
  276. }
  277. delete [] m_pArray;
  278. }
  279. /*++
  280. Routine Name
  281. TStringArray::Count
  282. Routine Description:
  283. Returns the number of strings in the array
  284. Arguments:
  285. None
  286. Return Value:
  287. DWORD - Numnber of string in the array
  288. --*/
  289. DWORD
  290. TStringArray::
  291. Count(
  292. VOID
  293. ) const
  294. {
  295. return m_Count;
  296. }
  297. /*++
  298. Routine Name
  299. TStringArray::StringAt
  300. Routine Description:
  301. Returns the string at the specified position. Position must be
  302. between 0 and Count(). Otherwise the function fails and returns NULL.
  303. Arguments:
  304. Position - index of the string to be returned
  305. Return Value:
  306. NULL - if position is out of bounds
  307. PCWSTR - valid string pointer
  308. --*/
  309. PCWSTR
  310. TStringArray::
  311. StringAt(
  312. IN DWORD Position
  313. ) const
  314. {
  315. PCWSTR psz = NULL;
  316. if (Position < m_Count)
  317. {
  318. psz = m_pArray[Position];
  319. }
  320. return psz;
  321. }
  322. /*++
  323. Routine Name
  324. TStringArray::AddString
  325. Routine Description:
  326. Adds a string to the array. The function simply adds the string as the last
  327. element in the array. The function creates a copy of the string.
  328. Arguments:
  329. pszString - string to be added. Cannot be NULL or empty
  330. Return Value:
  331. ERROR_SUCCESS - the string was added in the array
  332. Win32 error - an error occurred and the string was not added
  333. --*/
  334. DWORD
  335. TStringArray::
  336. AddString(
  337. IN PCWSTR pszString
  338. )
  339. {
  340. DWORD Error = ERROR_INVALID_PARAMETER;
  341. if (pszString && *pszString)
  342. {
  343. PWSTR *pNewArray = new PWSTR[m_Count + 1];
  344. if (pNewArray)
  345. {
  346. pNewArray[m_Count] = new WCHAR[1 + wcslen(pszString)];
  347. if (pNewArray[m_Count])
  348. {
  349. for (DWORD i = 0; i < m_Count; i++)
  350. {
  351. pNewArray[i] = m_pArray[i];
  352. }
  353. StringCchCopyW(pNewArray[m_Count],
  354. (1 + wcslen(pszString)),
  355. pszString);
  356. m_Count++;
  357. delete [] m_pArray;
  358. m_pArray = pNewArray;
  359. Error = ERROR_SUCCESS;
  360. }
  361. else
  362. {
  363. Error = ERROR_NOT_ENOUGH_MEMORY;
  364. delete [] pNewArray;
  365. }
  366. }
  367. else
  368. {
  369. Error = ERROR_NOT_ENOUGH_MEMORY;
  370. }
  371. }
  372. return Error;
  373. }
  374. /*++
  375. Routine Name
  376. TStringArray::Exclude
  377. Routine Description:
  378. Excludes all occurrences of a string from the array.
  379. Arguments:
  380. pszString - string to be excluded. Cannot be NULL or empty
  381. Return Value:
  382. ERROR_SUCCESS - the string was excluded from the array
  383. Win32 error - an error occurred and the string was not excluded
  384. --*/
  385. DWORD
  386. TStringArray::
  387. Exclude(
  388. IN PCWSTR pszString
  389. )
  390. {
  391. DWORD Error = ERROR_INVALID_PARAMETER;
  392. if (pszString && *pszString)
  393. {
  394. Error = ERROR_SUCCESS;
  395. for (DWORD i = 0; i < m_Count;)
  396. {
  397. if (!ClRtlStrICmp(pszString, m_pArray[i]))
  398. {
  399. delete [] m_pArray[i];
  400. for (DWORD j = i; j < m_Count - 1; j++)
  401. {
  402. m_pArray[j] = m_pArray[j+1];
  403. }
  404. m_Count--;
  405. }
  406. else
  407. {
  408. i++;
  409. }
  410. }
  411. }
  412. return Error;
  413. }
  414. /*++
  415. Name:
  416. GetSpoolerResourceGUID
  417. Description:
  418. This function checks if pszResource is the name of a cluster spooler. If it is,
  419. then it returns the GUID associated with the cluster spooler
  420. Arguments:
  421. hCluster - handle retrieved via OpenCluster
  422. pszResource - resource name
  423. ppGUID - pointer to where to receive sequence of bytes representing a GUID
  424. *ppGUID is NULL terminated and can be used as a string. Must be
  425. freed by the caller using delete []
  426. Return Value:
  427. S_OK - pszResource is not a cluster spooler or
  428. pszResource is a cluster spooler and then **ppGUID is a valid pointer
  429. any other HRESULT - failure
  430. --*/
  431. HRESULT
  432. GetSpoolerResourceGUID(
  433. IN HCLUSTER hCluster,
  434. IN PCWSTR pszResource,
  435. OUT BYTE **ppGUID
  436. )
  437. {
  438. HRESOURCE hResource;
  439. HRESULT hRetval;
  440. hRetval = hCluster && pszResource && ppGUID ? S_OK : E_INVALIDARG;
  441. if (SUCCEEDED(hRetval))
  442. {
  443. *ppGUID = NULL;
  444. hResource = OpenClusterResource(hCluster, pszResource);
  445. hRetval = hResource ? S_OK : GetLastErrorAsHResult();
  446. }
  447. if (SUCCEEDED(hRetval))
  448. {
  449. BYTE *pResType = NULL;
  450. hRetval = ClusResControl(hResource, CLUSCTL_RESOURCE_GET_RESOURCE_TYPE, &pResType, NULL);
  451. if (SUCCEEDED(hRetval))
  452. {
  453. //
  454. // Check resource type. We are interested only in IP Address resources.
  455. //
  456. if (!ClRtlStrICmp(reinterpret_cast<PWSTR>(pResType), g_pszSpoolerResource))
  457. {
  458. PWSTR pszIPAddress = NULL;
  459. DWORD cbResProp = 0;
  460. //
  461. // Get all the private properties of the IP Address resource.
  462. //
  463. hRetval = ClusResControl(hResource,
  464. CLUSCTL_RESOURCE_GET_ID,
  465. ppGUID,
  466. &cbResProp);
  467. }
  468. delete [] pResType;
  469. }
  470. CloseClusterResource(hResource);
  471. }
  472. return hRetval;
  473. }
  474. /*++
  475. Name:
  476. ClusResControl
  477. Description:
  478. Helper function. Encapsulates a call to ClusterResourceControl. The function
  479. allocates a buffer. Upon success, the caller nedds to free the buffer using
  480. delete [].
  481. Arguments:
  482. hResource - handle to cluster resource
  483. ControlCode - control code for ClusterResourceControl
  484. ppBuffer - pointer to address where to store byte array
  485. pcBytesReturned - number of bytes returned by ClusterResourceControl (not
  486. necessarily the number of byes allocated for *ppBuffer)
  487. Return Value:
  488. S_OK - success. ppBuffer can be used and must be freed using delete []
  489. any other HRESULT - failure
  490. --*/
  491. HRESULT
  492. ClusResControl(
  493. IN HRESOURCE hResource,
  494. IN DWORD ControlCode,
  495. OUT BYTE **ppBuffer,
  496. IN DWORD *pcBytesReturned OPTIONAL
  497. )
  498. {
  499. HRESULT hRetval;
  500. hRetval = ppBuffer ? S_OK : E_INVALIDARG;
  501. if (SUCCEEDED(hRetval))
  502. {
  503. DWORD Error;
  504. DWORD cbBuffer = kBufferAllocHint;
  505. DWORD cbNeeded = 0;
  506. *ppBuffer = new BYTE[cbBuffer];
  507. Error = *ppBuffer ? ERROR_SUCCESS : ERROR_NOT_ENOUGH_MEMORY;
  508. if (Error == ERROR_SUCCESS)
  509. {
  510. Error = ClusterResourceControl(hResource,
  511. NULL,
  512. ControlCode,
  513. NULL,
  514. 0,
  515. *ppBuffer,
  516. cbBuffer,
  517. &cbNeeded);
  518. if (Error == ERROR_MORE_DATA)
  519. {
  520. cbBuffer = cbNeeded;
  521. delete [] *ppBuffer;
  522. *ppBuffer = new BYTE[cbBuffer];
  523. Error = *ppBuffer ? ERROR_SUCCESS : ERROR_NOT_ENOUGH_MEMORY;
  524. if (Error == ERROR_SUCCESS)
  525. {
  526. Error = ClusterResourceControl(hResource,
  527. NULL,
  528. ControlCode,
  529. NULL,
  530. 0,
  531. *ppBuffer,
  532. cbBuffer,
  533. &cbNeeded);
  534. }
  535. }
  536. if (Error != ERROR_SUCCESS)
  537. {
  538. delete [] *ppBuffer;
  539. *ppBuffer = NULL;
  540. cbNeeded = 0;
  541. }
  542. if (pcBytesReturned)
  543. {
  544. *pcBytesReturned = cbNeeded;
  545. }
  546. }
  547. hRetval = HRESULT_FROM_WIN32(Error);
  548. }
  549. return hRetval;
  550. }
  551. /*++
  552. Routine Name:
  553. GetCurrentNodeName
  554. Routine Description:
  555. Allocates a buffer and fills it in with the name of the current node.
  556. Arguments:
  557. ppOut - pointer to where to store a PWSTR. Must be freed with delete []
  558. Return Value:
  559. ERROR_SUCCESS - a string was allocated, must be freed with delete []
  560. other Win32 error - an error occurred
  561. --*/
  562. DWORD
  563. GetCurrentNodeName(
  564. OUT PWSTR *ppOut
  565. )
  566. {
  567. DWORD Error = ERROR_INVALID_PARAMETER;
  568. if (ppOut)
  569. {
  570. DWORD cch = 0;
  571. *ppOut = NULL;
  572. if (!GetComputerName(NULL, &cch) && GetLastError() == ERROR_BUFFER_OVERFLOW)
  573. {
  574. *ppOut = new WCHAR[cch];
  575. if (*ppOut)
  576. {
  577. if (!GetComputerName(*ppOut, &cch))
  578. {
  579. delete [] *ppOut;
  580. *ppOut = NULL;
  581. Error = GetLastError();
  582. }
  583. else
  584. {
  585. Error = ERROR_SUCCESS;
  586. }
  587. }
  588. else
  589. {
  590. Error = ERROR_NOT_ENOUGH_MEMORY;
  591. }
  592. }
  593. else
  594. {
  595. Error = ERROR_INVALID_FUNCTION;
  596. }
  597. }
  598. return Error;
  599. }
  600. /*++
  601. Routine Name
  602. GetLastErrorAsHResult
  603. Routine Description:
  604. Returns the last error as an HRESULT
  605. Arguments:
  606. NONE
  607. Return Value:
  608. HRESULT
  609. --*/
  610. HRESULT
  611. GetLastErrorAsHResult(
  612. VOID
  613. )
  614. {
  615. DWORD d = GetLastError();
  616. return HRESULT_FROM_WIN32(d);
  617. }
  618. /*++
  619. Routine Name
  620. IsGUIDString
  621. Routine Description:
  622. Checks if a string is a valid GUID of the following format
  623. 361a22fd-9cb0-4d22-8a68-6c6fb3f22363
  624. Arguments:
  625. pszString - string
  626. Return Value:
  627. TRUE - pszString represents a guid
  628. FALSE - otherwise
  629. --*/
  630. BOOL
  631. IsGUIDString(
  632. IN PCWSTR pszString
  633. )
  634. {
  635. BOOL bRet = FALSE;
  636. CONST DWORD cchGUID = 36; // number of characters in a GUID of the format 361a22fd-9cb0-4d22-8a68-6c6fb3f22363
  637. if (pszString && *pszString && wcslen(pszString) == cchGUID)
  638. {
  639. bRet = TRUE;
  640. for (DWORD i = 0; bRet && i < cchGUID; i++)
  641. {
  642. if (i == 8 || i == 13 || i == 18 || i == 23)
  643. {
  644. bRet = pszString[i] == L'-';
  645. }
  646. else
  647. {
  648. bRet = pszString[i] >= L'0' && pszString[i] <= L'9' ||
  649. pszString[i] >= L'a' && pszString[i] <= L'f' ||
  650. pszString[i] >= L'A' && pszString[i] <= L'F';
  651. }
  652. }
  653. }
  654. return bRet;
  655. }
  656. /*++
  657. Routine Name
  658. DelOrMoveFile
  659. Routine Description:
  660. Deletes a file or a directory. If the file is read only, the function resets the file
  661. attrbiutes so the file can be removed. If the file is in use, then it is marked for
  662. deletion on reboot.
  663. Arguments:
  664. pszFile - directory or file name
  665. Return Value:
  666. ERROR_SUCCESS - the file/directory was deleted or marked for deletion
  667. other Win32 code - an error occurred.
  668. --*/
  669. DWORD
  670. DelOrMoveFile(
  671. IN PCWSTR pszFile
  672. )
  673. {
  674. DWORD Error = ERROR_INVALID_PARAMETER;
  675. if (pszFile)
  676. {
  677. Error = ERROR_SUCCESS;
  678. DWORD Attributes = GetFileAttributes(pszFile);
  679. if (Attributes == 0xFFFFFFFF)
  680. {
  681. Error = GetLastError();
  682. }
  683. else if (Attributes & FILE_ATTRIBUTE_READONLY)
  684. {
  685. if (!SetFileAttributes(pszFile, Attributes & ~FILE_ATTRIBUTE_READONLY))
  686. {
  687. Error = GetLastError();
  688. }
  689. }
  690. if (Error == ERROR_SUCCESS)
  691. {
  692. if (Attributes & FILE_ATTRIBUTE_DIRECTORY ? !RemoveDirectory(pszFile) : !DeleteFile(pszFile))
  693. {
  694. if (!MoveFileEx(pszFile, NULL, MOVEFILE_DELAY_UNTIL_REBOOT))
  695. {
  696. Error = GetLastError();
  697. }
  698. }
  699. }
  700. }
  701. return Error;
  702. }
  703. /*++
  704. Routine Name
  705. DelDirRecursively
  706. Routine Description:
  707. Deletes recursively all the files and subdirectories of a given directory. It also
  708. deletes the directory itself. If any files are in use, the they are marked for
  709. deletion on reboot.
  710. Arguments:
  711. pszDir - directory name. cannot be NULL
  712. Return Value:
  713. ERROR_SUCCESS - the files and subdirectories were deleted or marked for deletion
  714. other Win32 code - an error occurred.
  715. --*/
  716. DWORD
  717. DelDirRecursively(
  718. IN PCWSTR pszDir
  719. )
  720. {
  721. DWORD Error = ERROR_INVALID_PARAMETER;
  722. WCHAR Scratch[MAX_PATH];
  723. if (pszDir)
  724. {
  725. if ((Error = StrNCatBuff(Scratch,
  726. MAX_PATH,
  727. pszDir,
  728. L"\\*",
  729. NULL)) == ERROR_SUCCESS)
  730. {
  731. HANDLE hFindFile;
  732. WIN32_FIND_DATA FindData;
  733. //
  734. // Enumerate all the files in the directory
  735. //
  736. hFindFile = FindFirstFile(Scratch, &FindData);
  737. if (hFindFile != INVALID_HANDLE_VALUE)
  738. {
  739. do
  740. {
  741. if ((Error = StrNCatBuff(Scratch,
  742. MAX_PATH,
  743. pszDir,
  744. L"\\",
  745. FindData.cFileName,
  746. NULL)) == ERROR_SUCCESS)
  747. {
  748. if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  749. {
  750. //
  751. // skip the special . and .. entries
  752. //
  753. if (wcscmp(FindData.cFileName, L".") && wcscmp(FindData.cFileName, L".."))
  754. {
  755. Error = DelDirRecursively(Scratch);
  756. }
  757. }
  758. else
  759. {
  760. Error = DelOrMoveFile(Scratch);
  761. }
  762. }
  763. } while (Error == ERROR_SUCCESS && FindNextFile(hFindFile, &FindData));
  764. FindClose(hFindFile);
  765. //
  766. // Delete the directory itself
  767. //
  768. if (Error == ERROR_SUCCESS)
  769. {
  770. Error = DelOrMoveFile(pszDir);
  771. }
  772. }
  773. else
  774. {
  775. Error = GetLastError();
  776. }
  777. }
  778. }
  779. return Error;
  780. }