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.

760 lines
16 KiB

  1. // Utilities.cpp: implementation of the CUtilities class.
  2. //
  3. //////////////////////////////////////////////////////////////////////
  4. //***************************************************************************
  5. //
  6. // judyp May 1999
  7. //
  8. //***************************************************************************
  9. #include "stdafx.h"
  10. #pragma warning (disable : 4786)
  11. #pragma warning (disable : 4275)
  12. #include <iostream>
  13. #include <strstream>
  14. #include <fstream>
  15. #include <string>
  16. #include <sstream>
  17. #include <map>
  18. #include <list>
  19. using namespace std;
  20. #include <tchar.h>
  21. #include <windows.h>
  22. #ifdef NONNT5
  23. typedef unsigned long ULONG_PTR;
  24. #endif
  25. #include <wmistr.h>
  26. #include <guiddef.h>
  27. #include <initguid.h>
  28. #include <evntrace.h>
  29. #include <malloc.h>
  30. #include <WTYPES.H>
  31. #include "t_string.h"
  32. #include <tchar.h>
  33. #include <list>
  34. #include "Persistor.h"
  35. #include "Logger.h"
  36. #include "StructureWrappers.h"
  37. #include "StructureWapperHelpers.h"
  38. #include "TCOData.h"
  39. #include "Utilities.h"
  40. //////////////////////////////////////////////////////////////////////
  41. //
  42. //////////////////////////////////////////////////////////////////////
  43. TCHAR *NewTCHAR(const TCHAR *ptcToCopy)
  44. {
  45. if (ptcToCopy == NULL)
  46. {
  47. return NULL;
  48. }
  49. int nString = _tcsclen(ptcToCopy) + 1;
  50. int nTCHAR = sizeof(TCHAR);
  51. int nLen = nString * (nTCHAR);
  52. TCHAR *pNew = (TCHAR*) malloc(nLen);
  53. _tcscpy(pNew,ptcToCopy);
  54. return pNew;
  55. }
  56. LPSTR NewLPSTR(LPCWSTR lpwstrToCopy)
  57. {
  58. int nLen = (wcslen(lpwstrToCopy) + 1) * sizeof(WCHAR);
  59. LPSTR pNew = (char *)malloc( nLen );
  60. wcstombs(pNew, lpwstrToCopy, nLen);
  61. return pNew;
  62. }
  63. LPWSTR NewLPWSTR(LPCSTR lpstrToCopy)
  64. {
  65. int nLen = (strlen(lpstrToCopy) + 1);
  66. LPWSTR pNew = (WCHAR *)malloc( nLen * sizeof(WCHAR));
  67. mbstowcs(pNew, lpstrToCopy, nLen);
  68. return pNew;
  69. }
  70. LPTSTR DecodeStatus(IN ULONG Status)
  71. {
  72. LPTSTR lptstrError = (LPTSTR) malloc (MAX_STR * (sizeof(TCHAR)));
  73. memset( lptstrError, 0, MAX_STR );
  74. FormatMessage(
  75. FORMAT_MESSAGE_FROM_SYSTEM |
  76. FORMAT_MESSAGE_IGNORE_INSERTS,
  77. NULL,
  78. Status,
  79. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  80. lptstrError,
  81. MAX_STR,
  82. NULL );
  83. for (int i = 0; i < MAX_STR; i++)
  84. {
  85. if (lptstrError[i] == 0x0d)
  86. {
  87. lptstrError[i] = _T('\0');
  88. break;
  89. }
  90. }
  91. return lptstrError;
  92. }
  93. int GetFileList
  94. (LPTSTR lptstrPath, LPTSTR lptstrFileType, list<t_string> &rList)
  95. {
  96. t_string tsFind;
  97. tsFind = lptstrPath;
  98. tsFind += _T("\\");
  99. tsFind += lptstrFileType;
  100. WIN32_FIND_DATA wfdFile;
  101. HANDLE hFindHandle =
  102. FindFirstFile(tsFind.c_str(), &wfdFile);
  103. if (hFindHandle == INVALID_HANDLE_VALUE)
  104. {
  105. return HRESULT_FROM_WIN32(GetLastError());
  106. }
  107. if ((_tcscmp(wfdFile.cFileName,_T(".")) != 0) &&
  108. (_tcscmp(wfdFile.cFileName,_T("..")) != 0))
  109. {
  110. tsFind = lptstrPath;
  111. tsFind += _T("\\");
  112. tsFind += wfdFile.cFileName;
  113. rList.push_back(tsFind);
  114. tsFind.erase();
  115. }
  116. while (FindNextFile(hFindHandle, &wfdFile))
  117. {
  118. if ((_tcscmp(wfdFile.cFileName,_T(".")) != 0) &&
  119. (_tcscmp(wfdFile.cFileName,_T("..")) != 0))
  120. {
  121. tsFind = lptstrPath;
  122. tsFind += _T("\\");
  123. tsFind += wfdFile.cFileName;
  124. rList.push_back(tsFind);
  125. tsFind.erase();
  126. }
  127. }
  128. FindClose(hFindHandle);
  129. return ERROR_SUCCESS;
  130. }
  131. // From Q 118626
  132. BOOL IsAdmin()
  133. {
  134. HANDLE hAccessToken;
  135. UCHAR InfoBuffer[1024];
  136. PTOKEN_GROUPS ptgGroups = (PTOKEN_GROUPS)InfoBuffer;
  137. DWORD dwInfoBufferSize;
  138. PSID psidAdministrators;
  139. SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY;
  140. UINT x;
  141. BOOL bSuccess;
  142. if(!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE,
  143. &hAccessToken )) {
  144. if(GetLastError() != ERROR_NO_TOKEN)
  145. return FALSE;
  146. //
  147. // retry against process token if no thread token exists
  148. //
  149. if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY,
  150. &hAccessToken))
  151. return FALSE;
  152. }
  153. bSuccess = GetTokenInformation(hAccessToken,TokenGroups,InfoBuffer,
  154. 1024, &dwInfoBufferSize);
  155. CloseHandle(hAccessToken);
  156. if(!bSuccess )
  157. return FALSE;
  158. if(!AllocateAndInitializeSid(&siaNtAuthority, 2,
  159. SECURITY_BUILTIN_DOMAIN_RID,
  160. DOMAIN_ALIAS_RID_ADMINS,
  161. 0, 0, 0, 0, 0, 0,
  162. &psidAdministrators))
  163. return FALSE;
  164. // assume that we don't find the admin SID.
  165. bSuccess = FALSE;
  166. for(x=0;x<ptgGroups->GroupCount;x++)
  167. {
  168. if( EqualSid(psidAdministrators, ptgGroups->Groups[x].Sid) )
  169. {
  170. bSuccess = TRUE;
  171. break;
  172. }
  173. }
  174. FreeSid(psidAdministrators);
  175. return bSuccess;
  176. }
  177. t_string GUIDToTString(GUID Guid)
  178. {
  179. t_strstream strStream;
  180. t_string tsOut;
  181. strStream << _T("{");
  182. strStream.fill(_T('0'));
  183. strStream.width(8);
  184. strStream.flags(ios_base::right);
  185. strStream << hex << Guid.Data1;
  186. strStream << _T("-");
  187. strStream.width(4);
  188. strStream << hex << Guid.Data2;
  189. strStream << _T("-");
  190. strStream << hex << Guid.Data3;
  191. strStream << _T("-");
  192. // Data4 specifies an array of 8 bytes. The first 2 bytes contain
  193. // the third group of 4 hexadecimal digits. The remaining 6 bytes
  194. // contain the final 12 hexadecimal digits.
  195. #ifndef _UNICODE
  196. int i;
  197. strStream.width(1);
  198. BYTE Byte;
  199. int Int;
  200. for (i = 0; i < 2; i++)
  201. {
  202. Byte = Guid.Data4[i];
  203. Byte = Byte >> 4;
  204. Int = Byte;
  205. strStream << hex << Int;
  206. Byte = Guid.Data4[i];
  207. Byte = 0x0f & Byte;
  208. Int = Byte;
  209. strStream << hex << Int;
  210. }
  211. strStream << _T("-");
  212. strStream.width(1);
  213. for (i = 2; i < 8; i++)
  214. {
  215. BYTE Byte = Guid.Data4[i];
  216. Byte = Byte >> 4;
  217. Int = Byte;
  218. strStream << hex << Int;
  219. Byte = Guid.Data4[i];
  220. Byte = 0x0f & Byte;
  221. Int = Byte;
  222. strStream << hex << Int;
  223. }
  224. #else
  225. int i;
  226. for (i = 0; i < 2; i++)
  227. {
  228. TCHAR tc = Guid.Data4[i];
  229. // For some reason the width is reset each time through the
  230. // loop to be one.
  231. strStream.width(2);
  232. strStream << hex << tc;
  233. }
  234. strStream << _T("-");
  235. BYTE Byte;
  236. strStream.width(1);
  237. for (i = 2; i < 8; i++)
  238. {
  239. Byte = Guid.Data4[i];
  240. Byte = Byte >> 4;
  241. strStream << hex << Byte;
  242. Byte = Guid.Data4[i];
  243. Byte = 0x0f & Byte;
  244. strStream << hex << Byte;
  245. }
  246. #endif
  247. strStream << _T("}");
  248. strStream >> tsOut;
  249. return tsOut;
  250. }
  251. LPTSTR LPTSTRFromGuid(GUID Guid)
  252. {
  253. t_string tsGuid = GUIDToTString(Guid);
  254. return NewTCHAR(tsGuid.c_str());
  255. }
  256. t_string ULONGVarToTString(ULONG ul, bool bHex)
  257. {
  258. t_string tsTemp;
  259. t_strstream strStream;
  260. if (bHex)
  261. {
  262. strStream.width(8);
  263. strStream.fill('0');
  264. strStream.flags(ios_base::right);
  265. strStream << hex << ul;
  266. }
  267. else
  268. {
  269. strStream << ul;
  270. }
  271. strStream >> tsTemp;
  272. if (bHex)
  273. {
  274. t_string tsHex;
  275. tsHex = _T("0x");
  276. tsHex += tsTemp;
  277. return tsHex;
  278. }
  279. else
  280. {
  281. return tsTemp;
  282. }
  283. }
  284. ULONG InitializePropsArray
  285. (PEVENT_TRACE_PROPERTIES &pPropsArray, int nInstances)
  286. {
  287. pPropsArray =
  288. (PEVENT_TRACE_PROPERTIES) malloc
  289. (sizeof(EVENT_TRACE_PROPERTIES) * nInstances);
  290. RtlZeroMemory(pPropsArray, sizeof(EVENT_TRACE_PROPERTIES) * nInstances);
  291. for (int i = 0; i < nInstances; i++)
  292. {
  293. pPropsArray[i].LoggerName = (TCHAR *) malloc (sizeof(TCHAR) * MAX_STR);
  294. RtlZeroMemory(pPropsArray[i].LoggerName, sizeof(TCHAR) * MAX_STR);
  295. pPropsArray[i].LogFileName = (TCHAR *) malloc (sizeof(TCHAR) * MAX_STR);
  296. RtlZeroMemory(pPropsArray[i].LogFileName, sizeof(TCHAR) * MAX_STR);
  297. }
  298. return 0;
  299. }
  300. ULONG FreePropsArray
  301. (PEVENT_TRACE_PROPERTIES &pPropsArray, int nInstances)
  302. {
  303. for (int i = 0; i < nInstances; i++)
  304. {
  305. free(pPropsArray[i].LoggerName);
  306. free(pPropsArray[i].LogFileName);
  307. }
  308. free(pPropsArray);
  309. return 0;
  310. }
  311. int OpenLogFiles
  312. ( LPCTSTR lpctstrTCODetailFile,
  313. CLogger *&pDetailLogger,
  314. LPTSTR *plpstrReturnedError
  315. )
  316. {
  317. HRESULT hr = S_OK;
  318. t_string tsError;
  319. if (lpctstrTCODetailFile)
  320. {
  321. // Open *our* logger file.
  322. pDetailLogger = new CLogger(lpctstrTCODetailFile, false);
  323. hr = pDetailLogger->GetOpenStatus();
  324. if (FAILED (hr))
  325. {
  326. tsError = _T("Could not open detail log file \"");
  327. tsError += lpctstrTCODetailFile;
  328. tsError += _T("\".");
  329. *plpstrReturnedError = NewTCHAR(tsError.c_str());
  330. return hr;
  331. }
  332. }
  333. return hr;
  334. }
  335. int LogDetailBeforeCall
  336. ( CLogger *pDetailLogger,
  337. TCOData *pstructTCOData,
  338. BOOL bAdmin
  339. )
  340. {
  341. // Want to log the security context, user must have administrative priviledge!
  342. pDetailLogger->LogTCHAR(_T("---- Input Data ----\n"));
  343. pDetailLogger->LogTCHAR(_T("Description: "));
  344. pDetailLogger->LogTCHAR(pstructTCOData->m_lptstrLongDesc);
  345. pDetailLogger->LogTCHAR(_T("\n"));
  346. pDetailLogger->LogTCHAR(_T("User Security Context: "));
  347. if (bAdmin)
  348. {
  349. pDetailLogger->LogTCHAR(_T("Has administrative priviledge.\n"));
  350. }
  351. else
  352. {
  353. pDetailLogger->LogTCHAR(_T("Does not have administrative priviledge.\n"));
  354. }
  355. pDetailLogger->LogTCHAR(_T("LoggerType: "));
  356. pDetailLogger->LogTCHAR(pstructTCOData->m_lptstrLoggerMode);
  357. pDetailLogger->LogTCHAR(_T("\n"));
  358. pDetailLogger->LogTCHAR(_T("Enable: "));
  359. pDetailLogger->LogULONG(pstructTCOData->m_ulEnable);
  360. pDetailLogger->LogTCHAR(_T("\n"));
  361. pDetailLogger->LogTCHAR(_T("EnableFlag: "));
  362. pDetailLogger->LogULONG(pstructTCOData->m_ulEnableFlag);
  363. pDetailLogger->LogTCHAR(_T("\n"));
  364. pDetailLogger->LogTCHAR(_T("EnableLevel: "));
  365. pDetailLogger->LogULONG(pstructTCOData->m_ulEnableLevel);
  366. pDetailLogger->LogTCHAR(_T("\n"));
  367. pDetailLogger->LogTCHAR(_T("Expected Result: "));
  368. pDetailLogger->LogULONG(pstructTCOData->m_ulExpectedResult, true);
  369. pDetailLogger->LogTCHAR(_T("\n"));
  370. pDetailLogger->LogTCHAR(_T("Trace Handle: "));
  371. if (pstructTCOData->m_pTraceHandle == NULL)
  372. {
  373. pDetailLogger->LogULONG64(0, true);
  374. }
  375. else
  376. {
  377. pDetailLogger->LogULONG64(*pstructTCOData->m_pTraceHandle, true);
  378. }
  379. pDetailLogger->LogTCHAR(_T("\n"));
  380. pDetailLogger->LogTCHAR(_T("Instance Name: "));
  381. pDetailLogger->LogTCHAR(pstructTCOData->m_lptstrInstanceName);
  382. pDetailLogger->LogTCHAR(_T("\n"));
  383. pDetailLogger->LogEventTraceProperties(pstructTCOData->m_pProps);
  384. pDetailLogger->LogTCHAR(_T("Guids:"));
  385. LPTSTR lptstrGuid;
  386. if (pstructTCOData->m_pProps != 0)
  387. {
  388. for (int i = 0; i < pstructTCOData->m_nGuids; i++)
  389. {
  390. lptstrGuid = LPTSTRFromGuid(pstructTCOData->m_lpguidArray[i]);
  391. pDetailLogger->LogTCHAR(lptstrGuid);
  392. free (lptstrGuid);
  393. lptstrGuid = NULL;
  394. if (pstructTCOData->m_nGuids > 1
  395. && i < pstructTCOData->m_nGuids - 1)
  396. {
  397. pDetailLogger->LogTCHAR(_T(","));
  398. }
  399. }
  400. }
  401. pDetailLogger->LogTCHAR(_T("\n"));
  402. pDetailLogger->Flush();
  403. return 0;
  404. }
  405. int LogDetailAfterCall
  406. ( CLogger *pDetailLogger,
  407. TCOData *pstructTCOData,
  408. PEVENT_TRACE_PROPERTIES *pProps,
  409. ULONG ulResult,
  410. LPTSTR lpstrReturnedError,
  411. bool bValid,
  412. BOOL bAdmin,
  413. LPCTSTR lptstrBanner,
  414. bool bPrintProps
  415. )
  416. {
  417. pDetailLogger->LogTCHAR(_T("---- Returned Values ----\n"));
  418. if (lptstrBanner)
  419. {
  420. pDetailLogger->LogTCHAR(lptstrBanner);
  421. pDetailLogger->LogTCHAR(_T("\n"));
  422. }
  423. if (!bAdmin && ulResult == ERROR_SUCCESS
  424. && ulResult == pstructTCOData->m_ulExpectedResult)
  425. {
  426. pDetailLogger->LogTCHAR(_T("Test: "));
  427. pDetailLogger->LogTCHAR(pstructTCOData->m_lptstrShortDesc);
  428. pDetailLogger->LogTCHAR(_T(" failed\n"));
  429. pDetailLogger->LogTCHAR(_T("SecurityContextError: API should have failed because the user does not have administrative privledge.\n"));
  430. }
  431. else if (ulResult == pstructTCOData->m_ulExpectedResult)
  432. {
  433. pDetailLogger->LogTCHAR(_T("Test: "));
  434. pDetailLogger->LogTCHAR(pstructTCOData->m_lptstrShortDesc);
  435. pDetailLogger->LogTCHAR(_T(" passed\n"));
  436. }
  437. else
  438. {
  439. pDetailLogger->LogTCHAR(_T("Test: "));
  440. pDetailLogger->LogTCHAR(pstructTCOData->m_lptstrShortDesc);
  441. pDetailLogger->LogTCHAR(_T(" failed\n"));
  442. pDetailLogger->LogTCHAR(_T("Expected result: "));
  443. pDetailLogger->LogULONG(pstructTCOData->m_ulExpectedResult, true);
  444. pDetailLogger->LogTCHAR(_T("\n"));
  445. }
  446. pDetailLogger->LogTCHAR(_T("Test result: "));
  447. pDetailLogger->LogULONG(ulResult, true);
  448. pDetailLogger->LogTCHAR(_T("\n"));
  449. if (ulResult && lpstrReturnedError)
  450. {
  451. pDetailLogger->LogTCHAR(_T("Error Description: "));
  452. pDetailLogger->LogTCHAR(lpstrReturnedError);
  453. pDetailLogger->LogTCHAR(_T("\n"));
  454. }
  455. pDetailLogger->LogTCHAR(_T("Trace Handle: "));
  456. if (pstructTCOData->m_pTraceHandle == NULL)
  457. {
  458. pDetailLogger->LogULONG64(0, true);
  459. }
  460. else
  461. {
  462. pDetailLogger->LogULONG64(*pstructTCOData->m_pTraceHandle, true);
  463. }
  464. pDetailLogger->LogTCHAR(_T("\n"));
  465. if (bPrintProps)
  466. {
  467. pDetailLogger->LogEventTraceProperties(*pProps);
  468. pDetailLogger->LogTCHAR(_T("-------------------------------------------------------\n"));
  469. }
  470. pDetailLogger->Flush();
  471. return 0;
  472. }
  473. int LogSummaryBeforeCall
  474. (
  475. TCOData *pstructTCOData,
  476. LPCTSTR lpctstrDataFile,
  477. LPCTSTR lptstrAction,
  478. LPCTSTR lptstrAPI,
  479. bool bLogExpected
  480. )
  481. {
  482. t_cout << _T("\n") << lptstrAPI << _T(" called with TCOTest = ")
  483. << pstructTCOData->m_lptstrShortDesc << _T("\n");
  484. t_cout << _T("Action = ") << lptstrAction << _T("\n");
  485. if (lpctstrDataFile)
  486. {
  487. t_cout << _T("DataFile = ") << lpctstrDataFile << _T("\n");
  488. }
  489. if (bLogExpected && pstructTCOData->m_lptstrLongDesc)
  490. {
  491. t_cout << _T("Description = ") << pstructTCOData->m_lptstrLongDesc << _T("\n");
  492. }
  493. return ERROR_SUCCESS;
  494. }
  495. int LogSummaryAfterCall
  496. (
  497. TCOData *pstructTCOData,
  498. LPCTSTR lpctstrDataFile,
  499. LPCTSTR lptstrAction,
  500. ULONG ulActualResult,
  501. LPTSTR lptstrErrorDesc,
  502. bool bLogExpected
  503. )
  504. {
  505. t_string tsOut1;
  506. t_string tsOut2;
  507. tsOut1 = ULONGVarToTString(ulActualResult, true);
  508. tsOut2 = ULONGVarToTString(pstructTCOData->m_ulExpectedResult, true);
  509. if (ulActualResult == pstructTCOData->m_ulExpectedResult && bLogExpected)
  510. {
  511. t_cout << pstructTCOData->m_lptstrShortDesc << _T(" - Passed: Actual result ");
  512. t_cout << tsOut1 << _T(" = to expected result ") << tsOut2 << _T(".\n");
  513. }
  514. else if (ulActualResult != pstructTCOData->m_ulExpectedResult && bLogExpected)
  515. {
  516. t_cout << pstructTCOData->m_lptstrShortDesc << _T(" - Failed: Actual result ");
  517. t_cout << tsOut1 << _T(" not = to expected result ") << tsOut2 << _T(".\n");
  518. }
  519. else if (ulActualResult == ERROR_SUCCESS && !bLogExpected)
  520. {
  521. t_cout << pstructTCOData->m_lptstrShortDesc << _T(" - Passed.\n");
  522. }
  523. else if (ulActualResult != ERROR_SUCCESS && !bLogExpected)
  524. {
  525. t_cout << pstructTCOData->m_lptstrShortDesc << _T(" - Failed.\n");
  526. }
  527. if (lptstrErrorDesc && ulActualResult != pstructTCOData->m_ulExpectedResult)
  528. {
  529. t_cout << _T("Error: ") << lptstrErrorDesc << _T("\n");
  530. }
  531. return 0;
  532. }
  533. bool LogPropsDiff
  534. ( CLogger *pDetailLogger,
  535. PEVENT_TRACE_PROPERTIES pProps1,
  536. PEVENT_TRACE_PROPERTIES pProps2
  537. )
  538. {
  539. bool bDiff = false;
  540. if (pDetailLogger)
  541. {
  542. pDetailLogger->LogTCHAR(_T("EVENT_TRACE_PROPERTIES data items which differ:\n"));
  543. pDetailLogger->Flush();
  544. }
  545. if (pProps1->BufferSize != pProps2->BufferSize)
  546. {
  547. if (pDetailLogger)
  548. {
  549. pDetailLogger->LogTCHAR(_T(" BufferSize\n"));
  550. }
  551. bDiff = true;
  552. }
  553. if (pProps1->MinimumBuffers != pProps2->MinimumBuffers)
  554. {
  555. if (pDetailLogger)
  556. {
  557. pDetailLogger->LogTCHAR(_T(" MinimumBuffers\n"));
  558. }
  559. bDiff = true;
  560. }
  561. if (pProps1->MaximumBuffers != pProps2->MaximumBuffers)
  562. {
  563. if (pDetailLogger)
  564. {
  565. pDetailLogger->LogTCHAR(_T(" MaximumBuffers\n"));
  566. }
  567. bDiff = true;
  568. }
  569. if (pProps1->MaximumFileSize != pProps2->MaximumFileSize)
  570. {
  571. if (pDetailLogger)
  572. {
  573. pDetailLogger->LogTCHAR(_T(" MaximumFileSize\n"));
  574. }
  575. bDiff = true;
  576. }
  577. if (pProps1->LogFileMode != pProps2->LogFileMode)
  578. {
  579. if (pDetailLogger)
  580. {
  581. pDetailLogger->LogTCHAR(_T(" LogFileMode\n"));
  582. }
  583. bDiff = true;
  584. }
  585. if (pProps1->FlushTimer != pProps2->FlushTimer)
  586. {
  587. if (pDetailLogger)
  588. {
  589. pDetailLogger->LogTCHAR(_T(" FlushTimer\n"));
  590. }
  591. bDiff = true;
  592. }
  593. if (pProps1->EnableFlags != pProps2->EnableFlags)
  594. {
  595. if (pDetailLogger)
  596. {
  597. pDetailLogger->LogTCHAR(_T(" EnableFlags\n"));
  598. }
  599. bDiff = true;
  600. }
  601. if (pProps1->AgeLimit != pProps2->AgeLimit)
  602. {
  603. if (pDetailLogger)
  604. {
  605. pDetailLogger->LogTCHAR(_T(" AgeLimit\n"));
  606. }
  607. bDiff = true;
  608. }
  609. if (!bDiff)
  610. {
  611. if (pDetailLogger)
  612. {
  613. pDetailLogger->LogTCHAR(_T(" None\n"));
  614. }
  615. }
  616. if (pDetailLogger)
  617. {
  618. pDetailLogger->Flush();
  619. }
  620. return bDiff;
  621. }