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.

827 lines
23 KiB

  1. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2. //
  3. // File : OSACHECK.CPP
  4. //
  5. // Synopsis: Tools to check OSATTR(s) of catalog file(s).
  6. //
  7. // History: DSIE - January 30, 2001
  8. //
  9. // Microsoft Corporation (c) Copy Rights 2001.
  10. //
  11. #include <io.h>
  12. #include <tchar.h>
  13. #include <errno.h>
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <crtdbg.h>
  17. #include <direct.h>
  18. #include <atlbase.h>
  19. #include <windows.h>
  20. #include <cryptui.h>
  21. #include <wintrust.h>
  22. ////////////////////
  23. //
  24. // macros
  25. //
  26. #define ASSERT(x) _ASSERT(x)
  27. #define ARRAYSIZE(a) (sizeof(a) / sizeof(a[0]))
  28. ////////////////////
  29. //
  30. // Global variables
  31. //
  32. _TCHAR * g_pszFilePath = _T(""); // Pointer to catalog file path.
  33. BOOL g_bCatalogOSAttrOnly = FALSE; // OSAttr listing only flag.
  34. BOOL g_bIncludeSubDir = FALSE; // Inlcude sub-dir flag.
  35. BOOL g_bIgnoreError = FALSE; // Ignore error flag.
  36. BOOL g_bVerbose = FALSE; // Verbose flag.
  37. BOOL g_bViewCatalog = FALSE; // Display catalog dialog flag.
  38. //------------------------------------------------------------------------------
  39. //
  40. // Function: DebugTrace
  41. //
  42. //------------------------------------------------------------------------------
  43. #ifdef PKIDEBUG
  44. BOOL g_bUseOutputDebugString = FALSE; // Use OutputDebugString flag.
  45. void DebugTrace (char * pszFormat, ...)
  46. {
  47. char szMessage[512] = "";
  48. va_list arglist;
  49. va_start(arglist, pszFormat);
  50. _vsnprintf(szMessage, ARRAYSIZE(szMessage), pszFormat, arglist);
  51. if (g_bUseOutputDebugString)
  52. {
  53. OutputDebugString(szMessage);
  54. }
  55. else
  56. {
  57. fprintf(stderr, szMessage);
  58. }
  59. va_end(arglist);
  60. return;
  61. }
  62. #else
  63. inline void DebugTrace (char * pszFormat, ...) {}
  64. #endif
  65. //------------------------------------------------------------------------------
  66. //
  67. // Function: DisplayHelp
  68. //
  69. //------------------------------------------------------------------------------
  70. void DisplayHelp (_TCHAR * pszFullExePath, BOOL bExtraHelp)
  71. {
  72. _TCHAR szDrive[_MAX_DRIVE] = _T("");
  73. _TCHAR szDir[_MAX_DIR] = _T("");
  74. _TCHAR szFName[_MAX_FNAME] = _T("");
  75. _TCHAR szExt[_MAX_EXT] = _T("");
  76. _TCHAR * pszExeName = _T("OSACheck");
  77. if (pszFullExePath)
  78. {
  79. _splitpath(pszFullExePath, szDrive, szDir, szFName, szExt);
  80. pszExeName = szFName;
  81. }
  82. _ftprintf(stderr, _T("Usage: %s [drive:][path][filename] [options]\n"), pszExeName);
  83. _ftprintf(stderr, _T("\n"));
  84. _ftprintf(stderr, _T(" [drive:][path][filename]\n"));
  85. _ftprintf(stderr, _T(" Specifies drive, directory, and/or catalog files to scan.\n"));
  86. _ftprintf(stderr, _T("\n"));
  87. _ftprintf(stderr, _T(" [options]\n"));
  88. _ftprintf(stderr, _T(" -c List only catalog OSAttrs (no file listing).\n"));
  89. _ftprintf(stderr, _T(" -s Scan catalog files in specified directory and all subdirectories.\n"));
  90. _ftprintf(stderr, _T(" -i Ignore error and continue with next catalog file.\n"));
  91. _ftprintf(stderr, _T(" -v Verbose.\n"));
  92. if (bExtraHelp)
  93. {
  94. #ifdef PKIDEBUG
  95. _ftprintf(stderr, _T(" ~d Display catalog dialog.\n"));
  96. _ftprintf(stderr, _T(" ~o Use OutputDebugString() for debug trace.\n"));
  97. #endif
  98. _ftprintf(stderr, _T(" ~h This help screen.\n"));
  99. }
  100. else
  101. {
  102. _ftprintf(stderr, _T(" -h This help screen.\n"));
  103. }
  104. _ftprintf(stderr, _T("\n"));
  105. _ftprintf(stderr, _T("Note: If filename is not provided, *.CAT is assumed.\n"));
  106. _ftprintf(stderr, _T("\n"));
  107. return;
  108. }
  109. //------------------------------------------------------------------------------
  110. //
  111. // Function: ParseCommandLine
  112. //
  113. //------------------------------------------------------------------------------
  114. int ParseCommandLine (int argc, _TCHAR * argv[])
  115. {
  116. int nResult = 0;
  117. BOOL bDisplayHelp = FALSE;
  118. BOOL bExtraHelp = FALSE;
  119. ASSERT(argc);
  120. for (int i = 1; i < argc; i++)
  121. {
  122. ASSERT(argv[i]);
  123. if (_T('-') == argv[i][0] || _T('/') == argv[i][0])
  124. {
  125. switch (toupper(argv[i][1]))
  126. {
  127. case _T('C'):
  128. {
  129. g_bCatalogOSAttrOnly = TRUE;
  130. break;
  131. }
  132. case _T('I'):
  133. {
  134. g_bIgnoreError = TRUE;
  135. break;
  136. }
  137. case _T('S'):
  138. {
  139. g_bIncludeSubDir = TRUE;
  140. break;
  141. }
  142. case _T('V'):
  143. {
  144. g_bVerbose = TRUE;
  145. break;
  146. }
  147. case _T('?'):
  148. case _T('H'):
  149. default:
  150. {
  151. nResult = -1;
  152. bDisplayHelp = TRUE;
  153. break;
  154. }
  155. }
  156. }
  157. else if (_T('~') == argv[i][0])
  158. {
  159. switch (toupper(argv[i][1]))
  160. {
  161. case _T('D'):
  162. {
  163. g_bViewCatalog = TRUE;
  164. break;
  165. }
  166. #ifdef PKIDEBUG
  167. case _T('O'):
  168. {
  169. g_bUseOutputDebugString = TRUE;
  170. break;
  171. }
  172. #endif
  173. case _T('?'):
  174. case _T('H'):
  175. default:
  176. {
  177. nResult = -1;
  178. bExtraHelp = TRUE;
  179. bDisplayHelp = TRUE;
  180. break;
  181. }
  182. }
  183. }
  184. else if (0 == _tcslen(g_pszFilePath))
  185. {
  186. g_pszFilePath = argv[i];
  187. if (NULL == _tcschr(g_pszFilePath, _T('*')) && NULL == strchr(g_pszFilePath, _T('?')))
  188. {
  189. long hFile;
  190. struct _finddata_t fd;
  191. if (-1 != (hFile = _tfindfirst(g_pszFilePath, &fd)))
  192. {
  193. if (_A_SUBDIR & fd.attrib)
  194. {
  195. if (_T('\\') != g_pszFilePath[_tcslen(g_pszFilePath) - 1])
  196. {
  197. if (g_pszFilePath = (_TCHAR *) malloc((_tcslen(g_pszFilePath) +
  198. _tcslen(_T("\\")) + 1) *
  199. sizeof(_TCHAR)))
  200. {
  201. _tcscpy(g_pszFilePath, argv[i]);
  202. _tcscat(g_pszFilePath, _T("\\"));
  203. }
  204. else
  205. {
  206. nResult = -2;
  207. }
  208. }
  209. }
  210. _findclose(hFile);
  211. }
  212. }
  213. }
  214. else
  215. {
  216. nResult = -3;
  217. bDisplayHelp = TRUE;
  218. }
  219. if (bDisplayHelp)
  220. {
  221. DisplayHelp(argv[0], bExtraHelp);
  222. break;
  223. }
  224. }
  225. return nResult;
  226. }
  227. //------------------------------------------------------------------------------
  228. //
  229. // Function: ViewCatalog
  230. //
  231. //------------------------------------------------------------------------------
  232. int ViewCatalog (PCCTL_CONTEXT pCTLContext)
  233. {
  234. CRYPTUI_VIEWCTL_STRUCT ViewCTLStruct;
  235. ASSERT(pCTLContext);
  236. memset(&ViewCTLStruct, 0, sizeof(ViewCTLStruct));
  237. ViewCTLStruct.dwSize = sizeof(ViewCTLStruct);
  238. ViewCTLStruct.pCTLContext = pCTLContext;
  239. CryptUIDlgViewCTL(&ViewCTLStruct);
  240. return 0;
  241. }
  242. //------------------------------------------------------------------------------
  243. //
  244. // Function: DecodeObject
  245. //
  246. //------------------------------------------------------------------------------
  247. int DecodeObject (LPCSTR pszStructType,
  248. BYTE * pbEncoded,
  249. DWORD cbEncoded,
  250. CRYPT_DATA_BLOB * pDecodedBlob)
  251. {
  252. int nResult = 0;
  253. DWORD cbDecoded = 0;
  254. BYTE * pbDecoded = NULL;
  255. ASSERT(pszStructType);
  256. ASSERT(pbEncoded);
  257. ASSERT(pDecodedBlob);
  258. __try
  259. {
  260. pDecodedBlob->cbData = 0;
  261. pDecodedBlob->pbData = NULL;
  262. if (!CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
  263. pszStructType,
  264. (const BYTE *) pbEncoded,
  265. cbEncoded,
  266. 0,
  267. NULL,
  268. &cbDecoded))
  269. {
  270. nResult = GetLastError();
  271. DebugTrace("\nError [%#x]: CryptDecodeObject() failed.\n", nResult);
  272. __leave;
  273. }
  274. if (!(pbDecoded = (BYTE *) malloc(cbDecoded)))
  275. {
  276. nResult = E_OUTOFMEMORY;
  277. DebugTrace("\nError: out of memory.\n");
  278. __leave;
  279. }
  280. if (!CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
  281. pszStructType,
  282. (const BYTE *) pbEncoded,
  283. cbEncoded,
  284. 0,
  285. pbDecoded,
  286. &cbDecoded))
  287. {
  288. nResult = GetLastError();
  289. DebugTrace("\nError [%#x]: CryptDecodeObject() failed.\n", nResult);
  290. __leave;
  291. }
  292. pDecodedBlob->cbData = cbDecoded;
  293. pDecodedBlob->pbData = pbDecoded;
  294. }
  295. __finally
  296. {
  297. if (nResult && pbDecoded)
  298. {
  299. free(pbDecoded);
  300. }
  301. }
  302. return nResult;
  303. }
  304. //------------------------------------------------------------------------------
  305. //
  306. // Function: ProcessFileOSAttr
  307. //
  308. //------------------------------------------------------------------------------
  309. int ProcessFileOSAttr (PCTL_INFO pCTLInfo)
  310. {
  311. int nResult = 0;
  312. ASSERT(pCTLInfo);
  313. for (DWORD i = 0; i < pCTLInfo->cCTLEntry; i++)
  314. {
  315. DWORD cOSAttr = 0;
  316. DWORD cFiles = 0;
  317. PCTL_ENTRY pCTLEntry = &pCTLInfo->rgCTLEntry[i];
  318. if (i)
  319. {
  320. if (g_bVerbose)
  321. {
  322. _ftprintf(stdout, _T("\n%-10s"), _T(""));
  323. }
  324. else
  325. {
  326. _ftprintf(stdout, _T("\n%-14s"), _T(""));
  327. }
  328. }
  329. _ftprintf(stdout, _T("%-40S "), pCTLEntry->SubjectIdentifier.pbData);
  330. for (DWORD j = 0; j < pCTLEntry->cAttribute; j++)
  331. {
  332. PCRYPT_ATTRIBUTE pAttribute = &pCTLEntry->rgAttribute[j];
  333. CRYPT_DATA_BLOB DataBlob = {0, NULL};
  334. PCAT_NAMEVALUE pCATNameValue = NULL;
  335. if (0 != strcmp(pAttribute->pszObjId, CAT_NAMEVALUE_OBJID))
  336. {
  337. continue;
  338. }
  339. if (0 != (nResult = DecodeObject(CAT_NAMEVALUE_STRUCT,
  340. pAttribute->rgValue[0].pbData,
  341. pAttribute->rgValue[0].cbData,
  342. &DataBlob)))
  343. {
  344. return nResult;
  345. }
  346. pCATNameValue = (PCAT_NAMEVALUE) DataBlob.pbData;
  347. if (0 == wcscmp(L"File", pCATNameValue->pwszTag))
  348. {
  349. j = pCTLEntry->cAttribute;
  350. _ftprintf(stdout, _T("%-15S "), pCATNameValue->Value.pbData);
  351. cFiles++;
  352. }
  353. free(DataBlob.pbData);
  354. }
  355. if (0 == cFiles)
  356. {
  357. _ftprintf(stdout, _T("%-15s "), "");
  358. }
  359. for (j = 0; j < pCTLEntry->cAttribute; j++)
  360. {
  361. PCRYPT_ATTRIBUTE pAttribute = &pCTLEntry->rgAttribute[j];
  362. CRYPT_DATA_BLOB DataBlob = {0, NULL};
  363. PCAT_NAMEVALUE pCATNameValue = NULL;
  364. if (0 != strcmp(pAttribute->pszObjId, CAT_NAMEVALUE_OBJID))
  365. {
  366. continue;
  367. }
  368. if (0 != (nResult = DecodeObject(CAT_NAMEVALUE_STRUCT,
  369. pAttribute->rgValue[0].pbData,
  370. pAttribute->rgValue[0].cbData,
  371. &DataBlob)))
  372. {
  373. return nResult;
  374. }
  375. pCATNameValue = (PCAT_NAMEVALUE) DataBlob.pbData;
  376. if (0 == wcscmp(L"OSAttr", pCATNameValue->pwszTag))
  377. {
  378. j = pCTLEntry->cAttribute;
  379. _ftprintf(stdout, _T("%S "), pCATNameValue->Value.pbData);
  380. cOSAttr++;
  381. }
  382. free(DataBlob.pbData);
  383. }
  384. }
  385. return nResult;
  386. }
  387. //------------------------------------------------------------------------------
  388. //
  389. // Function: ProcessCatalogOSAttr
  390. //
  391. //------------------------------------------------------------------------------
  392. int ProcessCatalogOSAttr (PCTL_INFO pCTLInfo)
  393. {
  394. int nResult = 0;
  395. DWORD cOSAttr = 0;
  396. ASSERT(pCTLInfo);
  397. for (DWORD i = 0; i < pCTLInfo->cExtension; i++)
  398. {
  399. PCERT_EXTENSION pExtension = &pCTLInfo->rgExtension[i];
  400. if (0 == strcmp(CAT_NAMEVALUE_OBJID, pExtension->pszObjId))
  401. {
  402. BOOL bDuplicate = TRUE;
  403. CRYPT_DATA_BLOB DataBlob = {0, NULL};
  404. PCAT_NAMEVALUE pCATNameValue = NULL;
  405. if (0 != (nResult = DecodeObject(CAT_NAMEVALUE_STRUCT,
  406. pExtension->Value.pbData,
  407. pExtension->Value.cbData,
  408. &DataBlob)))
  409. {
  410. return nResult;
  411. }
  412. pCATNameValue = (PCAT_NAMEVALUE) DataBlob.pbData;
  413. if (0 == wcscmp(L"OSAttr", pCATNameValue->pwszTag))
  414. {
  415. i = pCTLInfo->cExtension;
  416. _ftprintf(stdout, _T("%S"), pCATNameValue->Value.pbData);
  417. cOSAttr++;
  418. }
  419. free(DataBlob.pbData);
  420. }
  421. }
  422. if (0 == cOSAttr)
  423. {
  424. _ftprintf(stdout, _T("None"));
  425. }
  426. return nResult;
  427. }
  428. //------------------------------------------------------------------------------
  429. //
  430. // Function: ProcessCatalog
  431. //
  432. //------------------------------------------------------------------------------
  433. int ProcessCatalog (_TCHAR * pszFileName)
  434. {
  435. int nResult = 0;
  436. PCCTL_CONTEXT pCTLContext = NULL;
  437. ASSERT(pszFileName);
  438. __try
  439. {
  440. USES_CONVERSION;
  441. WCHAR * pwszFileName = NULL;
  442. if (g_bVerbose)
  443. {
  444. _TCHAR szCWD[_MAX_PATH] = _T("");
  445. if (_tgetcwd(szCWD, ARRAYSIZE(szCWD)))
  446. {
  447. if (szCWD[_tcslen(szCWD) - 1] != _T('\\'))
  448. {
  449. szCWD[_tcslen(szCWD) + 1] = _T('\0');
  450. szCWD[_tcslen(szCWD)] = _T('\\');
  451. }
  452. _ftprintf(stdout, _T("-------------------------------------------------------------------------------\n"));
  453. _ftprintf(stdout, _T("Catalog = %s%s\n"), szCWD, pszFileName);
  454. }
  455. }
  456. else
  457. {
  458. _ftprintf(stdout, _T("%-14s"), pszFileName);
  459. }
  460. if (NULL == (pwszFileName = T2W(pszFileName)))
  461. {
  462. nResult = E_OUTOFMEMORY;
  463. DebugTrace(_T("Error: out of memory.\n"));
  464. __leave;
  465. }
  466. if (!CryptQueryObject(CERT_QUERY_OBJECT_FILE,
  467. pwszFileName,
  468. CERT_QUERY_CONTENT_FLAG_CTL,
  469. CERT_QUERY_FORMAT_FLAG_ALL,
  470. 0,
  471. NULL,
  472. NULL,
  473. NULL,
  474. NULL,
  475. NULL,
  476. (const void **) &pCTLContext))
  477. {
  478. nResult = GetLastError();
  479. DebugTrace(_T("Error [%#x]: CryptQueryObject() failed.\n"), nResult);
  480. __leave;
  481. }
  482. if (g_bVerbose)
  483. {
  484. _ftprintf(stdout, _T("Entries = %d\n"), pCTLContext->pCtlInfo->cCTLEntry);
  485. if (g_bCatalogOSAttrOnly)
  486. {
  487. _ftprintf(stdout, _T("OSAttrs = "));
  488. }
  489. else
  490. {
  491. _ftprintf(stdout, _T("Details = "));
  492. }
  493. }
  494. if (g_bCatalogOSAttrOnly)
  495. {
  496. nResult = ProcessCatalogOSAttr(pCTLContext->pCtlInfo);
  497. }
  498. else
  499. {
  500. nResult = ProcessFileOSAttr(pCTLContext->pCtlInfo);
  501. }
  502. _ftprintf(stdout, _T("\n"));
  503. if (0 != nResult)
  504. {
  505. __leave;
  506. }
  507. if (g_bViewCatalog)
  508. {
  509. ViewCatalog(pCTLContext);
  510. }
  511. }
  512. __finally
  513. {
  514. if (pCTLContext)
  515. {
  516. CertFreeCTLContext(pCTLContext);
  517. }
  518. }
  519. return nResult;
  520. }
  521. //------------------------------------------------------------------------------
  522. //
  523. // Function: ProcessCatalogs
  524. //
  525. //------------------------------------------------------------------------------
  526. int ProcessCatalogs (_TCHAR * pszFilePath, BOOL bIncludeSubDir)
  527. {
  528. int nResult = 0;
  529. long hFile = -1;
  530. TCHAR szFilePath[_MAX_PATH] = _T("");
  531. ASSERT(pszFilePath);
  532. __try
  533. {
  534. DWORD cDirs = 0;
  535. struct _finddata_t fd;
  536. if (0 == _tcslen(pszFilePath))
  537. {
  538. _tcscpy(szFilePath, _T("*.cat"));
  539. }
  540. else
  541. {
  542. _tcscpy(szFilePath, pszFilePath);
  543. }
  544. if (-1 != (hFile = _tfindfirst(szFilePath, &fd)))
  545. {
  546. do
  547. {
  548. if (_A_SUBDIR & fd.attrib)
  549. {
  550. if (_tcscmp(_T("."), fd.name) && _tcscmp(_T(".."), fd.name))
  551. {
  552. cDirs++;
  553. }
  554. }
  555. else
  556. {
  557. if (0 != (nResult = ProcessCatalog(fd.name)))
  558. {
  559. if (g_bIgnoreError)
  560. {
  561. nResult = 0;
  562. }
  563. else
  564. {
  565. __leave;
  566. }
  567. }
  568. }
  569. } while (0 == _tfindnext(hFile, &fd));
  570. _findclose(hFile);
  571. hFile = -1;
  572. }
  573. if (bIncludeSubDir)
  574. {
  575. if (0 == cDirs)
  576. {
  577. _tcscpy(szFilePath, _T("*.*"));
  578. }
  579. if (-1 != (hFile = _tfindfirst(szFilePath, &fd)))
  580. {
  581. do
  582. {
  583. if ((_A_SUBDIR & fd.attrib) && _tcscmp(_T("."), fd.name) && _tcscmp(_T(".."), fd.name))
  584. {
  585. _TCHAR szCWD[_MAX_PATH] = _T("");
  586. if (_tgetcwd(szCWD, ARRAYSIZE(szCWD)))
  587. {
  588. _tchdir(fd.name);
  589. nResult = ProcessCatalogs(pszFilePath, bIncludeSubDir);
  590. _tchdir(szCWD);
  591. }
  592. else
  593. {
  594. nResult = errno;
  595. DebugTrace(_T("\nError [%#x]: _tgetcwd() failed.\n"), errno);
  596. }
  597. if (0 != nResult)
  598. {
  599. if (g_bIgnoreError)
  600. {
  601. nResult = 0;
  602. }
  603. else
  604. {
  605. __leave;
  606. }
  607. }
  608. }
  609. } while (0 == _tfindnext(hFile, &fd));
  610. _findclose(hFile);
  611. hFile = -1;
  612. }
  613. }
  614. }
  615. __finally
  616. {
  617. if (-1 != hFile)
  618. {
  619. _findclose(hFile);
  620. }
  621. }
  622. return nResult;
  623. }
  624. //------------------------------------------------------------------------------
  625. //
  626. // Function: main
  627. //
  628. //------------------------------------------------------------------------------
  629. int __cdecl _tmain (int argc, _TCHAR * argv[])
  630. {
  631. int nResult = 0;
  632. int CurrentDrive = _getdrive();
  633. _TCHAR szCWD[_MAX_PATH] = _T("");
  634. _tgetcwd(szCWD, sizeof(szCWD) / sizeof(szCWD[0]));
  635. __try
  636. {
  637. _TCHAR szDrive[_MAX_DRIVE] = _T("");
  638. _TCHAR szDir[_MAX_DIR] = _T("");
  639. _TCHAR szFName[_MAX_FNAME] = _T("");
  640. _TCHAR szExt[_MAX_EXT] = _T("");
  641. _TCHAR szFilePath[_MAX_PATH] = _T("");
  642. if (0 != (nResult = ParseCommandLine(argc, argv)))
  643. {
  644. __leave;
  645. }
  646. if (g_bVerbose)
  647. {
  648. _ftprintf(stdout, _T("Current drive = %c:\n"), CurrentDrive - 1 + _T('A'));
  649. _ftprintf(stdout, _T("Current directory = %s\n"), szCWD);
  650. _ftprintf(stdout, _T("Command line ="));
  651. for (int i = 0; i < argc; i++)
  652. {
  653. _ftprintf(stdout, _T(" %s"), argv[i]);
  654. }
  655. _ftprintf(stdout, _T("\n"));
  656. }
  657. _splitpath(g_pszFilePath, szDrive, szDir, szFName, szExt);
  658. if (_tcslen(szDrive))
  659. {
  660. if (0 != _chdrive(toupper(szDrive[0]) - _T('A') + 1))
  661. {
  662. nResult = ENOENT;
  663. _ftprintf(stdout, _T("Error: _chdrive() to %s failed.\n"), szDrive);
  664. __leave;
  665. }
  666. }
  667. if (_tcslen(szDir))
  668. {
  669. if (0 != _tchdir(szDir))
  670. {
  671. nResult = ENOENT;
  672. _ftprintf(stdout, _T("Error: _tchdir() to %s failed.\n"), szDir);
  673. __leave;
  674. }
  675. }
  676. _tcscpy(szFilePath, szFName);
  677. _tcscat(szFilePath, szExt);
  678. nResult = ProcessCatalogs(szFilePath, g_bIncludeSubDir);
  679. }
  680. __finally
  681. {
  682. _chdrive(CurrentDrive);
  683. _tchdir(szCWD);
  684. }
  685. return nResult;
  686. }