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.

1466 lines
38 KiB

  1. #include "precomp.h"
  2. #include "viewlog.h"
  3. using namespace ShimLib;
  4. CSessionLogEntryArray g_arrSessionLog;
  5. TCHAR g_szSingleLogFile[MAX_PATH] = _T("");
  6. HWND g_hwndIssues = NULL;
  7. int g_cWidth;
  8. int g_cHeight;
  9. HWND g_hwndDetails = NULL;
  10. int g_cWidthDetails;
  11. int g_cHeightDetails;
  12. VLOG_LEVEL g_eMinLogLevel = VLOG_LEVEL_WARNING;
  13. CSessionLogEntry *g_pDetailsEntry = NULL;
  14. const int MAX_INSTANCE_REPEAT = 10;
  15. //
  16. // forward function declarations
  17. //
  18. INT_PTR CALLBACK
  19. DlgViewLogDetails(
  20. HWND hDlg,
  21. UINT message,
  22. WPARAM wParam,
  23. LPARAM lParam
  24. );
  25. TCHAR*
  26. fGetLine(
  27. TCHAR* szLine,
  28. int nChars,
  29. FILE* file
  30. )
  31. {
  32. if (_fgetts(szLine, nChars, file)) {
  33. int nLen = _tcslen(szLine);
  34. while (nLen && (szLine[nLen - 1] == _T('\n') || szLine[nLen - 1] == _T('\r'))) {
  35. szLine[nLen - 1] = 0;
  36. nLen--;
  37. }
  38. return szLine;
  39. } else {
  40. return NULL;
  41. }
  42. }
  43. BOOL
  44. IsModuleInSystem32(
  45. LPCTSTR szModule
  46. )
  47. {
  48. TCHAR szSysDir[MAX_PATH];
  49. TCHAR szPath[MAX_PATH];
  50. if (!GetSystemDirectory(szSysDir, MAX_PATH)) {
  51. return FALSE;
  52. }
  53. if (FAILED(StringCchPrintf(szPath,
  54. ARRAY_LENGTH(szPath),
  55. _T("%s\\%s"),
  56. szSysDir,
  57. szModule))) {
  58. return FALSE;
  59. }
  60. if (GetFileAttributes(szPath) == INVALID_FILE_ATTRIBUTES) {
  61. return FALSE;
  62. }
  63. return TRUE;
  64. }
  65. BOOL
  66. GetSessionLogEntry(
  67. HWND hDlg,
  68. LPCTSTR szLogFullPath,
  69. CSessionLogEntryArray& arrEntries
  70. )
  71. {
  72. TCHAR szLine[4096];
  73. FILE * file = NULL;
  74. SYSTEMTIME stime;
  75. CSessionLogEntry *pEntryTemp = NULL;
  76. TCHAR *szBegin = NULL;
  77. TCHAR *szEnd = NULL;
  78. BOOL bSuccess = FALSE;
  79. HWND hTree = GetDlgItem(hDlg, IDC_ISSUES);
  80. file = _tfopen(szLogFullPath, _T("rt"));
  81. if (!file) {
  82. goto out;
  83. }
  84. if (fGetLine(szLine, 4096, file)) {
  85. ZeroMemory(&stime, sizeof(SYSTEMTIME));
  86. int nFields = _stscanf(szLine, _T("# LOG_BEGIN %hd/%hd/%hd %hd:%hd:%hd"),
  87. &stime.wMonth,
  88. &stime.wDay,
  89. &stime.wYear,
  90. &stime.wHour,
  91. &stime.wMinute,
  92. &stime.wSecond);
  93. //
  94. // if we parsed that line properly, then we've got a valid line.
  95. // Parse it.
  96. //
  97. if (nFields != 6) {
  98. DPF("[GetSessionLogEntry] '%ls' does not appear to be a AppVerifier log file.",
  99. szLogFullPath);
  100. goto out;
  101. }
  102. {
  103. CSessionLogEntry EntryTemp;
  104. EntryTemp.RunTime = stime;
  105. EntryTemp.strLogPath = szLogFullPath;
  106. //
  107. // get the log file and exe path
  108. //
  109. szBegin = _tcschr(szLine, _T('\''));
  110. if (szBegin) {
  111. szBegin++;
  112. szEnd = _tcschr(szBegin, _T('\''));
  113. if (szEnd) {
  114. TCHAR szName[MAX_PATH];
  115. TCHAR szExt[_MAX_EXT];
  116. *szEnd = 0;
  117. EntryTemp.strExePath = szBegin;
  118. *szEnd = 0;
  119. //
  120. // split the path and get the name and extension
  121. //
  122. _tsplitpath(EntryTemp.strExePath.c_str(), NULL, NULL, szName, szExt);
  123. EntryTemp.strExeName = szName;
  124. EntryTemp.strExeName += szExt;
  125. }
  126. }
  127. arrEntries.push_back(EntryTemp);
  128. bSuccess = TRUE;
  129. }
  130. }
  131. out:
  132. if (file) {
  133. fclose(file);
  134. file = NULL;
  135. }
  136. return bSuccess;
  137. }
  138. DWORD
  139. ReadSessionLog(
  140. HWND hDlg,
  141. CSessionLogEntryArray& arrEntries
  142. )
  143. {
  144. TCHAR szLine[4096];
  145. FILE * file = NULL;
  146. SYSTEMTIME stime;
  147. DWORD dwEntries = 0;
  148. DWORD cchSize;
  149. TCHAR *szBegin = NULL;
  150. TCHAR *szEnd = NULL;
  151. HWND hTree = GetDlgItem(hDlg, IDC_ISSUES);
  152. TCHAR szVLog[MAX_PATH];
  153. TCHAR szLogFullPath[MAX_PATH];
  154. WIN32_FIND_DATA FindData;
  155. HANDLE hFind = INVALID_HANDLE_VALUE;
  156. TCHAR szLogSearch[MAX_PATH];
  157. //
  158. // BUGBUG -- this is cheesy, but it's the fastest way to make the change
  159. // to remove session.log. Going forward, we should combine these two functions
  160. // into one, and switch to vectors instead of linked lists
  161. //
  162. cchSize = GetAppVerifierLogPath(szVLog, ARRAY_LENGTH(szVLog));
  163. if (cchSize > ARRAY_LENGTH(szVLog) || cchSize == 0) {
  164. return 0;
  165. }
  166. StringCchCopy(szLogSearch, ARRAY_LENGTH(szLogSearch), szVLog);
  167. StringCchCatW(szLogSearch, ARRAY_LENGTH(szLogSearch), _T("\\"));
  168. StringCchCat(szLogSearch, ARRAY_LENGTH(szLogSearch), _T("*.log"));
  169. //
  170. // enumerate all the logs and make entries for them
  171. //
  172. hFind = FindFirstFile(szLogSearch, &FindData);
  173. while (hFind != INVALID_HANDLE_VALUE) {
  174. //
  175. // make sure to exclude session.log, in case we're using older shims
  176. //
  177. if (_tcsicmp(FindData.cFileName, _T("session.log")) == 0) {
  178. goto nextFile;
  179. }
  180. StringCchCopy(szLogFullPath, ARRAY_LENGTH(szLogFullPath), szVLog);
  181. StringCchCat(szLogFullPath, ARRAY_LENGTH(szLogFullPath), _T("\\"));
  182. StringCchCat(szLogFullPath, ARRAY_LENGTH(szLogFullPath), FindData.cFileName);
  183. BOOL bSuccess = GetSessionLogEntry(hDlg, szLogFullPath, arrEntries);
  184. if (bSuccess) {
  185. dwEntries++;
  186. }
  187. nextFile:
  188. if (!FindNextFile(hFind, &FindData)) {
  189. FindClose(hFind);
  190. hFind = INVALID_HANDLE_VALUE;
  191. }
  192. }
  193. return dwEntries;
  194. }
  195. void
  196. FillTreeView(
  197. HWND hDlg
  198. )
  199. {
  200. HWND hTree = GetDlgItem(hDlg, IDC_ISSUES);
  201. CProcessLogEntry *pProcessEntry = NULL;
  202. if (!hTree) {
  203. return;
  204. }
  205. TreeView_DeleteAllItems(hTree);
  206. for (CSessionLogEntry* pSLogEntry = g_arrSessionLog.begin();
  207. pSLogEntry != g_arrSessionLog.end();
  208. pSLogEntry++) {
  209. //
  210. // Add it to the tree.
  211. //
  212. TVINSERTSTRUCT is;
  213. WCHAR szItem[256];
  214. StringCchPrintf(szItem,
  215. ARRAY_LENGTH(szItem),
  216. L"%ls - %d/%d/%d %d:%02d",
  217. pSLogEntry->strExeName.c_str(),
  218. pSLogEntry->RunTime.wMonth,
  219. pSLogEntry->RunTime.wDay,
  220. pSLogEntry->RunTime.wYear,
  221. pSLogEntry->RunTime.wHour,
  222. pSLogEntry->RunTime.wMinute);
  223. is.hParent = TVI_ROOT;
  224. is.hInsertAfter = TVI_LAST;
  225. is.item.lParam = 0;
  226. is.item.mask = TVIF_TEXT;
  227. is.item.pszText = szItem;
  228. pSLogEntry->hTreeItem = TreeView_InsertItem(hTree, &is);
  229. //
  230. // Add it to the tree
  231. //
  232. for (pProcessEntry = pSLogEntry->arrProcessLogEntries.begin();
  233. pProcessEntry != pSLogEntry->arrProcessLogEntries.end();
  234. pProcessEntry++ ) {
  235. if (pProcessEntry->dwOccurences > 0 && pProcessEntry->eLevel >= g_eMinLogLevel) {
  236. is.hParent = pSLogEntry->hTreeItem;
  237. is.hInsertAfter = TVI_LAST;
  238. is.item.lParam = (LPARAM)pProcessEntry;
  239. is.item.mask = TVIF_TEXT | TVIF_PARAM;
  240. is.item.pszText = (LPWSTR)pProcessEntry->strLogTitle.c_str();
  241. pProcessEntry->hTreeItem = TreeView_InsertItem(hTree, &is);
  242. TreeView_SetItemState(hTree,
  243. pProcessEntry->hTreeItem,
  244. INDEXTOSTATEIMAGEMASK(pProcessEntry->eLevel + 1),
  245. TVIS_STATEIMAGEMASK);
  246. DWORD * pdwInstance;
  247. DWORD dwInstances = 0;
  248. for (pdwInstance = pProcessEntry->arrLogInstances.begin();
  249. pdwInstance != pProcessEntry->arrLogInstances.end();
  250. pdwInstance++ ) {
  251. wstring strInstText;
  252. WCHAR szTemp[50];
  253. CProcessLogInstance *pInstance = &(pSLogEntry->arrProcessLogInstances[*pdwInstance]);
  254. //
  255. // check to see if we should filter out the logs from system DLLs
  256. // Complicated test - we filter system dlls, but not the EXE under test.
  257. // Filtering is only performed if we're not in NTDEV internal mode
  258. //
  259. BOOL bFilterSystem = FALSE;
  260. if (!g_bInternalMode &&
  261. IsModuleInSystem32(pInstance->strModule.c_str()) &&
  262. (_tcsicmp(pInstance->strModule.c_str(), pSLogEntry->strExeName.c_str()) != 0)) {
  263. bFilterSystem = TRUE;
  264. }
  265. if (!pInstance->bDuplicate && pInstance->eLevel >= g_eMinLogLevel && !bFilterSystem) {
  266. if (pInstance->strModule != L"?" && pInstance->dwOffset != 0) {
  267. StringCchPrintf(szTemp, ARRAY_LENGTH(szTemp), L"(%s:%08X) ", pInstance->strModule.c_str(), pInstance->dwOffset);
  268. strInstText += szTemp;
  269. }
  270. strInstText += pInstance->strText;
  271. if (pInstance->dwNumRepeats > 1) {
  272. StringCchPrintf(szTemp, ARRAY_LENGTH(szTemp), L" [%dx]", pInstance->dwNumRepeats);
  273. strInstText += szTemp;
  274. }
  275. is.hParent = pProcessEntry->hTreeItem;
  276. is.hInsertAfter = TVI_LAST;
  277. is.item.lParam = 0;
  278. is.item.mask = TVIF_TEXT;
  279. is.item.pszText = (LPWSTR)(strInstText.c_str());
  280. HTREEITEM hItem = TreeView_InsertItem(hTree, &is);
  281. dwInstances++;
  282. TreeView_SetItemState(hTree,
  283. hItem,
  284. INDEXTOSTATEIMAGEMASK(pInstance->eLevel + 1),
  285. TVIS_STATEIMAGEMASK);
  286. }
  287. }
  288. if (dwInstances == 0) {
  289. //
  290. // we didn't end up adding any children, so delete this entry
  291. //
  292. TreeView_DeleteItem(hTree, pProcessEntry->hTreeItem);
  293. pProcessEntry->hTreeItem = NULL;
  294. }
  295. }
  296. }
  297. }
  298. }
  299. DWORD
  300. ReadProcessLog(HWND hDlg, CSessionLogEntry* pSLogEntry)
  301. {
  302. static TCHAR szLine[4096];
  303. FILE * file = NULL;
  304. CProcessLogEntry *pProcessEntry = NULL;
  305. static TCHAR szShimName[4096];
  306. DWORD dwEntries = 0;
  307. TCHAR * szTemp = NULL;
  308. DWORD dwEntry = 0;
  309. TCHAR *szBegin = NULL;
  310. HWND hTree = GetDlgItem(hDlg, IDC_ISSUES);
  311. if (!pSLogEntry) {
  312. return 0;
  313. }
  314. file = _tfopen(pSLogEntry->strLogPath.c_str(), _T("rt"));
  315. if (!file) {
  316. return 0;
  317. }
  318. //
  319. // first get the headers
  320. //
  321. szTemp = fGetLine(szLine, ARRAY_LENGTH(szLine), file);
  322. while (szTemp) {
  323. if (szLine[0] != _T('#')) {
  324. goto nextLine;
  325. }
  326. //
  327. // relatively simplistic guard against overflow in szShimName in the scanf, below
  328. //
  329. if (_tcslen(szLine) >= ARRAY_LENGTH(szShimName)) {
  330. goto nextLine;
  331. }
  332. if (_stscanf(szLine, _T("# LOGENTRY %s %d '"), szShimName, &dwEntry) == 2) {
  333. if (pProcessEntry) {
  334. pSLogEntry->arrProcessLogEntries.push_back(*pProcessEntry);
  335. delete pProcessEntry;
  336. pProcessEntry = NULL;
  337. dwEntries++;
  338. }
  339. pProcessEntry = new CProcessLogEntry;
  340. if (!pProcessEntry) {
  341. goto out;
  342. }
  343. pProcessEntry->strShimName = szShimName;
  344. pProcessEntry->dwLogNum = dwEntry;
  345. szBegin = _tcschr(szLine, _T('\''));
  346. if (szBegin) {
  347. szBegin++;
  348. pProcessEntry->strLogTitle = szBegin;
  349. }
  350. } else if (_tcsncmp(szLine, _T("# DESCRIPTION BEGIN"), 19) == 0) {
  351. szTemp = fGetLine(szLine, 4096, file);
  352. while (szTemp) {
  353. if (_tcsncmp(szLine, _T("# DESCRIPTION END"), 17) == 0) {
  354. break;
  355. }
  356. if (pProcessEntry) {
  357. //
  358. // throw in a carriage return if necessary
  359. //
  360. if (pProcessEntry->strLogDescription.size()) {
  361. pProcessEntry->strLogDescription += _T("\n");
  362. }
  363. pProcessEntry->strLogDescription += szLine;
  364. }
  365. szTemp = fGetLine(szLine, 4096, file);
  366. }
  367. } else if (_tcsncmp(szLine, _T("# URL '"), 7) == 0) {
  368. szBegin = _tcschr(szLine, _T('\''));
  369. if (szBegin) {
  370. szBegin++;
  371. pProcessEntry->strLogURL = szBegin;
  372. }
  373. }
  374. nextLine:
  375. szTemp = fGetLine(szLine, ARRAY_LENGTH(szLine), file);
  376. }
  377. //
  378. // if we've still got an entry in process, save it
  379. //
  380. if (pProcessEntry) {
  381. pSLogEntry->arrProcessLogEntries.push_back(*pProcessEntry);
  382. delete pProcessEntry;
  383. pProcessEntry = NULL;
  384. dwEntries++;
  385. }
  386. //
  387. // go back to the beginning and read the log lines
  388. //
  389. if (fseek(file, 0, SEEK_SET) != 0) {
  390. return 0;
  391. }
  392. szTemp = fGetLine(szLine, ARRAY_LENGTH(szLine), file);
  393. while (szTemp) {
  394. CProcessLogEntry *pEntry;
  395. static TCHAR szName[4096];
  396. DWORD dwOffset = 0;
  397. static TCHAR szModule[4096];
  398. VLOG_LEVEL eLevel = VLOG_LEVEL_ERROR;
  399. DWORD dwLen;
  400. StringCchCopyW(szModule, ARRAY_LENGTH(szModule), L"<unknown>");
  401. if (szLine[0] != _T('|')) {
  402. goto nextInstance;
  403. }
  404. //
  405. // relatively simplistic guard against overflow in the scanf, below
  406. //
  407. dwLen = _tcslen(szLine);
  408. if (dwLen >= ARRAY_LENGTH(szName) || dwLen >= ARRAY_LENGTH(szModule) ) {
  409. goto nextLine;
  410. }
  411. int nFields = _stscanf(szLine, _T("| %s %d | %d %s %x"), szName, &dwEntry, &eLevel, szModule, &dwOffset);
  412. if (nFields >= 2) {
  413. BOOL bFound = FALSE;
  414. CProcessLogInstance Instance;
  415. Instance.strModule = szModule;
  416. Instance.eLevel = eLevel;
  417. Instance.dwOffset = dwOffset;
  418. szBegin = _tcschr(szLine, _T('\''));
  419. if (szBegin) {
  420. szBegin++;
  421. Instance.strText = szBegin;
  422. }
  423. //
  424. // Get the associated entry (header) for this log instance
  425. //
  426. for (pEntry = pSLogEntry->arrProcessLogEntries.begin();
  427. pEntry != pSLogEntry->arrProcessLogEntries.end();
  428. pEntry++ ) {
  429. if (pEntry->strShimName == szName && pEntry->dwLogNum == dwEntry) {
  430. bFound = TRUE;
  431. break;
  432. }
  433. }
  434. if (!bFound) {
  435. //
  436. // no matching log entry found
  437. //
  438. DPF("[ReadProcessLog] Mo matching log header found for log instance '%ls'.",
  439. Instance.strText.c_str());
  440. pEntry = NULL;
  441. goto nextInstance;
  442. }
  443. Instance.dwProcessLogEntry = pEntry - pSLogEntry->arrProcessLogEntries.begin();
  444. //
  445. // check to see if this is a repeat of a previous entry
  446. //
  447. for (CProcessLogInstance *pInstance = pSLogEntry->arrProcessLogInstances.begin();
  448. pInstance != pSLogEntry->arrProcessLogInstances.end();
  449. pInstance++) {
  450. if (pInstance->strModule == Instance.strModule &&
  451. pInstance->dwOffset == Instance.dwOffset &&
  452. pInstance->strText == Instance.strText &&
  453. pInstance->dwProcessLogEntry == Instance.dwProcessLogEntry &&
  454. pInstance->bDuplicate == FALSE) {
  455. //
  456. // we won't save more than a certain number of repeats
  457. //
  458. if (pInstance->dwNumRepeats >= MAX_INSTANCE_REPEAT) {
  459. goto nextInstance;
  460. }
  461. //
  462. // it's a match, so count up the repeats on the one we found, and mark
  463. // this one duplicate
  464. //
  465. pInstance->dwNumRepeats++;
  466. Instance.bDuplicate = TRUE;
  467. break;
  468. }
  469. }
  470. //
  471. // save the instance in the chronological log
  472. //
  473. pSLogEntry->arrProcessLogInstances.push_back(Instance);
  474. //
  475. // update the cross-pointer in the entry object
  476. //
  477. pEntry->arrLogInstances.push_back(pSLogEntry->arrProcessLogInstances.size() - 1);
  478. //
  479. // now update the entry object's count of instances
  480. //
  481. pEntry->dwOccurences++;
  482. //
  483. // update the level
  484. //
  485. if (pEntry->eLevel < Instance.eLevel) {
  486. pEntry->eLevel = Instance.eLevel;
  487. }
  488. }
  489. nextInstance:
  490. szTemp = fGetLine(szLine, ARRAY_LENGTH(szLine), file);
  491. }
  492. out:
  493. if (file) {
  494. fclose(file);
  495. file = NULL;
  496. }
  497. return dwEntries;
  498. }
  499. void
  500. SetLogDialogCaption(HWND hDlg, ULONG ulCaptionID, LPCWSTR szAdditional)
  501. {
  502. wstring wstrCaption;
  503. if (AVLoadString(ulCaptionID, wstrCaption)) {
  504. if (szAdditional) {
  505. wstrCaption += szAdditional;
  506. }
  507. SetWindowText(hDlg, wstrCaption.c_str());
  508. }
  509. }
  510. void
  511. RefreshLog(HWND hDlg)
  512. {
  513. TreeView_DeleteAllItems(g_hwndIssues);
  514. g_arrSessionLog.clear();
  515. if (g_szSingleLogFile[0]) {
  516. GetSessionLogEntry(hDlg, g_szSingleLogFile, g_arrSessionLog);
  517. SetLogDialogCaption(hDlg, IDS_LOG_TITLE_SINGLE, g_szSingleLogFile);
  518. } else {
  519. ReadSessionLog(hDlg, g_arrSessionLog);
  520. SetLogDialogCaption(hDlg, IDS_LOG_TITLE_LOCAL, NULL);
  521. }
  522. for (CSessionLogEntry* pEntry = g_arrSessionLog.begin();
  523. pEntry != g_arrSessionLog.end();
  524. pEntry++ ) {
  525. ReadProcessLog(hDlg, pEntry);
  526. }
  527. FillTreeView(hDlg);
  528. EnableWindow(GetDlgItem(hDlg, IDC_BTN_DELETE_LOG), FALSE);
  529. }
  530. void
  531. InitListViewDetails(HWND hDlg)
  532. {
  533. DWORD dwLine;
  534. static const WCHAR *aszSeverity[3] = {
  535. L"Info",
  536. L"Warning",
  537. L"Error"
  538. };
  539. if (!g_pDetailsEntry || !g_hwndDetails) {
  540. return;
  541. }
  542. ListView_SetExtendedListViewStyleEx(g_hwndDetails, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);
  543. LVCOLUMN lvc;
  544. lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH;
  545. lvc.fmt = LVCFMT_LEFT;
  546. lvc.cx = 40;
  547. lvc.iSubItem = 0;
  548. lvc.pszText = L"Line";
  549. ListView_InsertColumn(g_hwndDetails, 0, &lvc);
  550. lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH;
  551. lvc.fmt = LVCFMT_LEFT;
  552. lvc.cx = 80;
  553. lvc.iSubItem = 1;
  554. lvc.pszText = L"Module";
  555. ListView_InsertColumn(g_hwndDetails, 1, &lvc);
  556. lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH;
  557. lvc.fmt = LVCFMT_LEFT;
  558. lvc.cx = 70;
  559. lvc.iSubItem = 2;
  560. lvc.pszText = L"Offset";
  561. ListView_InsertColumn(g_hwndDetails, 2, &lvc);
  562. lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH;
  563. lvc.fmt = LVCFMT_LEFT;
  564. lvc.cx = 70;
  565. lvc.iSubItem = 3;
  566. lvc.pszText = L"Severity";
  567. ListView_InsertColumn(g_hwndDetails, 3, &lvc);
  568. lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH;
  569. lvc.fmt = LVCFMT_LEFT;
  570. lvc.cx = 900;
  571. lvc.iSubItem = 4;
  572. lvc.pszText = L"Text";
  573. ListView_InsertColumn(g_hwndDetails, 4, &lvc);
  574. CProcessLogInstance* pInstance;
  575. for (pInstance = g_pDetailsEntry->arrProcessLogInstances.begin(), dwLine = 0;
  576. pInstance != g_pDetailsEntry->arrProcessLogInstances.end();
  577. pInstance++, dwLine++ ) {
  578. WCHAR szTemp[1024];
  579. LVITEM lvi;
  580. //
  581. // if we're in external mode, filter out logs from modules in system32
  582. //
  583. if (!g_bInternalMode &&
  584. IsModuleInSystem32(pInstance->strModule.c_str()) &&
  585. (_tcsicmp(pInstance->strModule.c_str(), g_pDetailsEntry->strExeName.c_str()) != 0)) {
  586. dwLine--;
  587. continue;
  588. }
  589. szTemp[0] = 0;
  590. StringCchPrintf(szTemp, ARRAY_LENGTH(szTemp), L"%04d", dwLine);
  591. lvi.mask = LVIF_TEXT | LVIF_PARAM;
  592. lvi.pszText = szTemp;
  593. lvi.lParam = (LPARAM)pInstance;
  594. lvi.iItem = 99999;
  595. lvi.iSubItem = 0;
  596. int nItem = ListView_InsertItem(g_hwndDetails, &lvi);
  597. if (nItem == -1) {
  598. DPF("[InitListViewDetails] Couldn't add line %d.",
  599. dwLine);
  600. return;
  601. }
  602. if (pInstance->strModule != L"?" && pInstance->dwOffset != 0) {
  603. ListView_SetItemText(g_hwndDetails, nItem, 1, (LPWSTR)pInstance->strModule.c_str());
  604. }
  605. if (pInstance->dwOffset != 0) {
  606. StringCchPrintf(szTemp, ARRAY_LENGTH(szTemp), L"%08X", pInstance->dwOffset);
  607. ListView_SetItemText(g_hwndDetails, nItem, 2, szTemp);
  608. }
  609. if (pInstance->eLevel <= VLOG_LEVEL_ERROR) {
  610. StringCchCopy(szTemp, ARRAY_LENGTH(szTemp), aszSeverity[pInstance->eLevel]);
  611. } else {
  612. StringCchCopy(szTemp, ARRAY_LENGTH(szTemp), L"<unknown>");
  613. }
  614. ListView_SetItemText(g_hwndDetails, nItem, 3, szTemp);
  615. CProcessLogEntry* pEntry = &(g_pDetailsEntry->arrProcessLogEntries[pInstance->dwProcessLogEntry]);
  616. StringCchPrintf(szTemp,
  617. ARRAY_LENGTH(szTemp),
  618. L"%s - %s",
  619. pEntry->strLogTitle.c_str(),
  620. pInstance->strText.c_str());
  621. ListView_SetItemText(g_hwndDetails, nItem, 4, szTemp);
  622. }
  623. }
  624. void
  625. HandleSizing(
  626. HWND hDlg
  627. )
  628. {
  629. int nWidth;
  630. int nHeight;
  631. RECT rDlg;
  632. HDWP hdwp = BeginDeferWindowPos(0);
  633. if (!hdwp) {
  634. return;
  635. }
  636. GetWindowRect(hDlg, &rDlg);
  637. nWidth = rDlg.right - rDlg.left;
  638. nHeight = rDlg.bottom - rDlg.top;
  639. int deltaW = nWidth - g_cWidth;
  640. int deltaH = nHeight - g_cHeight;
  641. HWND hwnd;
  642. RECT r;
  643. hwnd = GetDlgItem(hDlg, IDC_ISSUES);
  644. GetWindowRect(hwnd, &r);
  645. DeferWindowPos(hdwp,
  646. hwnd,
  647. NULL,
  648. 0,
  649. 0,
  650. r.right - r.left + deltaW,
  651. r.bottom - r.top + deltaH,
  652. SWP_NOMOVE | SWP_NOZORDER);
  653. hwnd = GetDlgItem(hDlg, IDC_SOLUTIONS_STATIC);
  654. GetWindowRect(hwnd, &r);
  655. MapWindowPoints(NULL, hDlg, (LPPOINT)&r, 2);
  656. DeferWindowPos(hdwp,
  657. hwnd,
  658. NULL,
  659. r.left,
  660. r.top + deltaH,
  661. 0,
  662. 0,
  663. SWP_NOSIZE | SWP_NOZORDER);
  664. hwnd = GetDlgItem(hDlg, IDC_ISSUE_DESCRIPTION);
  665. GetWindowRect(hwnd, &r);
  666. MapWindowPoints(NULL, hDlg, (LPPOINT)&r, 2);
  667. DeferWindowPos(hdwp,
  668. hwnd,
  669. NULL,
  670. r.left,
  671. r.top + deltaH,
  672. r.right - r.left + deltaW,
  673. r.bottom - r.top,
  674. SWP_NOZORDER);
  675. EndDeferWindowPos(hdwp);
  676. g_cWidth = nWidth;
  677. g_cHeight = nHeight;
  678. }
  679. void
  680. HandleSizingDetails(
  681. HWND hDlg
  682. )
  683. {
  684. int nWidth;
  685. int nHeight;
  686. RECT rDlg;
  687. HDWP hdwp = BeginDeferWindowPos(0);
  688. if (!hdwp) {
  689. return;
  690. }
  691. GetWindowRect(hDlg, &rDlg);
  692. nWidth = rDlg.right - rDlg.left;
  693. nHeight = rDlg.bottom - rDlg.top;
  694. int deltaW = nWidth - g_cWidthDetails;
  695. int deltaH = nHeight - g_cHeightDetails;
  696. HWND hwnd;
  697. RECT r;
  698. hwnd = GetDlgItem(hDlg, IDC_LIST_DETAILS);
  699. GetWindowRect(hwnd, &r);
  700. DeferWindowPos(hdwp,
  701. hwnd,
  702. NULL,
  703. 0,
  704. 0,
  705. r.right - r.left + deltaW,
  706. r.bottom - r.top + deltaH,
  707. SWP_NOMOVE | SWP_NOZORDER);
  708. EndDeferWindowPos(hdwp);
  709. g_cWidthDetails = nWidth;
  710. g_cHeightDetails = nHeight;
  711. }
  712. CSessionLogEntry*
  713. GetSessionLogEntryFromHItem(
  714. HTREEITEM hItem
  715. )
  716. {
  717. for (CSessionLogEntry* pEntry = g_arrSessionLog.begin();
  718. pEntry != g_arrSessionLog.end();
  719. pEntry++ ) {
  720. if (pEntry->hTreeItem == hItem) {
  721. return pEntry;
  722. }
  723. }
  724. return NULL;
  725. }
  726. void
  727. DeleteAllLogs(
  728. HWND hDlg
  729. )
  730. {
  731. ResetVerifierLog();
  732. RefreshLog(hDlg);
  733. }
  734. void
  735. ExportSelectedLog(
  736. HWND hDlg
  737. )
  738. {
  739. HTREEITEM hItem = TreeView_GetSelection(g_hwndIssues);
  740. WCHAR szName[MAX_PATH];
  741. WCHAR szExt[MAX_PATH];
  742. wstring wstrName;
  743. if (hItem == NULL) {
  744. return;
  745. }
  746. CSessionLogEntry* pSession;
  747. TVITEM ti;
  748. //
  749. // first check if this is a top-level item
  750. //
  751. pSession = GetSessionLogEntryFromHItem(hItem);
  752. if (!pSession) {
  753. return;
  754. }
  755. _wsplitpath(pSession->strLogPath.c_str(), NULL, NULL, szName, szExt);
  756. wstrName = szName;
  757. wstrName += szExt;
  758. WCHAR wszFilter[] = L"Log files (*.log)\0*.log\0";
  759. OPENFILENAME ofn;
  760. WCHAR wszAppFullPath[MAX_PATH];
  761. WCHAR wszAppShortName[MAX_PATH];
  762. wstring wstrTitle;
  763. if (!AVLoadString(IDS_EXPORT_LOG_TITLE, wstrTitle)) {
  764. wstrTitle = _T("Export Log");
  765. }
  766. StringCchCopy(wszAppFullPath, ARRAY_LENGTH(wszAppFullPath), wstrName.c_str());
  767. ofn.lStructSize = sizeof(OPENFILENAME);
  768. ofn.hwndOwner = hDlg;
  769. ofn.hInstance = NULL;
  770. ofn.lpstrFilter = wszFilter;
  771. ofn.lpstrCustomFilter = NULL;
  772. ofn.nMaxCustFilter = 0;
  773. ofn.nFilterIndex = 0;
  774. ofn.lpstrFile = wszAppFullPath;
  775. ofn.nMaxFile = MAX_PATH;
  776. ofn.lpstrFileTitle = wszAppShortName;
  777. ofn.nMaxFileTitle = MAX_PATH;
  778. ofn.lpstrInitialDir = NULL;
  779. ofn.lpstrTitle = wstrTitle.c_str();
  780. ofn.Flags = OFN_HIDEREADONLY; // hide the "open read-only" checkbox
  781. ofn.lpstrDefExt = _T("log");
  782. if ( !GetSaveFileName(&ofn) )
  783. {
  784. return;
  785. }
  786. if (CopyFile(pSession->strLogPath.c_str(), wszAppFullPath, FALSE) == 0) {
  787. DWORD dwErr = GetLastError();
  788. AVErrorResourceFormat(IDS_CANT_COPY, dwErr);
  789. }
  790. }
  791. void
  792. DeleteSelectedLog(
  793. HWND hDlg
  794. )
  795. {
  796. HTREEITEM hItem = TreeView_GetSelection(g_hwndIssues);
  797. if (hItem == NULL) {
  798. return;
  799. }
  800. CSessionLogEntry* pSession;
  801. TVITEM ti;
  802. //
  803. // first check if this is a top-level item
  804. //
  805. pSession = GetSessionLogEntryFromHItem(hItem);
  806. if (!pSession) {
  807. return;
  808. }
  809. DeleteFile(pSession->strLogPath.c_str());
  810. RefreshLog(hDlg);
  811. }
  812. void
  813. OpenSelectedLogDetails(
  814. HWND hDlg
  815. )
  816. {
  817. HTREEITEM hItem = TreeView_GetSelection(g_hwndIssues);
  818. if (hItem == NULL) {
  819. return;
  820. }
  821. CSessionLogEntry* pSession;
  822. TVITEM ti;
  823. //
  824. // first check if this is a top-level item
  825. //
  826. g_pDetailsEntry = GetSessionLogEntryFromHItem(hItem);
  827. if (!g_pDetailsEntry) {
  828. return;
  829. }
  830. DialogBox(g_hInstance, (LPCTSTR)IDD_VIEW_LOG_DETAIL, hDlg, DlgViewLogDetails);
  831. g_pDetailsEntry = NULL;
  832. }
  833. void
  834. SetDescriptionText(
  835. HWND hDlg,
  836. CProcessLogEntry* pEntry
  837. )
  838. {
  839. wstring strText;
  840. if (pEntry) {
  841. strText = pEntry->strLogDescription;
  842. if (pEntry->strLogURL.size()) {
  843. wstring strIntro, strLink;
  844. if (AVLoadString(IDS_URL_INTRO, strIntro) && AVLoadString(IDS_URL_LINK, strLink)) {
  845. strText += L" " + strIntro + L" <A HREF=\"" + pEntry->strLogURL + L"\">" + strLink + L"</A>";
  846. }
  847. }
  848. }
  849. if (strText.size() == 0) {
  850. AVLoadString(IDS_SOLUTION_DEFAULT, strText);
  851. }
  852. SetDlgItemText(hDlg, IDC_ISSUE_DESCRIPTION, strText.c_str());
  853. }
  854. void
  855. HandleSelectionChanged(
  856. HWND hDlg,
  857. HTREEITEM hItem
  858. )
  859. {
  860. CProcessLogEntry* pEntry = NULL;
  861. CSessionLogEntry* pSession = NULL;
  862. TVITEM ti;
  863. wstring strText;
  864. if (!hDlg) {
  865. return;
  866. }
  867. if (!hItem) {
  868. EnableWindow(GetDlgItem(hDlg, IDC_BTN_VIEW_DETAILS), FALSE);
  869. EnableWindow(GetDlgItem(hDlg, IDC_BTN_EXPORT_LOG), FALSE);
  870. EnableWindow(GetDlgItem(hDlg, IDC_BTN_DELETE_LOG), FALSE);
  871. goto out;
  872. }
  873. //
  874. // first check if this is a top-level item
  875. //
  876. pSession = GetSessionLogEntryFromHItem(hItem);
  877. if (pSession) {
  878. EnableWindow(GetDlgItem(hDlg, IDC_BTN_VIEW_DETAILS), TRUE);
  879. if (!g_szSingleLogFile[0]) {
  880. EnableWindow(GetDlgItem(hDlg, IDC_BTN_EXPORT_LOG), TRUE);
  881. EnableWindow(GetDlgItem(hDlg, IDC_BTN_DELETE_LOG), TRUE);
  882. } else {
  883. EnableWindow(GetDlgItem(hDlg, IDC_BTN_EXPORT_LOG), FALSE);
  884. EnableWindow(GetDlgItem(hDlg, IDC_BTN_DELETE_LOG), FALSE);
  885. }
  886. } else {
  887. EnableWindow(GetDlgItem(hDlg, IDC_BTN_VIEW_DETAILS), FALSE);
  888. }
  889. ti.mask = TVIF_HANDLE | TVIF_PARAM;
  890. ti.hItem = hItem;
  891. TreeView_GetItem(g_hwndIssues, &ti);
  892. if (ti.lParam == 0) {
  893. hItem = TreeView_GetParent(g_hwndIssues, hItem);
  894. ti.mask = TVIF_HANDLE | TVIF_PARAM;
  895. ti.hItem = hItem;
  896. TreeView_GetItem(g_hwndIssues, &ti);
  897. if (ti.lParam == 0) {
  898. goto out;
  899. }
  900. }
  901. pEntry = (CProcessLogEntry*)ti.lParam;
  902. out:
  903. SetDescriptionText(hDlg, pEntry);
  904. }
  905. // Message handler for log view dialog.
  906. INT_PTR CALLBACK
  907. DlgViewLog(
  908. HWND hDlg,
  909. UINT message,
  910. WPARAM wParam,
  911. LPARAM lParam
  912. )
  913. {
  914. HDC hDC;
  915. switch (message) {
  916. case WM_INITDIALOG:
  917. {
  918. EnableWindow(GetDlgItem(hDlg, IDC_BTN_DELETE_LOG), FALSE);
  919. EnableWindow(GetDlgItem(hDlg, IDC_BTN_EXPORT_LOG), FALSE);
  920. EnableWindow(GetDlgItem(hDlg, IDC_BTN_VIEW_DETAILS), FALSE);
  921. if (g_eMinLogLevel == VLOG_LEVEL_INFO) {
  922. CheckRadioButton(hDlg, IDC_SHOW_ALL, IDC_SHOW_ERRORS, IDC_SHOW_ALL);
  923. } else {
  924. CheckRadioButton(hDlg, IDC_SHOW_ALL, IDC_SHOW_ERRORS, IDC_SHOW_ERRORS);
  925. }
  926. if (g_szSingleLogFile[0]) {
  927. EnableWindow(GetDlgItem(hDlg, IDC_BTN_DELETE_ALL), FALSE);
  928. }
  929. g_hwndIssues = GetDlgItem(hDlg, IDC_ISSUES);
  930. HIMAGELIST hImage = ImageList_LoadImage(g_hInstance,
  931. MAKEINTRESOURCE(IDB_BULLETS),
  932. 16,
  933. 0,
  934. CLR_DEFAULT,
  935. IMAGE_BITMAP,
  936. LR_LOADTRANSPARENT);
  937. if (hImage != NULL) {
  938. TreeView_SetImageList(g_hwndIssues,
  939. hImage,
  940. TVSIL_STATE);
  941. }
  942. RECT r;
  943. GetWindowRect(hDlg, &r);
  944. g_cWidth = r.right - r.left;
  945. g_cHeight = r.bottom - r.top;
  946. RefreshLog(hDlg);
  947. return TRUE;
  948. }
  949. break;
  950. case WM_SIZE:
  951. HandleSizing(hDlg);
  952. break;
  953. case WM_GETMINMAXINFO:
  954. {
  955. MINMAXINFO* pmmi = (MINMAXINFO*)lParam;
  956. pmmi->ptMinTrackSize.y = 300;
  957. return 0;
  958. break;
  959. }
  960. case WM_NOTIFY:
  961. if (wParam == IDC_ISSUES) {
  962. LPNMHDR pnm = (LPNMHDR)lParam;
  963. if (g_hwndIssues == NULL) {
  964. break;
  965. }
  966. switch (pnm->code) {
  967. case NM_CLICK:
  968. {
  969. TVHITTESTINFO ht;
  970. HTREEITEM hItem;
  971. GetCursorPos(&ht.pt);
  972. ScreenToClient(g_hwndIssues, &ht.pt);
  973. TreeView_HitTest(g_hwndIssues, &ht);
  974. HandleSelectionChanged(hDlg, ht.hItem);
  975. break;
  976. }
  977. case TVN_SELCHANGED:
  978. {
  979. NM_TREEVIEW* pnmtv = (NM_TREEVIEW*)pnm;
  980. HandleSelectionChanged(hDlg, pnmtv->itemNew.hItem);
  981. break;
  982. }
  983. }
  984. } else if (wParam == IDC_ISSUE_DESCRIPTION) {
  985. LPNMHDR pnm = (LPNMHDR)lParam;
  986. if (g_hwndIssues == NULL) {
  987. break;
  988. }
  989. switch (pnm->code) {
  990. case NM_CLICK:
  991. {
  992. SHELLEXECUTEINFO sei = { 0};
  993. HTREEITEM hItem = TreeView_GetSelection(g_hwndIssues);
  994. if (hItem == NULL) {
  995. break;
  996. }
  997. CProcessLogEntry* pEntry = NULL;
  998. TVITEM ti;
  999. ti.mask = TVIF_HANDLE | TVIF_PARAM;
  1000. ti.hItem = hItem;
  1001. TreeView_GetItem(g_hwndIssues, &ti);
  1002. if (ti.lParam == 0) {
  1003. hItem = TreeView_GetParent(g_hwndIssues, hItem);
  1004. ti.mask = TVIF_HANDLE | TVIF_PARAM;
  1005. ti.hItem = hItem;
  1006. TreeView_GetItem(g_hwndIssues, &ti);
  1007. if (ti.lParam == 0) {
  1008. break;
  1009. }
  1010. }
  1011. pEntry = (CProcessLogEntry*)ti.lParam;
  1012. SetDescriptionText(hDlg, pEntry);
  1013. if (pEntry) {
  1014. sei.cbSize = sizeof(SHELLEXECUTEINFO);
  1015. sei.fMask = SEE_MASK_DOENVSUBST;
  1016. sei.hwnd = hDlg;
  1017. sei.nShow = SW_SHOWNORMAL;
  1018. sei.lpFile = (LPWSTR)(pEntry->strLogURL.c_str());
  1019. ShellExecuteEx(&sei);
  1020. }
  1021. }
  1022. }
  1023. }
  1024. break;
  1025. case WM_COMMAND:
  1026. switch (LOWORD (wParam)) {
  1027. case IDC_SHOW_ERRORS:
  1028. g_eMinLogLevel = VLOG_LEVEL_WARNING;
  1029. FillTreeView(hDlg);
  1030. break;
  1031. case IDC_SHOW_ALL:
  1032. g_eMinLogLevel = VLOG_LEVEL_INFO;
  1033. FillTreeView(hDlg);
  1034. break;
  1035. case IDC_BTN_DELETE_LOG:
  1036. DeleteSelectedLog(hDlg);
  1037. break;
  1038. case IDC_BTN_DELETE_ALL:
  1039. DeleteAllLogs(hDlg);
  1040. break;
  1041. case IDC_BTN_EXPORT_LOG:
  1042. ExportSelectedLog(hDlg);
  1043. break;
  1044. case IDC_BTN_VIEW_DETAILS:
  1045. OpenSelectedLogDetails(hDlg);
  1046. break;
  1047. case IDOK:
  1048. case IDCANCEL:
  1049. EndDialog(hDlg, LOWORD(wParam));
  1050. return TRUE;
  1051. break;
  1052. }
  1053. break;
  1054. }
  1055. return FALSE;
  1056. }
  1057. int CALLBACK
  1058. DetailsCompareFunc(
  1059. LPARAM lParam1,
  1060. LPARAM lParam2,
  1061. LPARAM lParamSort
  1062. )
  1063. {
  1064. WCHAR szTemp1[256], szTemp2[256];
  1065. szTemp1[0] = 0;
  1066. szTemp2[0] = 0;
  1067. ListView_GetItemText(g_hwndDetails, lParam1, lParamSort, szTemp1, ARRAY_LENGTH(szTemp1));
  1068. ListView_GetItemText(g_hwndDetails, lParam2, lParamSort, szTemp2, ARRAY_LENGTH(szTemp2));
  1069. return _wcsicmp(szTemp1, szTemp2);
  1070. }
  1071. // Message handler for details log view dialog.
  1072. INT_PTR CALLBACK
  1073. DlgViewLogDetails(
  1074. HWND hDlg,
  1075. UINT message,
  1076. WPARAM wParam,
  1077. LPARAM lParam
  1078. )
  1079. {
  1080. HDC hDC;
  1081. switch (message) {
  1082. case WM_INITDIALOG:
  1083. {
  1084. g_hwndDetails = GetDlgItem(hDlg, IDC_LIST_DETAILS);
  1085. RECT r;
  1086. GetWindowRect(hDlg, &r);
  1087. g_cWidthDetails = r.right - r.left;
  1088. g_cHeightDetails = r.bottom - r.top;
  1089. InitListViewDetails(hDlg);
  1090. return TRUE;
  1091. }
  1092. break;
  1093. case WM_SIZE:
  1094. HandleSizingDetails(hDlg);
  1095. break;
  1096. case WM_GETMINMAXINFO:
  1097. {
  1098. MINMAXINFO* pmmi = (MINMAXINFO*)lParam;
  1099. pmmi->ptMinTrackSize.y = 300;
  1100. return 0;
  1101. break;
  1102. }
  1103. case WM_NOTIFY:
  1104. if (wParam == IDC_LIST_DETAILS) {
  1105. LPNMHDR pnm = (LPNMHDR)lParam;
  1106. if (g_hwndIssues == NULL) {
  1107. break;
  1108. }
  1109. switch (pnm->code) {
  1110. case LVN_COLUMNCLICK:
  1111. {
  1112. LPNMLISTVIEW pnmv = (LPNMLISTVIEW) lParam;
  1113. ListView_SortItemsEx(g_hwndDetails, DetailsCompareFunc, pnmv->iSubItem);
  1114. break;
  1115. }
  1116. }
  1117. }
  1118. break;
  1119. case WM_COMMAND:
  1120. switch (LOWORD (wParam)) {
  1121. case IDOK:
  1122. case IDCANCEL:
  1123. EndDialog(hDlg, LOWORD(wParam));
  1124. return TRUE;
  1125. break;
  1126. }
  1127. break;
  1128. }
  1129. return FALSE;
  1130. }