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
1466 lines
38 KiB
|
|
#include "precomp.h"
|
|
|
|
#include "viewlog.h"
|
|
|
|
using namespace ShimLib;
|
|
|
|
CSessionLogEntryArray g_arrSessionLog;
|
|
|
|
TCHAR g_szSingleLogFile[MAX_PATH] = _T("");
|
|
|
|
HWND g_hwndIssues = NULL;
|
|
int g_cWidth;
|
|
int g_cHeight;
|
|
|
|
HWND g_hwndDetails = NULL;
|
|
int g_cWidthDetails;
|
|
int g_cHeightDetails;
|
|
|
|
VLOG_LEVEL g_eMinLogLevel = VLOG_LEVEL_WARNING;
|
|
|
|
CSessionLogEntry *g_pDetailsEntry = NULL;
|
|
|
|
const int MAX_INSTANCE_REPEAT = 10;
|
|
|
|
//
|
|
// forward function declarations
|
|
//
|
|
INT_PTR CALLBACK
|
|
DlgViewLogDetails(
|
|
HWND hDlg,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
);
|
|
|
|
|
|
TCHAR*
|
|
fGetLine(
|
|
TCHAR* szLine,
|
|
int nChars,
|
|
FILE* file
|
|
)
|
|
{
|
|
if (_fgetts(szLine, nChars, file)) {
|
|
int nLen = _tcslen(szLine);
|
|
while (nLen && (szLine[nLen - 1] == _T('\n') || szLine[nLen - 1] == _T('\r'))) {
|
|
szLine[nLen - 1] = 0;
|
|
nLen--;
|
|
}
|
|
return szLine;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
IsModuleInSystem32(
|
|
LPCTSTR szModule
|
|
)
|
|
{
|
|
TCHAR szSysDir[MAX_PATH];
|
|
TCHAR szPath[MAX_PATH];
|
|
|
|
if (!GetSystemDirectory(szSysDir, MAX_PATH)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (FAILED(StringCchPrintf(szPath,
|
|
ARRAY_LENGTH(szPath),
|
|
_T("%s\\%s"),
|
|
szSysDir,
|
|
szModule))) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (GetFileAttributes(szPath) == INVALID_FILE_ATTRIBUTES) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
BOOL
|
|
GetSessionLogEntry(
|
|
HWND hDlg,
|
|
LPCTSTR szLogFullPath,
|
|
CSessionLogEntryArray& arrEntries
|
|
)
|
|
{
|
|
TCHAR szLine[4096];
|
|
FILE * file = NULL;
|
|
SYSTEMTIME stime;
|
|
CSessionLogEntry *pEntryTemp = NULL;
|
|
TCHAR *szBegin = NULL;
|
|
TCHAR *szEnd = NULL;
|
|
BOOL bSuccess = FALSE;
|
|
|
|
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) {
|
|
DPF("[GetSessionLogEntry] '%ls' does not appear to be a AppVerifier log file.",
|
|
szLogFullPath);
|
|
|
|
goto out;
|
|
}
|
|
|
|
{
|
|
CSessionLogEntry EntryTemp;
|
|
|
|
EntryTemp.RunTime = stime;
|
|
EntryTemp.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;
|
|
|
|
EntryTemp.strExePath = szBegin;
|
|
|
|
*szEnd = 0;
|
|
|
|
//
|
|
// split the path and get the name and extension
|
|
//
|
|
_tsplitpath(EntryTemp.strExePath.c_str(), NULL, NULL, szName, szExt);
|
|
|
|
EntryTemp.strExeName = szName;
|
|
EntryTemp.strExeName += szExt;
|
|
}
|
|
}
|
|
|
|
arrEntries.push_back(EntryTemp);
|
|
|
|
bSuccess = TRUE;
|
|
}
|
|
}
|
|
|
|
out:
|
|
|
|
if (file) {
|
|
fclose(file);
|
|
file = NULL;
|
|
}
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
|
|
DWORD
|
|
ReadSessionLog(
|
|
HWND hDlg,
|
|
CSessionLogEntryArray& arrEntries
|
|
)
|
|
{
|
|
TCHAR szLine[4096];
|
|
FILE * file = NULL;
|
|
SYSTEMTIME stime;
|
|
DWORD dwEntries = 0;
|
|
DWORD cchSize;
|
|
TCHAR *szBegin = NULL;
|
|
TCHAR *szEnd = NULL;
|
|
|
|
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
|
|
//
|
|
cchSize = GetAppVerifierLogPath(szVLog, ARRAY_LENGTH(szVLog));
|
|
|
|
if (cchSize > ARRAY_LENGTH(szVLog) || cchSize == 0) {
|
|
return 0;
|
|
}
|
|
|
|
StringCchCopy(szLogSearch, ARRAY_LENGTH(szLogSearch), szVLog);
|
|
StringCchCatW(szLogSearch, ARRAY_LENGTH(szLogSearch), _T("\\"));
|
|
StringCchCat(szLogSearch, ARRAY_LENGTH(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;
|
|
}
|
|
|
|
StringCchCopy(szLogFullPath, ARRAY_LENGTH(szLogFullPath), szVLog);
|
|
StringCchCat(szLogFullPath, ARRAY_LENGTH(szLogFullPath), _T("\\"));
|
|
StringCchCat(szLogFullPath, ARRAY_LENGTH(szLogFullPath), FindData.cFileName);
|
|
|
|
BOOL bSuccess = GetSessionLogEntry(hDlg, szLogFullPath, arrEntries);
|
|
if (bSuccess) {
|
|
dwEntries++;
|
|
}
|
|
|
|
nextFile:
|
|
|
|
if (!FindNextFile(hFind, &FindData)) {
|
|
FindClose(hFind);
|
|
hFind = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
|
|
return dwEntries;
|
|
}
|
|
|
|
void
|
|
FillTreeView(
|
|
HWND hDlg
|
|
)
|
|
{
|
|
HWND hTree = GetDlgItem(hDlg, IDC_ISSUES);
|
|
CProcessLogEntry *pProcessEntry = NULL;
|
|
|
|
if (!hTree) {
|
|
return;
|
|
}
|
|
|
|
TreeView_DeleteAllItems(hTree);
|
|
|
|
for (CSessionLogEntry* pSLogEntry = g_arrSessionLog.begin();
|
|
pSLogEntry != g_arrSessionLog.end();
|
|
pSLogEntry++) {
|
|
|
|
//
|
|
// Add it to the tree.
|
|
//
|
|
TVINSERTSTRUCT is;
|
|
|
|
WCHAR szItem[256];
|
|
|
|
StringCchPrintf(szItem,
|
|
ARRAY_LENGTH(szItem),
|
|
L"%ls - %d/%d/%d %d:%02d",
|
|
pSLogEntry->strExeName.c_str(),
|
|
pSLogEntry->RunTime.wMonth,
|
|
pSLogEntry->RunTime.wDay,
|
|
pSLogEntry->RunTime.wYear,
|
|
pSLogEntry->RunTime.wHour,
|
|
pSLogEntry->RunTime.wMinute);
|
|
|
|
is.hParent = TVI_ROOT;
|
|
is.hInsertAfter = TVI_LAST;
|
|
is.item.lParam = 0;
|
|
is.item.mask = TVIF_TEXT;
|
|
is.item.pszText = szItem;
|
|
|
|
pSLogEntry->hTreeItem = TreeView_InsertItem(hTree, &is);
|
|
|
|
//
|
|
// Add it to the tree
|
|
//
|
|
for (pProcessEntry = pSLogEntry->arrProcessLogEntries.begin();
|
|
pProcessEntry != pSLogEntry->arrProcessLogEntries.end();
|
|
pProcessEntry++ ) {
|
|
|
|
if (pProcessEntry->dwOccurences > 0 && pProcessEntry->eLevel >= g_eMinLogLevel) {
|
|
|
|
is.hParent = pSLogEntry->hTreeItem;
|
|
is.hInsertAfter = TVI_LAST;
|
|
is.item.lParam = (LPARAM)pProcessEntry;
|
|
is.item.mask = TVIF_TEXT | TVIF_PARAM;
|
|
is.item.pszText = (LPWSTR)pProcessEntry->strLogTitle.c_str();
|
|
|
|
pProcessEntry->hTreeItem = TreeView_InsertItem(hTree, &is);
|
|
|
|
TreeView_SetItemState(hTree,
|
|
pProcessEntry->hTreeItem,
|
|
INDEXTOSTATEIMAGEMASK(pProcessEntry->eLevel + 1),
|
|
TVIS_STATEIMAGEMASK);
|
|
|
|
DWORD * pdwInstance;
|
|
DWORD dwInstances = 0;
|
|
|
|
for (pdwInstance = pProcessEntry->arrLogInstances.begin();
|
|
pdwInstance != pProcessEntry->arrLogInstances.end();
|
|
pdwInstance++ ) {
|
|
|
|
wstring strInstText;
|
|
WCHAR szTemp[50];
|
|
|
|
CProcessLogInstance *pInstance = &(pSLogEntry->arrProcessLogInstances[*pdwInstance]);
|
|
|
|
//
|
|
// check to see if we should filter out the logs from system DLLs
|
|
// Complicated test - we filter system dlls, but not the EXE under test.
|
|
// Filtering is only performed if we're not in NTDEV internal mode
|
|
//
|
|
BOOL bFilterSystem = FALSE;
|
|
|
|
if (!g_bInternalMode &&
|
|
IsModuleInSystem32(pInstance->strModule.c_str()) &&
|
|
(_tcsicmp(pInstance->strModule.c_str(), pSLogEntry->strExeName.c_str()) != 0)) {
|
|
|
|
bFilterSystem = TRUE;
|
|
}
|
|
|
|
if (!pInstance->bDuplicate && pInstance->eLevel >= g_eMinLogLevel && !bFilterSystem) {
|
|
if (pInstance->strModule != L"?" && pInstance->dwOffset != 0) {
|
|
StringCchPrintf(szTemp, ARRAY_LENGTH(szTemp), L"(%s:%08X) ", pInstance->strModule.c_str(), pInstance->dwOffset);
|
|
strInstText += szTemp;
|
|
}
|
|
strInstText += pInstance->strText;
|
|
|
|
if (pInstance->dwNumRepeats > 1) {
|
|
StringCchPrintf(szTemp, ARRAY_LENGTH(szTemp), L" [%dx]", pInstance->dwNumRepeats);
|
|
strInstText += szTemp;
|
|
}
|
|
|
|
is.hParent = pProcessEntry->hTreeItem;
|
|
is.hInsertAfter = TVI_LAST;
|
|
is.item.lParam = 0;
|
|
is.item.mask = TVIF_TEXT;
|
|
is.item.pszText = (LPWSTR)(strInstText.c_str());
|
|
|
|
HTREEITEM hItem = TreeView_InsertItem(hTree, &is);
|
|
dwInstances++;
|
|
|
|
TreeView_SetItemState(hTree,
|
|
hItem,
|
|
INDEXTOSTATEIMAGEMASK(pInstance->eLevel + 1),
|
|
TVIS_STATEIMAGEMASK);
|
|
}
|
|
}
|
|
|
|
if (dwInstances == 0) {
|
|
//
|
|
// we didn't end up adding any children, so delete this entry
|
|
//
|
|
TreeView_DeleteItem(hTree, pProcessEntry->hTreeItem);
|
|
pProcessEntry->hTreeItem = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DWORD
|
|
ReadProcessLog(HWND hDlg, CSessionLogEntry* pSLogEntry)
|
|
{
|
|
static TCHAR szLine[4096];
|
|
FILE * file = NULL;
|
|
CProcessLogEntry *pProcessEntry = NULL;
|
|
static TCHAR szShimName[4096];
|
|
DWORD dwEntries = 0;
|
|
TCHAR * szTemp = NULL;
|
|
DWORD dwEntry = 0;
|
|
TCHAR *szBegin = NULL;
|
|
|
|
HWND hTree = GetDlgItem(hDlg, IDC_ISSUES);
|
|
|
|
if (!pSLogEntry) {
|
|
return 0;
|
|
}
|
|
|
|
file = _tfopen(pSLogEntry->strLogPath.c_str(), _T("rt"));
|
|
|
|
if (!file) {
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// first get the headers
|
|
//
|
|
szTemp = fGetLine(szLine, ARRAY_LENGTH(szLine), file);
|
|
while (szTemp) {
|
|
|
|
if (szLine[0] != _T('#')) {
|
|
goto nextLine;
|
|
}
|
|
|
|
//
|
|
// relatively simplistic guard against overflow in szShimName in the scanf, below
|
|
//
|
|
if (_tcslen(szLine) >= ARRAY_LENGTH(szShimName)) {
|
|
goto nextLine;
|
|
}
|
|
|
|
if (_stscanf(szLine, _T("# LOGENTRY %s %d '"), szShimName, &dwEntry) == 2) {
|
|
if (pProcessEntry) {
|
|
pSLogEntry->arrProcessLogEntries.push_back(*pProcessEntry);
|
|
|
|
delete pProcessEntry;
|
|
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.size()) {
|
|
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, ARRAY_LENGTH(szLine), file);
|
|
|
|
}
|
|
|
|
//
|
|
// if we've still got an entry in process, save it
|
|
//
|
|
if (pProcessEntry) {
|
|
pSLogEntry->arrProcessLogEntries.push_back(*pProcessEntry);
|
|
|
|
delete pProcessEntry;
|
|
pProcessEntry = NULL;
|
|
|
|
dwEntries++;
|
|
}
|
|
|
|
//
|
|
// go back to the beginning and read the log lines
|
|
//
|
|
if (fseek(file, 0, SEEK_SET) != 0) {
|
|
return 0;
|
|
}
|
|
|
|
szTemp = fGetLine(szLine, ARRAY_LENGTH(szLine), file);
|
|
|
|
while (szTemp) {
|
|
CProcessLogEntry *pEntry;
|
|
static TCHAR szName[4096];
|
|
DWORD dwOffset = 0;
|
|
static TCHAR szModule[4096];
|
|
VLOG_LEVEL eLevel = VLOG_LEVEL_ERROR;
|
|
DWORD dwLen;
|
|
|
|
StringCchCopyW(szModule, ARRAY_LENGTH(szModule), L"<unknown>");
|
|
|
|
if (szLine[0] != _T('|')) {
|
|
goto nextInstance;
|
|
}
|
|
|
|
//
|
|
// relatively simplistic guard against overflow in the scanf, below
|
|
//
|
|
dwLen = _tcslen(szLine);
|
|
if (dwLen >= ARRAY_LENGTH(szName) || dwLen >= ARRAY_LENGTH(szModule) ) {
|
|
goto nextLine;
|
|
}
|
|
|
|
int nFields = _stscanf(szLine, _T("| %s %d | %d %s %x"), szName, &dwEntry, &eLevel, szModule, &dwOffset);
|
|
if (nFields >= 2) {
|
|
BOOL bFound = FALSE;
|
|
CProcessLogInstance Instance;
|
|
|
|
Instance.strModule = szModule;
|
|
Instance.eLevel = eLevel;
|
|
Instance.dwOffset = dwOffset;
|
|
|
|
szBegin = _tcschr(szLine, _T('\''));
|
|
if (szBegin) {
|
|
szBegin++;
|
|
|
|
Instance.strText = szBegin;
|
|
}
|
|
|
|
//
|
|
// Get the associated entry (header) for this log instance
|
|
//
|
|
for (pEntry = pSLogEntry->arrProcessLogEntries.begin();
|
|
pEntry != pSLogEntry->arrProcessLogEntries.end();
|
|
pEntry++ ) {
|
|
|
|
if (pEntry->strShimName == szName && pEntry->dwLogNum == dwEntry) {
|
|
bFound = TRUE;
|
|
|
|
break;
|
|
}
|
|
|
|
}
|
|
if (!bFound) {
|
|
//
|
|
// no matching log entry found
|
|
//
|
|
DPF("[ReadProcessLog] Mo matching log header found for log instance '%ls'.",
|
|
Instance.strText.c_str());
|
|
pEntry = NULL;
|
|
goto nextInstance;
|
|
}
|
|
|
|
Instance.dwProcessLogEntry = pEntry - pSLogEntry->arrProcessLogEntries.begin();
|
|
|
|
//
|
|
// check to see if this is a repeat of a previous entry
|
|
//
|
|
for (CProcessLogInstance *pInstance = pSLogEntry->arrProcessLogInstances.begin();
|
|
pInstance != pSLogEntry->arrProcessLogInstances.end();
|
|
pInstance++) {
|
|
if (pInstance->strModule == Instance.strModule &&
|
|
pInstance->dwOffset == Instance.dwOffset &&
|
|
pInstance->strText == Instance.strText &&
|
|
pInstance->dwProcessLogEntry == Instance.dwProcessLogEntry &&
|
|
pInstance->bDuplicate == FALSE) {
|
|
|
|
//
|
|
// we won't save more than a certain number of repeats
|
|
//
|
|
if (pInstance->dwNumRepeats >= MAX_INSTANCE_REPEAT) {
|
|
goto nextInstance;
|
|
}
|
|
|
|
//
|
|
// it's a match, so count up the repeats on the one we found, and mark
|
|
// this one duplicate
|
|
//
|
|
pInstance->dwNumRepeats++;
|
|
Instance.bDuplicate = TRUE;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// save the instance in the chronological log
|
|
//
|
|
pSLogEntry->arrProcessLogInstances.push_back(Instance);
|
|
|
|
//
|
|
// update the cross-pointer in the entry object
|
|
//
|
|
pEntry->arrLogInstances.push_back(pSLogEntry->arrProcessLogInstances.size() - 1);
|
|
|
|
//
|
|
// now update the entry object's count of instances
|
|
//
|
|
pEntry->dwOccurences++;
|
|
|
|
//
|
|
// update the level
|
|
//
|
|
if (pEntry->eLevel < Instance.eLevel) {
|
|
pEntry->eLevel = Instance.eLevel;
|
|
}
|
|
}
|
|
|
|
nextInstance:
|
|
|
|
szTemp = fGetLine(szLine, ARRAY_LENGTH(szLine), file);
|
|
|
|
}
|
|
|
|
out:
|
|
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);
|
|
|
|
g_arrSessionLog.clear();
|
|
|
|
if (g_szSingleLogFile[0]) {
|
|
GetSessionLogEntry(hDlg, g_szSingleLogFile, g_arrSessionLog);
|
|
SetLogDialogCaption(hDlg, IDS_LOG_TITLE_SINGLE, g_szSingleLogFile);
|
|
} else {
|
|
ReadSessionLog(hDlg, g_arrSessionLog);
|
|
SetLogDialogCaption(hDlg, IDS_LOG_TITLE_LOCAL, NULL);
|
|
}
|
|
|
|
for (CSessionLogEntry* pEntry = g_arrSessionLog.begin();
|
|
pEntry != g_arrSessionLog.end();
|
|
pEntry++ ) {
|
|
|
|
ReadProcessLog(hDlg, pEntry);
|
|
}
|
|
|
|
FillTreeView(hDlg);
|
|
|
|
EnableWindow(GetDlgItem(hDlg, IDC_BTN_DELETE_LOG), FALSE);
|
|
|
|
}
|
|
|
|
void
|
|
InitListViewDetails(HWND hDlg)
|
|
{
|
|
DWORD dwLine;
|
|
|
|
static const WCHAR *aszSeverity[3] = {
|
|
L"Info",
|
|
L"Warning",
|
|
L"Error"
|
|
};
|
|
|
|
if (!g_pDetailsEntry || !g_hwndDetails) {
|
|
return;
|
|
}
|
|
|
|
ListView_SetExtendedListViewStyleEx(g_hwndDetails, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);
|
|
|
|
|
|
LVCOLUMN lvc;
|
|
|
|
lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH;
|
|
lvc.fmt = LVCFMT_LEFT;
|
|
lvc.cx = 40;
|
|
lvc.iSubItem = 0;
|
|
lvc.pszText = L"Line";
|
|
|
|
ListView_InsertColumn(g_hwndDetails, 0, &lvc);
|
|
|
|
lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH;
|
|
lvc.fmt = LVCFMT_LEFT;
|
|
lvc.cx = 80;
|
|
lvc.iSubItem = 1;
|
|
lvc.pszText = L"Module";
|
|
|
|
ListView_InsertColumn(g_hwndDetails, 1, &lvc);
|
|
|
|
lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH;
|
|
lvc.fmt = LVCFMT_LEFT;
|
|
lvc.cx = 70;
|
|
lvc.iSubItem = 2;
|
|
lvc.pszText = L"Offset";
|
|
|
|
ListView_InsertColumn(g_hwndDetails, 2, &lvc);
|
|
|
|
lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH;
|
|
lvc.fmt = LVCFMT_LEFT;
|
|
lvc.cx = 70;
|
|
lvc.iSubItem = 3;
|
|
lvc.pszText = L"Severity";
|
|
|
|
ListView_InsertColumn(g_hwndDetails, 3, &lvc);
|
|
|
|
lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH;
|
|
lvc.fmt = LVCFMT_LEFT;
|
|
lvc.cx = 900;
|
|
lvc.iSubItem = 4;
|
|
lvc.pszText = L"Text";
|
|
|
|
ListView_InsertColumn(g_hwndDetails, 4, &lvc);
|
|
|
|
|
|
CProcessLogInstance* pInstance;
|
|
|
|
for (pInstance = g_pDetailsEntry->arrProcessLogInstances.begin(), dwLine = 0;
|
|
pInstance != g_pDetailsEntry->arrProcessLogInstances.end();
|
|
pInstance++, dwLine++ ) {
|
|
|
|
WCHAR szTemp[1024];
|
|
LVITEM lvi;
|
|
|
|
//
|
|
// if we're in external mode, filter out logs from modules in system32
|
|
//
|
|
if (!g_bInternalMode &&
|
|
IsModuleInSystem32(pInstance->strModule.c_str()) &&
|
|
(_tcsicmp(pInstance->strModule.c_str(), g_pDetailsEntry->strExeName.c_str()) != 0)) {
|
|
|
|
dwLine--;
|
|
|
|
continue;
|
|
}
|
|
|
|
szTemp[0] = 0;
|
|
|
|
StringCchPrintf(szTemp, ARRAY_LENGTH(szTemp), L"%04d", dwLine);
|
|
|
|
lvi.mask = LVIF_TEXT | LVIF_PARAM;
|
|
lvi.pszText = szTemp;
|
|
lvi.lParam = (LPARAM)pInstance;
|
|
lvi.iItem = 99999;
|
|
lvi.iSubItem = 0;
|
|
|
|
int nItem = ListView_InsertItem(g_hwndDetails, &lvi);
|
|
|
|
if (nItem == -1) {
|
|
DPF("[InitListViewDetails] Couldn't add line %d.",
|
|
dwLine);
|
|
return;
|
|
}
|
|
|
|
if (pInstance->strModule != L"?" && pInstance->dwOffset != 0) {
|
|
ListView_SetItemText(g_hwndDetails, nItem, 1, (LPWSTR)pInstance->strModule.c_str());
|
|
}
|
|
|
|
if (pInstance->dwOffset != 0) {
|
|
StringCchPrintf(szTemp, ARRAY_LENGTH(szTemp), L"%08X", pInstance->dwOffset);
|
|
|
|
ListView_SetItemText(g_hwndDetails, nItem, 2, szTemp);
|
|
}
|
|
|
|
if (pInstance->eLevel <= VLOG_LEVEL_ERROR) {
|
|
StringCchCopy(szTemp, ARRAY_LENGTH(szTemp), aszSeverity[pInstance->eLevel]);
|
|
} else {
|
|
StringCchCopy(szTemp, ARRAY_LENGTH(szTemp), L"<unknown>");
|
|
}
|
|
|
|
ListView_SetItemText(g_hwndDetails, nItem, 3, szTemp);
|
|
|
|
CProcessLogEntry* pEntry = &(g_pDetailsEntry->arrProcessLogEntries[pInstance->dwProcessLogEntry]);
|
|
|
|
StringCchPrintf(szTemp,
|
|
ARRAY_LENGTH(szTemp),
|
|
L"%s - %s",
|
|
pEntry->strLogTitle.c_str(),
|
|
pInstance->strText.c_str());
|
|
|
|
ListView_SetItemText(g_hwndDetails, nItem, 4, szTemp);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void
|
|
HandleSizing(
|
|
HWND hDlg
|
|
)
|
|
{
|
|
int nWidth;
|
|
int nHeight;
|
|
RECT rDlg;
|
|
|
|
HDWP hdwp = BeginDeferWindowPos(0);
|
|
|
|
if (!hdwp) {
|
|
return;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
void
|
|
HandleSizingDetails(
|
|
HWND hDlg
|
|
)
|
|
{
|
|
int nWidth;
|
|
int nHeight;
|
|
RECT rDlg;
|
|
|
|
HDWP hdwp = BeginDeferWindowPos(0);
|
|
|
|
if (!hdwp) {
|
|
return;
|
|
}
|
|
|
|
GetWindowRect(hDlg, &rDlg);
|
|
|
|
nWidth = rDlg.right - rDlg.left;
|
|
nHeight = rDlg.bottom - rDlg.top;
|
|
|
|
int deltaW = nWidth - g_cWidthDetails;
|
|
int deltaH = nHeight - g_cHeightDetails;
|
|
|
|
HWND hwnd;
|
|
RECT r;
|
|
|
|
hwnd = GetDlgItem(hDlg, IDC_LIST_DETAILS);
|
|
|
|
GetWindowRect(hwnd, &r);
|
|
|
|
DeferWindowPos(hdwp,
|
|
hwnd,
|
|
NULL,
|
|
0,
|
|
0,
|
|
r.right - r.left + deltaW,
|
|
r.bottom - r.top + deltaH,
|
|
SWP_NOMOVE | SWP_NOZORDER);
|
|
|
|
|
|
EndDeferWindowPos(hdwp);
|
|
|
|
g_cWidthDetails = nWidth;
|
|
g_cHeightDetails = nHeight;
|
|
}
|
|
|
|
CSessionLogEntry*
|
|
GetSessionLogEntryFromHItem(
|
|
HTREEITEM hItem
|
|
)
|
|
{
|
|
for (CSessionLogEntry* pEntry = g_arrSessionLog.begin();
|
|
pEntry != g_arrSessionLog.end();
|
|
pEntry++ ) {
|
|
|
|
if (pEntry->hTreeItem == hItem) {
|
|
return pEntry;
|
|
}
|
|
}
|
|
|
|
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.c_str(), 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");
|
|
}
|
|
|
|
StringCchCopy(wszAppFullPath, ARRAY_LENGTH(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.c_str(), 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.c_str());
|
|
|
|
RefreshLog(hDlg);
|
|
|
|
|
|
}
|
|
|
|
void
|
|
OpenSelectedLogDetails(
|
|
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
|
|
//
|
|
g_pDetailsEntry = GetSessionLogEntryFromHItem(hItem);
|
|
if (!g_pDetailsEntry) {
|
|
return;
|
|
}
|
|
|
|
DialogBox(g_hInstance, (LPCTSTR)IDD_VIEW_LOG_DETAIL, hDlg, DlgViewLogDetails);
|
|
|
|
g_pDetailsEntry = NULL;
|
|
}
|
|
|
|
void
|
|
SetDescriptionText(
|
|
HWND hDlg,
|
|
CProcessLogEntry* pEntry
|
|
)
|
|
{
|
|
wstring strText;
|
|
|
|
if (pEntry) {
|
|
strText = pEntry->strLogDescription;
|
|
if (pEntry->strLogURL.size()) {
|
|
wstring strIntro, strLink;
|
|
|
|
if (AVLoadString(IDS_URL_INTRO, strIntro) && AVLoadString(IDS_URL_LINK, strLink)) {
|
|
strText += L" " + strIntro + L" <A HREF=\"" + pEntry->strLogURL + L"\">" + strLink + L"</A>";
|
|
}
|
|
}
|
|
}
|
|
|
|
if (strText.size() == 0) {
|
|
AVLoadString(IDS_SOLUTION_DEFAULT, strText);
|
|
}
|
|
|
|
SetDlgItemText(hDlg, IDC_ISSUE_DESCRIPTION, strText.c_str());
|
|
}
|
|
|
|
void
|
|
HandleSelectionChanged(
|
|
HWND hDlg,
|
|
HTREEITEM hItem
|
|
)
|
|
{
|
|
CProcessLogEntry* pEntry = NULL;
|
|
CSessionLogEntry* pSession = NULL;
|
|
TVITEM ti;
|
|
wstring strText;
|
|
|
|
if (!hDlg) {
|
|
return;
|
|
}
|
|
if (!hItem) {
|
|
EnableWindow(GetDlgItem(hDlg, IDC_BTN_VIEW_DETAILS), FALSE);
|
|
EnableWindow(GetDlgItem(hDlg, IDC_BTN_EXPORT_LOG), FALSE);
|
|
EnableWindow(GetDlgItem(hDlg, IDC_BTN_DELETE_LOG), FALSE);
|
|
goto out;
|
|
}
|
|
|
|
//
|
|
// first check if this is a top-level item
|
|
//
|
|
pSession = GetSessionLogEntryFromHItem(hItem);
|
|
if (pSession) {
|
|
EnableWindow(GetDlgItem(hDlg, IDC_BTN_VIEW_DETAILS), TRUE);
|
|
if (!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);
|
|
}
|
|
} else {
|
|
EnableWindow(GetDlgItem(hDlg, IDC_BTN_VIEW_DETAILS), 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) {
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
pEntry = (CProcessLogEntry*)ti.lParam;
|
|
|
|
out:
|
|
|
|
SetDescriptionText(hDlg, pEntry);
|
|
}
|
|
|
|
// Message handler for log view dialog.
|
|
INT_PTR 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);
|
|
EnableWindow(GetDlgItem(hDlg, IDC_BTN_VIEW_DETAILS), FALSE);
|
|
|
|
if (g_eMinLogLevel == VLOG_LEVEL_INFO) {
|
|
CheckRadioButton(hDlg, IDC_SHOW_ALL, IDC_SHOW_ERRORS, IDC_SHOW_ALL);
|
|
} else {
|
|
CheckRadioButton(hDlg, IDC_SHOW_ALL, IDC_SHOW_ERRORS, IDC_SHOW_ERRORS);
|
|
}
|
|
|
|
if (g_szSingleLogFile[0]) {
|
|
EnableWindow(GetDlgItem(hDlg, IDC_BTN_DELETE_ALL), FALSE);
|
|
}
|
|
|
|
g_hwndIssues = GetDlgItem(hDlg, IDC_ISSUES);
|
|
|
|
HIMAGELIST hImage = ImageList_LoadImage(g_hInstance,
|
|
MAKEINTRESOURCE(IDB_BULLETS),
|
|
16,
|
|
0,
|
|
CLR_DEFAULT,
|
|
IMAGE_BITMAP,
|
|
LR_LOADTRANSPARENT);
|
|
|
|
if (hImage != NULL) {
|
|
TreeView_SetImageList(g_hwndIssues,
|
|
hImage,
|
|
TVSIL_STATE);
|
|
}
|
|
|
|
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);
|
|
|
|
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 = NULL;
|
|
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;
|
|
|
|
SetDescriptionText(hDlg, pEntry);
|
|
|
|
if (pEntry) {
|
|
sei.cbSize = sizeof(SHELLEXECUTEINFO);
|
|
sei.fMask = SEE_MASK_DOENVSUBST;
|
|
sei.hwnd = hDlg;
|
|
sei.nShow = SW_SHOWNORMAL;
|
|
sei.lpFile = (LPWSTR)(pEntry->strLogURL.c_str());
|
|
|
|
ShellExecuteEx(&sei);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
switch (LOWORD (wParam)) {
|
|
case IDC_SHOW_ERRORS:
|
|
g_eMinLogLevel = VLOG_LEVEL_WARNING;
|
|
FillTreeView(hDlg);
|
|
break;
|
|
|
|
case IDC_SHOW_ALL:
|
|
g_eMinLogLevel = VLOG_LEVEL_INFO;
|
|
FillTreeView(hDlg);
|
|
break;
|
|
|
|
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 IDC_BTN_VIEW_DETAILS:
|
|
OpenSelectedLogDetails(hDlg);
|
|
break;
|
|
|
|
case IDOK:
|
|
case IDCANCEL:
|
|
EndDialog(hDlg, LOWORD(wParam));
|
|
return TRUE;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
int CALLBACK
|
|
DetailsCompareFunc(
|
|
LPARAM lParam1,
|
|
LPARAM lParam2,
|
|
LPARAM lParamSort
|
|
)
|
|
{
|
|
WCHAR szTemp1[256], szTemp2[256];
|
|
|
|
szTemp1[0] = 0;
|
|
szTemp2[0] = 0;
|
|
|
|
ListView_GetItemText(g_hwndDetails, lParam1, lParamSort, szTemp1, ARRAY_LENGTH(szTemp1));
|
|
ListView_GetItemText(g_hwndDetails, lParam2, lParamSort, szTemp2, ARRAY_LENGTH(szTemp2));
|
|
|
|
return _wcsicmp(szTemp1, szTemp2);
|
|
}
|
|
|
|
// Message handler for details log view dialog.
|
|
INT_PTR CALLBACK
|
|
DlgViewLogDetails(
|
|
HWND hDlg,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
HDC hDC;
|
|
|
|
switch (message) {
|
|
case WM_INITDIALOG:
|
|
{
|
|
g_hwndDetails = GetDlgItem(hDlg, IDC_LIST_DETAILS);
|
|
|
|
RECT r;
|
|
|
|
GetWindowRect(hDlg, &r);
|
|
|
|
g_cWidthDetails = r.right - r.left;
|
|
g_cHeightDetails = r.bottom - r.top;
|
|
|
|
InitListViewDetails(hDlg);
|
|
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
case WM_SIZE:
|
|
HandleSizingDetails(hDlg);
|
|
break;
|
|
|
|
case WM_GETMINMAXINFO:
|
|
{
|
|
MINMAXINFO* pmmi = (MINMAXINFO*)lParam;
|
|
|
|
pmmi->ptMinTrackSize.y = 300;
|
|
|
|
return 0;
|
|
break;
|
|
}
|
|
|
|
case WM_NOTIFY:
|
|
if (wParam == IDC_LIST_DETAILS) {
|
|
|
|
LPNMHDR pnm = (LPNMHDR)lParam;
|
|
|
|
if (g_hwndIssues == NULL) {
|
|
break;
|
|
}
|
|
|
|
switch (pnm->code) {
|
|
case LVN_COLUMNCLICK:
|
|
{
|
|
LPNMLISTVIEW pnmv = (LPNMLISTVIEW) lParam;
|
|
|
|
ListView_SortItemsEx(g_hwndDetails, DetailsCompareFunc, pnmv->iSubItem);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
switch (LOWORD (wParam)) {
|
|
|
|
case IDOK:
|
|
case IDCANCEL:
|
|
EndDialog(hDlg, LOWORD(wParam));
|
|
return TRUE;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|