windows-nt/Source/XPSP1/NT/windows/appcompat/appverifier/viewlog.cpp
2020-09-26 16:20:57 +08:00

852 lines
20 KiB
C++

#include "precomp.h"
#include "viewlog.h"
CSessionLogEntry* g_pSessionLogHead = NULL;
TCHAR g_szSingleLogFile[MAX_PATH] = _T("");
HWND g_hwndIssues;
int g_cWidth;
int g_cHeight;
TCHAR*
fGetLine(
TCHAR* szLine,
int nChars,
FILE* file
)
{
if (_fgetts(szLine, nChars, file)) {
int nLen = _tcslen(szLine);
while (szLine[nLen - 1] == _T('\n') || szLine[nLen - 1] == _T('\r')) {
szLine[nLen - 1] = 0;
nLen--;
}
return szLine;
} else {
return NULL;
}
}
CSessionLogEntry*
GetSessionLogEntry(
HWND hDlg,
LPCTSTR szLogFullPath
)
{
TCHAR szLine[4096];
FILE * file = NULL;
SYSTEMTIME stime;
CSessionLogEntry *pEntryTemp = NULL;
TCHAR *szBegin = NULL;
TCHAR *szEnd = NULL;
HWND hTree = GetDlgItem(hDlg, IDC_ISSUES);
file = _tfopen(szLogFullPath, _T("rt"));
if (!file) {
goto out;
}
if (fGetLine(szLine, 4096, file)) {
ZeroMemory(&stime, sizeof(SYSTEMTIME));
int nFields = _stscanf(szLine, _T("# LOG_BEGIN %hd/%hd/%hd %hd:%hd:%hd"),
&stime.wMonth,
&stime.wDay,
&stime.wYear,
&stime.wHour,
&stime.wMinute,
&stime.wSecond);
//
// if we parsed that line properly, then we've got a valid line.
// Parse it.
//
if (nFields == 6) {
pEntryTemp = new CSessionLogEntry;
if (!pEntryTemp) {
goto out;
}
pEntryTemp->RunTime = stime;
pEntryTemp->strLogPath = szLogFullPath;
//
// get the log file and exe path
//
szBegin = _tcschr(szLine, _T('\''));
if (szBegin) {
szBegin++;
szEnd = _tcschr(szBegin, _T('\''));
if (szEnd) {
TCHAR szName[MAX_PATH];
TCHAR szExt[_MAX_EXT];
*szEnd = 0;
pEntryTemp->strExePath = szBegin;
*szEnd = 0;
//
// split the path and get the name and extension
//
_tsplitpath(pEntryTemp->strExePath, NULL, NULL, szName, szExt);
pEntryTemp->strExeName = szName;
pEntryTemp->strExeName += szExt;
}
}
//
// Add it to the tree.
//
TVINSERTSTRUCT is;
WCHAR szItem[256];
wsprintf(szItem, L"%s - %d/%d/%d %d:%02d",
pEntryTemp->strExeName,
pEntryTemp->RunTime.wMonth,
pEntryTemp->RunTime.wDay,
pEntryTemp->RunTime.wYear,
pEntryTemp->RunTime.wHour,
pEntryTemp->RunTime.wMinute);
is.hParent = TVI_ROOT;
is.hInsertAfter = TVI_LAST;
is.item.lParam = 0;
is.item.mask = TVIF_TEXT;
is.item.pszText = szItem;
pEntryTemp->hTreeItem = TreeView_InsertItem(hTree, &is);
}
}
out:
if (file) {
fclose(file);
file = NULL;
}
return pEntryTemp;
}
DWORD
ReadSessionLog(HWND hDlg, CSessionLogEntry **ppSessionLog)
{
TCHAR szLine[4096];
FILE * file = NULL;
SYSTEMTIME stime;
CSessionLogEntry *pEntryTemp = NULL;
DWORD dwEntries = 0;
TCHAR *szBegin = NULL;
TCHAR *szEnd = NULL;
CSessionLogEntry **ppEnd = ppSessionLog;
HWND hTree = GetDlgItem(hDlg, IDC_ISSUES);
TCHAR szVLog[MAX_PATH];
TCHAR szLogFullPath[MAX_PATH];
WIN32_FIND_DATA FindData;
HANDLE hFind = INVALID_HANDLE_VALUE;
TCHAR szLogSearch[MAX_PATH];
//
// BUGBUG -- this is cheesy, but it's the fastest way to make the change
// to remove session.log. Going forward, we should combine these two functions
// into one, and switch to vectors instead of linked lists
//
GetSystemWindowsDirectory(szVLog, MAX_PATH);
_tcscat(szVLog, _T("\\AppPatch\\VLog\\"));
_tcscpy(szLogSearch, szVLog);
_tcscat(szLogSearch, _T("*.log"));
//
// enumerate all the logs and make entries for them
//
hFind = FindFirstFile(szLogSearch, &FindData);
while (hFind != INVALID_HANDLE_VALUE) {
//
// make sure to exclude session.log, in case we're using older shims
//
if (_tcsicmp(FindData.cFileName, _T("session.log")) == 0) {
goto nextFile;
}
_tcscpy(szLogFullPath, szVLog);
_tcscat(szLogFullPath, FindData.cFileName);
pEntryTemp = GetSessionLogEntry(hDlg, szLogFullPath);
if (pEntryTemp) {
//
// we want these in the order they appear in the log,
// so we add to the end rather than the beginning.
//
*ppEnd = pEntryTemp;
ppEnd = &(pEntryTemp->pNext);
dwEntries++;
}
nextFile:
if (!FindNextFile(hFind, &FindData)) {
FindClose(hFind);
hFind = INVALID_HANDLE_VALUE;
}
}
return dwEntries;
}
DWORD
ReadProcessLog(HWND hDlg, CSessionLogEntry* pSLogEntry)
{
TCHAR szLine[4096];
FILE * file = NULL;
CProcessLogEntry *pProcessEntry = NULL;
TCHAR szShimName[256];
DWORD dwEntries = 0;
TCHAR * szTemp = NULL;
DWORD dwEntry = 0;
TCHAR *szBegin = NULL;
CProcessLogEntry **ppProcessTail = &(pSLogEntry->pProcessLog);
HWND hTree = GetDlgItem(hDlg, IDC_ISSUES);
if (!pSLogEntry) {
return 0;
}
file = _tfopen(pSLogEntry->strLogPath, _T("rt"));
if (!file) {
return 0;
}
//
// first get the headers
//
szTemp = fGetLine(szLine, 4096, file);
while (szTemp) {
if (szLine[0] == _T('|')) {
break;
}
if (szLine[0] != _T('#')) {
goto nextLine;
}
if (_stscanf(szLine, _T("# LOGENTRY %s %d '"), szShimName, &dwEntry) == 2) {
if (pProcessEntry) {
*ppProcessTail = pProcessEntry;
ppProcessTail = &(pProcessEntry->pNext);
pProcessEntry = NULL;
dwEntries++;
}
pProcessEntry = new CProcessLogEntry;
if (!pProcessEntry) {
goto out;
}
pProcessEntry->strShimName = szShimName;
pProcessEntry->dwLogNum = dwEntry;
szBegin = _tcschr(szLine, _T('\''));
if (szBegin) {
szBegin++;
pProcessEntry->strLogTitle = szBegin;
}
} else if (_tcsncmp(szLine, _T("# DESCRIPTION BEGIN"), 19) == 0) {
szTemp = fGetLine(szLine, 4096, file);
while (szTemp) {
if (_tcsncmp(szLine, _T("# DESCRIPTION END"), 17) == 0) {
break;
}
if (pProcessEntry) {
//
// throw in a carriage return if necessary
//
if (pProcessEntry->strLogDescription.GetLength()) {
pProcessEntry->strLogDescription += _T("\n");
}
pProcessEntry->strLogDescription += szLine;
}
szTemp = fGetLine(szLine, 4096, file);
}
} else if (_tcsncmp(szLine, _T("# URL '"), 7) == 0) {
szBegin = _tcschr(szLine, _T('\''));
if (szBegin) {
szBegin++;
pProcessEntry->strLogURL = szBegin;
}
}
nextLine:
szTemp = fGetLine(szLine, 4096, file);
}
//
// if we've still got an entry in process, save it
//
if (pProcessEntry) {
*ppProcessTail = pProcessEntry;
ppProcessTail = &(pProcessEntry->pNext);
pProcessEntry = NULL;
dwEntries++;
}
//
// now read all the log lines
//
while (szTemp) {
CProcessLogEntry *pEntry;
TCHAR szName[256];
int nFields = _stscanf(szLine, _T("| %s %d '"), szName, &dwEntry);
if (nFields == 2) {
BOOL bFound = FALSE;
pEntry = pSLogEntry->pProcessLog;
while (pEntry) {
if (pEntry->strShimName == szName && pEntry->dwLogNum == dwEntry) {
pEntry->dwOccurences++;
bFound = TRUE;
//
// here's where we would save the occurrence info
//
szBegin = _tcschr(szLine, _T('\''));
if (szBegin) {
szBegin++;
pEntry->arrProblems.Add(szBegin);
}
break;
}
pEntry = pEntry->pNext;
}
if (!bFound) {
//
// need to dump a debug string -- no matching log entry found
//
;
}
}
szTemp = fGetLine(szLine, 4096, file);
}
out:
//
// Add it to the tree
//
pProcessEntry = pSLogEntry->pProcessLog;
while (pProcessEntry != NULL) {
if (pProcessEntry->dwOccurences > 0) {
TVINSERTSTRUCT is;
is.hParent = pSLogEntry->hTreeItem;
is.hInsertAfter = TVI_LAST;
is.item.lParam = (LPARAM)pProcessEntry;
is.item.mask = TVIF_TEXT | TVIF_PARAM;
is.item.pszText = pProcessEntry->strLogTitle.GetBuffer(0);
pProcessEntry->hTreeItem = TreeView_InsertItem(hTree, &is);
for (int i = 0; i < pProcessEntry->arrProblems.GetSize(); i++) {
is.hParent = pProcessEntry->hTreeItem;
is.hInsertAfter = TVI_LAST;
is.item.lParam = 0;
is.item.mask = TVIF_TEXT;
is.item.pszText = pProcessEntry->arrProblems.GetAt(i).GetBuffer(0);
TreeView_InsertItem(hTree, &is);
}
}
pProcessEntry = pProcessEntry->pNext;
}
if (file) {
fclose(file);
file = NULL;
}
return dwEntries;
}
void
SetLogDialogCaption(HWND hDlg, ULONG ulCaptionID, LPCWSTR szAdditional)
{
wstring wstrCaption;
if (AVLoadString(ulCaptionID, wstrCaption)) {
if (szAdditional) {
wstrCaption += szAdditional;
}
SetWindowText(hDlg, wstrCaption.c_str());
}
}
void
RefreshLog(HWND hDlg)
{
TreeView_DeleteAllItems(g_hwndIssues);
if (g_pSessionLogHead) {
delete g_pSessionLogHead;
g_pSessionLogHead = NULL;
}
if (g_szSingleLogFile[0]) {
g_pSessionLogHead = GetSessionLogEntry(hDlg, g_szSingleLogFile);
SetLogDialogCaption(hDlg, IDS_LOG_TITLE_SINGLE, g_szSingleLogFile);
} else {
ReadSessionLog(hDlg, &g_pSessionLogHead);
SetLogDialogCaption(hDlg, IDS_LOG_TITLE_LOCAL, NULL);
}
CSessionLogEntry* pEntry = g_pSessionLogHead;
while (pEntry) {
ReadProcessLog(hDlg, pEntry);
pEntry = pEntry->pNext;
}
EnableWindow(GetDlgItem(hDlg, IDC_BTN_DELETE_LOG), FALSE);
}
void
HandleSizing(
HWND hDlg
)
{
int nWidth;
int nHeight;
RECT rDlg;
HDWP hdwp = BeginDeferWindowPos(0);
GetWindowRect(hDlg, &rDlg);
nWidth = rDlg.right - rDlg.left;
nHeight = rDlg.bottom - rDlg.top;
int deltaW = nWidth - g_cWidth;
int deltaH = nHeight - g_cHeight;
HWND hwnd;
RECT r;
hwnd = GetDlgItem(hDlg, IDC_ISSUES);
GetWindowRect(hwnd, &r);
DeferWindowPos(hdwp,
hwnd,
NULL,
0,
0,
r.right - r.left + deltaW,
r.bottom - r.top + deltaH,
SWP_NOMOVE | SWP_NOZORDER);
hwnd = GetDlgItem(hDlg, IDC_SOLUTIONS_STATIC);
GetWindowRect(hwnd, &r);
MapWindowPoints(NULL, hDlg, (LPPOINT)&r, 2);
DeferWindowPos(hdwp,
hwnd,
NULL,
r.left,
r.top + deltaH,
0,
0,
SWP_NOSIZE | SWP_NOZORDER);
hwnd = GetDlgItem(hDlg, IDC_ISSUE_DESCRIPTION);
GetWindowRect(hwnd, &r);
MapWindowPoints(NULL, hDlg, (LPPOINT)&r, 2);
DeferWindowPos(hdwp,
hwnd,
NULL,
r.left,
r.top + deltaH,
r.right - r.left + deltaW,
r.bottom - r.top,
SWP_NOZORDER);
EndDeferWindowPos(hdwp);
g_cWidth = nWidth;
g_cHeight = nHeight;
}
CSessionLogEntry*
GetSessionLogEntryFromHItem(
HTREEITEM hItem
)
{
CSessionLogEntry* pEntry = g_pSessionLogHead;
while (pEntry) {
if (pEntry->hTreeItem == hItem) {
return pEntry;
}
pEntry = pEntry->pNext;
}
return NULL;
}
void
DeleteAllLogs(
HWND hDlg
)
{
ResetVerifierLog();
RefreshLog(hDlg);
}
void
ExportSelectedLog(
HWND hDlg
)
{
HTREEITEM hItem = TreeView_GetSelection(g_hwndIssues);
WCHAR szName[MAX_PATH];
WCHAR szExt[MAX_PATH];
wstring wstrName;
if (hItem == NULL) {
return;
}
CSessionLogEntry* pSession;
TVITEM ti;
//
// first check if this is a top-level item
//
pSession = GetSessionLogEntryFromHItem(hItem);
if (!pSession) {
return;
}
_wsplitpath(pSession->strLogPath, NULL, NULL, szName, szExt);
wstrName = szName;
wstrName += szExt;
WCHAR wszFilter[] = L"Log files (*.log)\0*.log\0";
OPENFILENAME ofn;
WCHAR wszAppFullPath[MAX_PATH];
WCHAR wszAppShortName[MAX_PATH];
wstring wstrTitle;
if (!AVLoadString(IDS_EXPORT_LOG_TITLE, wstrTitle)) {
wstrTitle = _T("Export Log");
}
wcscpy(wszAppFullPath, wstrName.c_str());
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = hDlg;
ofn.hInstance = NULL;
ofn.lpstrFilter = wszFilter;
ofn.lpstrCustomFilter = NULL;
ofn.nMaxCustFilter = 0;
ofn.nFilterIndex = 0;
ofn.lpstrFile = wszAppFullPath;
ofn.nMaxFile = MAX_PATH;
ofn.lpstrFileTitle = wszAppShortName;
ofn.nMaxFileTitle = MAX_PATH;
ofn.lpstrInitialDir = NULL;
ofn.lpstrTitle = wstrTitle.c_str();
ofn.Flags = OFN_HIDEREADONLY; // hide the "open read-only" checkbox
ofn.lpstrDefExt = _T("log");
if ( !GetSaveFileName(&ofn) )
{
return;
}
if (CopyFile(pSession->strLogPath, wszAppFullPath, FALSE) == 0) {
DWORD dwErr = GetLastError();
AVErrorResourceFormat(IDS_CANT_COPY, dwErr);
}
}
void
DeleteSelectedLog(
HWND hDlg
)
{
HTREEITEM hItem = TreeView_GetSelection(g_hwndIssues);
if (hItem == NULL) {
return;
}
CSessionLogEntry* pSession;
TVITEM ti;
//
// first check if this is a top-level item
//
pSession = GetSessionLogEntryFromHItem(hItem);
if (!pSession) {
return;
}
DeleteFile(pSession->strLogPath);
RefreshLog(hDlg);
}
void
HandleSelectionChanged(
HWND hDlg,
HTREEITEM hItem
)
{
CProcessLogEntry* pEntry;
CSessionLogEntry* pSession;
TVITEM ti;
//
// first check if this is a top-level item
//
pSession = GetSessionLogEntryFromHItem(hItem);
if (pSession && !g_szSingleLogFile[0]) {
EnableWindow(GetDlgItem(hDlg, IDC_BTN_EXPORT_LOG), TRUE);
EnableWindow(GetDlgItem(hDlg, IDC_BTN_DELETE_LOG), TRUE);
} else {
EnableWindow(GetDlgItem(hDlg, IDC_BTN_EXPORT_LOG), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_BTN_DELETE_LOG), FALSE);
}
ti.mask = TVIF_HANDLE | TVIF_PARAM;
ti.hItem = hItem;
TreeView_GetItem(g_hwndIssues, &ti);
if (ti.lParam == 0) {
hItem = TreeView_GetParent(g_hwndIssues, hItem);
ti.mask = TVIF_HANDLE | TVIF_PARAM;
ti.hItem = hItem;
TreeView_GetItem(g_hwndIssues, &ti);
if (ti.lParam == 0) {
return;
}
}
pEntry = (CProcessLogEntry*)ti.lParam;
SetDlgItemText(hDlg, IDC_ISSUE_DESCRIPTION, pEntry->strLogDescription);
}
// Message handler for log view dialog.
LRESULT CALLBACK
DlgViewLog(
HWND hDlg,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
HDC hDC;
switch (message) {
case WM_INITDIALOG:
{
EnableWindow(GetDlgItem(hDlg, IDC_BTN_DELETE_LOG), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_BTN_EXPORT_LOG), FALSE);
if (g_szSingleLogFile[0]) {
EnableWindow(GetDlgItem(hDlg, IDC_BTN_DELETE_ALL), FALSE);
}
g_hwndIssues = GetDlgItem(hDlg, IDC_ISSUES);
RECT r;
GetWindowRect(hDlg, &r);
g_cWidth = r.right - r.left;
g_cHeight = r.bottom - r.top;
RefreshLog(hDlg);
return TRUE;
}
break;
case WM_SIZE:
HandleSizing(hDlg);
break;
case WM_GETMINMAXINFO:
{
MINMAXINFO* pmmi = (MINMAXINFO*)lParam;
pmmi->ptMinTrackSize.y = 300;
return 0;
break;
}
case WM_NOTIFY:
if (wParam == IDC_ISSUES) {
LPNMHDR pnm = (LPNMHDR)lParam;
if (g_hwndIssues == NULL) {
break;
}
switch (pnm->code) {
case NM_CLICK:
{
TVHITTESTINFO ht;
HTREEITEM hItem;
GetCursorPos(&ht.pt);
ScreenToClient(g_hwndIssues, &ht.pt);
TreeView_HitTest(g_hwndIssues, &ht);
if (ht.hItem == NULL) {
break;
}
HandleSelectionChanged(hDlg, ht.hItem);
break;
}
case TVN_SELCHANGED:
{
NM_TREEVIEW* pnmtv = (NM_TREEVIEW*)pnm;
HandleSelectionChanged(hDlg, pnmtv->itemNew.hItem);
break;
}
}
} else if (wParam == IDC_ISSUE_DESCRIPTION) {
LPNMHDR pnm = (LPNMHDR)lParam;
if (g_hwndIssues == NULL) {
break;
}
switch (pnm->code) {
case NM_CLICK:
{
SHELLEXECUTEINFO sei = { 0};
HTREEITEM hItem = TreeView_GetSelection(g_hwndIssues);
if (hItem == NULL) {
break;
}
CProcessLogEntry* pEntry;
TVITEM ti;
ti.mask = TVIF_HANDLE | TVIF_PARAM;
ti.hItem = hItem;
TreeView_GetItem(g_hwndIssues, &ti);
if (ti.lParam == 0) {
hItem = TreeView_GetParent(g_hwndIssues, hItem);
ti.mask = TVIF_HANDLE | TVIF_PARAM;
ti.hItem = hItem;
TreeView_GetItem(g_hwndIssues, &ti);
if (ti.lParam == 0) {
break;
}
}
pEntry = (CProcessLogEntry*)ti.lParam;
SetDlgItemText(hDlg, IDC_ISSUE_DESCRIPTION, pEntry->strLogDescription);
sei.cbSize = sizeof(SHELLEXECUTEINFO);
sei.fMask = SEE_MASK_DOENVSUBST;
sei.hwnd = hDlg;
sei.nShow = SW_SHOWNORMAL;
sei.lpFile = pEntry->strLogURL;
ShellExecuteEx(&sei);
}
}
}
break;
case WM_COMMAND:
switch (LOWORD (wParam)) {
case IDC_BTN_DELETE_LOG:
DeleteSelectedLog(hDlg);
break;
case IDC_BTN_DELETE_ALL:
DeleteAllLogs(hDlg);
break;
case IDC_BTN_EXPORT_LOG:
ExportSelectedLog(hDlg);
break;
case IDOK:
case IDCANCEL:
EndDialog(hDlg, LOWORD(wParam));
return TRUE;
break;
}
break;
}
return FALSE;
}