* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*
- * COPYRIGHT : See COPYING in the top level directory
- * PROJECT : Event Log Viewer
- * FILE : eventvwr.c
- * PROGRAMMER: Marc Piulachs (marc.piulachs at codexchange [dot] net)
+ * PROJECT: ReactOS Event Log Viewer
+ * LICENSE: GPL - See COPYING in the top level directory
+ * FILE: base/applications/mscutils/eventvwr/eventvwr.c
+ * PURPOSE: Event Log Viewer main file
+ * PROGRAMMERS: Marc Piulachs (marc.piulachs at codexchange [dot] net)
+ * Eric Kohl
+ * Hermes Belusca-Maito
*/
#include "eventvwr.h"
-#include <windows.h>
-#include <commctrl.h>
-#include <stdio.h>
-#include <time.h>
-
-#if _MSC_VER
- #pragma warning(disable: 4996) /* 'strdup' was declared deprecated */
- #define _CRT_SECURE_NO_DEPRECATE /* all deprecated unsafe string functions */
-#endif
+#include "evtdetctl.h"
+
+#include <sddl.h> // For ConvertSidToStringSidW
+#include <shellapi.h>
+#include <shlwapi.h>
+
+// #include "resource.h"
+
+#define LVM_PROGRESS (WM_APP + 1) // Used by the subclassed ListView
-static const LPWSTR EVENT_SOURCE_APPLICATION = L"Application";
-static const LPWSTR EVENT_SOURCE_SECURITY = L"Security";
-static const LPWSTR EVENT_SOURCE_SYSTEM = L"System";
-static const WCHAR szWindowClass[] = L"EVENTVWR"; /* the main window class name*/
+static const LPCWSTR EVENTVWR_WNDCLASS = L"EVENTVWR"; /* The main window class name */
+static const LPCWSTR EVENTLOG_BASE_KEY = L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\";
+static const LPCWSTR EVNTVWR_PARAM_KEY = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Event Viewer";
-//MessageFile message buffer size
-#define EVENT_MESSAGE_EVENTTEXT_BUFFER 1024*10
+/* The 3 system logs that should always exist in the user's system */
+static const LPCWSTR SystemLogs[] =
+{
+ L"Application",
+ L"Security",
+ L"System"
+};
+
+/* MessageFile message buffer size */
+#define EVENT_MESSAGE_EVENTTEXT_BUFFER 1024*10 // NOTE: Used by evtdetctl.c
#define EVENT_MESSAGE_FILE_BUFFER 1024*10
#define EVENT_DLL_SEPARATOR L";"
-#define EVENT_MESSAGE_FILE L"EventMessageFile"
#define EVENT_CATEGORY_MESSAGE_FILE L"CategoryMessageFile"
+#define EVENT_MESSAGE_FILE L"EventMessageFile"
#define EVENT_PARAMETER_MESSAGE_FILE L"ParameterMessageFile"
#define MAX_LOADSTRING 255
+#define SPLIT_WIDTH 4
+
/* Globals */
-HINSTANCE hInst; /* current instance */
-WCHAR szTitle[MAX_LOADSTRING]; /* The title bar text */
-HWND hwndMainWindow; /* Main window */
-HWND hwndListView; /* ListView control */
-HWND hwndStatus; /* Status bar */
-PEVENTLOGRECORD *g_RecordPtrs = NULL;
+HINSTANCE hInst; /* Current instance */
+WCHAR szTitle[MAX_LOADSTRING]; /* The title bar text */
+WCHAR szTitleTemplate[MAX_LOADSTRING]; /* The logged-on title bar text */
+WCHAR szStatusBarTemplate[MAX_LOADSTRING]; /* The status bar text */
+WCHAR szLoadingWait[MAX_LOADSTRING]; /* The "Loading, please wait..." text */
+WCHAR szEmptyList[MAX_LOADSTRING]; /* The "There are no items to show in this view" text */
+WCHAR szSaveFilter[MAX_LOADSTRING]; /* Filter Mask for the save Dialog */
+
+INT nVSplitPos; /* Vertical splitter (1) position */
+INT nHSplitPos; /* Horizontal splitter (2) position */
+BYTE bSplit = 0; /* Splitter state:
+ * 0: No splitting;
+ * 1: Vertical splitting;
+ * 2: Horizontal splitting.
+ */
+
+HWND hwndMainWindow = NULL; /* Main window */
+HWND hwndTreeView; /* TreeView control */
+HWND hwndListView; /* ListView control */ // NOTE: Used by evtdetctl.c
+HWND hwndEventDetails; /* Event details pane */
+HWND hwndStatus; /* Status bar */
+HWND hwndStatusProgress; /* Progress bar in the status bar */
+HMENU hMainMenu; /* The application's main menu */
+
+HTREEITEM htiSystemLogs = NULL, htiAppLogs = NULL, htiUserLogs = NULL;
+
+LPWSTR lpComputerName = NULL; /* NULL: local user computer (default) */
+LPWSTR lpszzUserLogsToLoad = NULL; /* The list of user logs to load at startup (multi-string) */
+SIZE_T cbUserLogsSize = 0;
+
+HKEY hkMachine = NULL; // Registry handle to the HKEY_LOCAL_MACHINE key of the remote computer registry.
+
+/* Global event records cache for the current active event log filter */
DWORD g_TotalRecords = 0;
+PEVENTLOGRECORD* g_RecordPtrs = NULL;
-LPWSTR lpSourceLogName = NULL;
-LPWSTR lpComputerName = NULL;
+/* Lists of event logs and event log filters */
+LIST_ENTRY EventLogList;
+LIST_ENTRY EventLogFilterList;
+PEVENTLOGFILTER ActiveFilter = NULL;
-/* Forward declarations of functions included in this code module: */
-ATOM MyRegisterClass(HINSTANCE hInstance);
-BOOL InitInstance(HINSTANCE, int);
-LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
-INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
-INT_PTR CALLBACK EventDetails(HWND, UINT, WPARAM, LPARAM);
-static INT_PTR CALLBACK StatusMessageWindowProc (HWND, UINT, WPARAM, LPARAM);
+HANDLE hEnumEventsThread = NULL;
+HANDLE hStopEnumEvent = NULL;
+
+/*
+ * Setting EnumFilter to a valid pointer and raising the hStartEnumEvent event
+ * triggers the event-enumerator thread to perform a new enumeration.
+ */
+PEVENTLOGFILTER EnumFilter = NULL;
+HANDLE hStartStopEnumEvent = NULL; // End-of-application event
+HANDLE hStartEnumEvent = NULL; // Command event
+/* Default Open/Save-As dialog box */
+OPENFILENAMEW sfn;
-int APIENTRY
-wWinMain(HINSTANCE hInstance,
- HINSTANCE hPrevInstance,
- LPWSTR lpCmdLine,
- int nCmdShow)
+
+/* Event Viewer Application Settings */
+typedef struct _SETTINGS
{
- MSG msg;
- HACCEL hAccelTable;
- INITCOMMONCONTROLSEX iccx;
+ BOOL bShowDetailsPane; /* Show (TRUE) or Hide (FALSE) the events details pane */
+ BOOL bShowGrid; /* Show (TRUE) or Hide (FALSE) the events view grid */
+ BOOL bSaveSettings; /* Save (TRUE) or do not save (FALSE) current settings on exit */
+ BOOL bNewestEventsFirst; /* Sort the displayed events the newest ones first (TRUE) or last (FALSE) */
+ INT nVSplitPos; /* Vertical splitter position */
+ INT nHSplitPos; /* Horizontal splitter position */
+ WINDOWPLACEMENT wpPos;
+} SETTINGS, *PSETTINGS;
- UNREFERENCED_PARAMETER(hPrevInstance);
- UNREFERENCED_PARAMETER(lpCmdLine);
+SETTINGS Settings;
- /* Whenever any of the common controls are used in your app,
- * you must call InitCommonControlsEx() to register the classes
- * for those controls. */
- iccx.dwSize = sizeof(INITCOMMONCONTROLSEX);
- iccx.dwICC = ICC_LISTVIEW_CLASSES;
- InitCommonControlsEx(&iccx);
- /* Initialize global strings */
- LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
- MyRegisterClass(hInstance);
+/* Forward declarations of functions included in this code module */
- /* Perform application initialization: */
- if (!InitInstance(hInstance, nCmdShow))
- {
- return FALSE;
- }
+static DWORD WINAPI
+StartStopEnumEventsThread(IN LPVOID lpParameter);
- hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_EVENTVWR));
+VOID OpenUserEventLogFile(IN LPCWSTR lpszFileName);
- /* Main message loop: */
- while (GetMessageW(&msg, NULL, 0, 0))
- {
- if (!TranslateAcceleratorW(msg.hwnd, hAccelTable, &msg))
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- }
+VOID BuildLogListAndFilterList(IN LPCWSTR lpComputerName);
+VOID FreeLogList(VOID);
+VOID FreeLogFilterList(VOID);
- return (int)msg.wParam;
-}
+ATOM MyRegisterClass(HINSTANCE);
+BOOL InitInstance(HINSTANCE);
+LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
+INT_PTR EventLogProperties(HINSTANCE, HWND, PEVENTLOGFILTER);
+INT_PTR CALLBACK EventDetails(HWND, UINT, WPARAM, LPARAM);
+
+
+/* MAIN FUNCTIONS *************************************************************/
-static void FreeRecords(void)
+VOID
+ShowWin32Error(IN DWORD dwError)
{
- DWORD iIndex;
+ LPWSTR lpMessageBuffer;
- if (!g_RecordPtrs)
+ if (dwError == ERROR_SUCCESS)
return;
- for (iIndex = 0; iIndex < g_TotalRecords; iIndex++)
- HeapFree(GetProcessHeap(), 0, g_RecordPtrs[iIndex]);
- HeapFree(GetProcessHeap(), 0, g_RecordPtrs);
- g_RecordPtrs = NULL;
+ if (!FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ dwError,
+ LANG_USER_DEFAULT,
+ (LPWSTR)&lpMessageBuffer,
+ 0, NULL))
+ {
+ return;
+ }
+
+ MessageBoxW(hwndMainWindow, lpMessageBuffer, szTitle, MB_OK | MB_ICONERROR);
+ LocalFree(lpMessageBuffer);
}
VOID
-EventTimeToSystemTime(DWORD EventTime,
- SYSTEMTIME *pSystemTime)
+DisplayUsage(VOID)
{
- SYSTEMTIME st1970 = { 1970, 1, 0, 1, 0, 0, 0, 0 };
- FILETIME ftLocal;
- union
- {
- FILETIME ft;
- ULONGLONG ll;
- } u1970, uUCT;
+ LPWSTR lpBuffer;
+ LPCWSTR lpUsage;
+ INT iUsageLen = LoadStringW(hInst, IDS_USAGE, (LPWSTR)&lpUsage, 0);
- uUCT.ft.dwHighDateTime = 0;
- uUCT.ft.dwLowDateTime = EventTime;
- SystemTimeToFileTime(&st1970, &u1970.ft);
- uUCT.ll = uUCT.ll * 10000000 + u1970.ll;
- FileTimeToLocalFileTime(&uUCT.ft, &ftLocal);
- FileTimeToSystemTime(&ftLocal, pSystemTime);
-}
+ if (iUsageLen == 0)
+ return;
+ lpBuffer = HeapAlloc(GetProcessHeap(), 0, (iUsageLen + 1) * sizeof(WCHAR));
+ if (!lpBuffer)
+ return;
-void
-TrimNulls(LPWSTR s)
-{
- WCHAR *c;
+ StringCchCopyNW(lpBuffer, iUsageLen + 1, lpUsage, iUsageLen);
+ MessageBoxW(NULL, lpBuffer, szTitle, MB_OK | MB_ICONINFORMATION);
- if (s != NULL)
- {
- c = s + wcslen(s) - 1;
- while (c >= s && iswspace(*c))
- --c;
- *++c = L'\0';
- }
+ HeapFree(GetProcessHeap(), 0, lpBuffer);
}
-
BOOL
-GetEventMessageFileDLL(IN LPCWSTR lpLogName,
- IN LPCWSTR SourceName,
- IN LPCWSTR EntryName,
- OUT LPWSTR ExpandedName)
+ProcessCmdLine(IN LPWSTR lpCmdLine)
{
- DWORD dwSize;
- BYTE szModuleName[MAX_PATH];
- WCHAR szKeyName[MAX_PATH];
- HKEY hAppKey = NULL;
- HKEY hSourceKey = NULL;
- BOOL bReturn = FALSE;
-
- wcscpy(szKeyName, L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\");
- wcscat(szKeyName, lpLogName);
-
- if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
- szKeyName,
- 0,
- KEY_READ,
- &hAppKey) == ERROR_SUCCESS)
- {
- if (RegOpenKeyExW(hAppKey,
- SourceName,
- 0,
- KEY_READ,
- &hSourceKey) == ERROR_SUCCESS)
- {
- dwSize = MAX_PATH;
- if (RegQueryValueExW(hSourceKey,
- EntryName,
- NULL,
- NULL,
- (LPBYTE)szModuleName,
- &dwSize) == ERROR_SUCCESS)
- {
- /* Returns a string containing the requested substituted environment variable. */
- ExpandEnvironmentStringsW((LPCWSTR)szModuleName, ExpandedName, MAX_PATH);
+ BOOL Success = FALSE;
+ INT i, argc;
+ LPWSTR* argv;
- /* Successful */
- bReturn = TRUE;
- }
- }
- }
- else
+ /* Skip any leading whitespace */
+ if (lpCmdLine)
{
- MessageBoxW(NULL,
- L"Registry access failed!",
- L"Event Log",
- MB_OK | MB_ICONINFORMATION);
+ while (iswspace(*lpCmdLine))
+ ++lpCmdLine;
}
- if (hSourceKey != NULL)
- RegCloseKey(hSourceKey);
-
- if (hAppKey != NULL)
- RegCloseKey(hAppKey);
-
- return bReturn;
-}
-
+ /* No command line means no processing needed */
+ if (!lpCmdLine || !*lpCmdLine)
+ return TRUE;
-BOOL
-GetEventCategory(IN LPCWSTR KeyName,
- IN LPCWSTR SourceName,
- IN EVENTLOGRECORD *pevlr,
- OUT LPWSTR CategoryName)
-{
- HANDLE hLibrary = NULL;
- WCHAR szMessageDLL[MAX_PATH];
- LPVOID lpMsgBuf = NULL;
+ /* Build the arguments vector */
+ argv = CommandLineToArgvW(lpCmdLine, &argc);
+ if (!argv)
+ return FALSE;
- if (GetEventMessageFileDLL (KeyName, SourceName, EVENT_CATEGORY_MESSAGE_FILE , szMessageDLL))
+ /* Parse the command line for options (skip the program name) */
+ for (i = 1; i < argc; ++i)
{
- hLibrary = LoadLibraryExW(szMessageDLL,
- NULL,
- DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE);
- if (hLibrary != NULL)
+ /* Check for new options */
+ if (argv[i][0] == L'-' || argv[i][0] == L'/')
{
- /* Retrieve the message string. */
- if (FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
- hLibrary,
- pevlr->EventCategory,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPWSTR)&lpMsgBuf,
- EVENT_MESSAGE_FILE_BUFFER,
- NULL) != 0)
+ if (argv[i][1] == L'?' && argv[i][2] == 0)
{
- if (lpMsgBuf)
- {
- /* Trim the string */
- TrimNulls((LPWSTR)lpMsgBuf);
-
- /* Copy the category name */
- wcscpy(CategoryName, (LPCWSTR)lpMsgBuf);
- }
- else
- {
- wcscpy(CategoryName, (LPCWSTR)lpMsgBuf);
- }
+ /* Display help */
+ DisplayUsage();
+ goto Quit;
}
else
+ if (argv[i][2] == L':')
{
- LoadStringW(hInst, IDS_NONE, CategoryName, MAX_PATH);
- }
-
- if (hLibrary != NULL)
- FreeLibrary(hLibrary);
+ switch (towupper(argv[i][1]))
+ {
+ case L'L':
+ {
+ LPWSTR lpNewBuffer;
+ LPWSTR lpFileName = argv[i] + 3;
+ SIZE_T cbFileName;
- /* Free the buffer allocated by FormatMessage */
- if (lpMsgBuf)
- LocalFree(lpMsgBuf);
+ /* Check for a quoted file name */
+ if (*lpFileName == L'\"')
+ {
+ /* Skip this quote, and the last one too if any */
+ ++lpFileName;
+ cbFileName = wcslen(lpFileName);
+ if (cbFileName > 0 && lpFileName[cbFileName - 1] == L'\"')
+ lpFileName[cbFileName - 1] = UNICODE_NULL;
+ }
- return TRUE;
- }
- }
+ /* Skip this one if we do not actually have a file name */
+ if (!*lpFileName)
+ continue;
- LoadStringW(hInst, IDS_NONE, CategoryName, MAX_PATH);
+ cbFileName = (wcslen(lpFileName) + 1) * sizeof(WCHAR);
- return FALSE;
-}
+ /* Reallocate the list of user logs to load */
+ if (lpszzUserLogsToLoad)
+ {
+ lpNewBuffer = HeapReAlloc(GetProcessHeap(),
+ HEAP_ZERO_MEMORY,
+ lpszzUserLogsToLoad,
+ /* Count the multi-string NULL-terminator */
+ cbUserLogsSize + cbFileName + sizeof(WCHAR));
+ }
+ else
+ {
+ cbUserLogsSize = 0;
+ lpNewBuffer = HeapAlloc(GetProcessHeap(),
+ HEAP_ZERO_MEMORY,
+ /* Count the multi-string NULL-terminator */
+ cbUserLogsSize + cbFileName + sizeof(WCHAR));
+ }
+ if (!lpNewBuffer)
+ {
+ ShowWin32Error(ERROR_NOT_ENOUGH_MEMORY);
+ goto Quit;
+ }
-BOOL
-GetEventMessage(IN LPCWSTR KeyName,
- IN LPCWSTR SourceName,
- IN EVENTLOGRECORD *pevlr,
- OUT LPWSTR EventText)
-{
- DWORD i;
- HANDLE hLibrary = NULL;
- WCHAR SourceModuleName[1000];
- WCHAR ParameterModuleName[1000];
- LPWSTR lpMsgBuf = NULL;
- WCHAR szStringIDNotFound[MAX_LOADSTRING];
- LPWSTR szDll;
- LPWSTR szMessage;
- LPWSTR *szArguments;
- BOOL bDone = FALSE;
+ lpszzUserLogsToLoad = lpNewBuffer;
+ lpNewBuffer = (LPWSTR)((ULONG_PTR)lpNewBuffer + cbUserLogsSize);
+ cbUserLogsSize += cbFileName;
- /* TODO : GetEventMessageFileDLL can return a comma separated list of DLLs */
- if (GetEventMessageFileDLL (KeyName, SourceName, EVENT_MESSAGE_FILE, SourceModuleName))
- {
- /* Get the event message */
- szMessage = (LPWSTR)((LPBYTE)pevlr + pevlr->StringOffset);
+ /* Save the file name */
+ StringCbCopyW(lpNewBuffer, cbFileName, lpFileName);
- /* Allocate space for parameters */
- szArguments = malloc(sizeof(LPVOID) * pevlr->NumStrings);
- if (!szArguments)
- {
- return FALSE;
- }
+ continue;
+ }
- for (i = 0; i < pevlr->NumStrings ; i++)
- {
- if (wcsstr(szMessage , L"%%"))
- {
- if (GetEventMessageFileDLL(KeyName, SourceName, EVENT_PARAMETER_MESSAGE_FILE, ParameterModuleName))
- {
- /* Not yet support for reading messages from parameter message DLL */
+ default:
+ break;
}
}
- szArguments[i] = szMessage;
- szMessage += wcslen(szMessage) + 1;
+ /* Unknown argument: display help and bail out */
+ DisplayUsage();
+ goto Quit;
}
-
- szDll = wcstok(SourceModuleName, EVENT_DLL_SEPARATOR);
- while ((szDll != NULL) && (!bDone))
+ else
{
- hLibrary = LoadLibraryExW(szDll,
- NULL,
- DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE);
- if (hLibrary == NULL)
- {
- /* The DLL could not be loaded try the next one (if any) */
- szDll = wcstok(NULL, EVENT_DLL_SEPARATOR);
- }
- else
+ /*
+ * An argument that does not start with the switch character.
+ * If this is the first argument then this corresponds to the
+ * optional computer name. Otherwise this is a wrong argument.
+ */
+ if (i == 1)
{
- /* Retrieve the message string. */
- if (FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_ALLOCATE_BUFFER |
- FORMAT_MESSAGE_FROM_HMODULE |
- FORMAT_MESSAGE_ARGUMENT_ARRAY,
- hLibrary,
- pevlr->EventID,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPWSTR)&lpMsgBuf,
- 0,
- (va_list*)szArguments) == 0)
- {
- /* We haven't found the string , get next DLL (if any) */
- szDll = wcstok(NULL, EVENT_DLL_SEPARATOR);
- }
- else
- {
- if (lpMsgBuf)
- {
- /* The ID was found and the message was formated */
- bDone = TRUE;
+ /* Store the computer name */
+ LPWSTR lpTemp = argv[i];
+ SIZE_T cbLength;
- /* Trim the string */
- TrimNulls((LPWSTR)lpMsgBuf);
+ /* Strip any leading backslashes */
+ while (*lpTemp == L'\\')
+ ++lpTemp;
- /* Copy the event text */
- wcscpy(EventText ,lpMsgBuf);
- }
+ cbLength = (wcslen(lpTemp) + 1) * sizeof(WCHAR);
+ lpComputerName = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbLength);
+ if (lpComputerName)
+ {
+ StringCbCopyW(lpComputerName, cbLength, lpTemp);
}
-
- FreeLibrary(hLibrary);
+ /* else, fall back to local computer */
+ }
+ else
+ {
+ /* Invalid syntax: display help and bail out */
+ DisplayUsage();
+ goto Quit;
}
}
+ }
+
+ Success = TRUE;
- if (!bDone)
+Quit:
+ /* In case of failure, free anything we have allocated */
+ if (!Success)
+ {
+ if (lpszzUserLogsToLoad)
{
- LoadStringW(hInst, IDS_EVENTSTRINGIDNOTFOUND, szStringIDNotFound, MAX_LOADSTRING);
- swprintf(EventText, szStringIDNotFound, (pevlr->EventID & 0xFFFF), SourceName);
+ cbUserLogsSize = 0;
+ HeapFree(GetProcessHeap(), 0, lpszzUserLogsToLoad);
+ lpszzUserLogsToLoad = NULL;
+ }
+ if (lpComputerName)
+ {
+ HeapFree(GetProcessHeap(), 0, lpComputerName);
+ lpComputerName = NULL;
}
-
- free(szArguments);
-
- /* No more dlls to try, return result */
- return bDone;
}
- LoadStringW(hInst, IDS_EVENTSTRINGIDNOTFOUND, szStringIDNotFound, MAX_LOADSTRING);
- swprintf(EventText, szStringIDNotFound, (pevlr->EventID & 0xFFFF), SourceName);
-
- return FALSE;
+ /* Free the arguments vector and exit */
+ LocalFree(argv);
+ return Success;
}
-
-VOID
-GetEventType(IN WORD dwEventType,
- OUT LPWSTR eventTypeText)
+BOOL
+LoadSettings(int nDefCmdShow)
{
- switch (dwEventType)
+ HKEY hKeyEventVwr;
+ LONG Result;
+ DWORD dwSize;
+ DWORD dwType;
+ DWORD Value;
+ UNICODE_STRING ValueU;
+ WCHAR buffer[100];
+
+ /* Load the default values */
+ Settings.bShowDetailsPane = TRUE;
+ Settings.bShowGrid = FALSE;
+ Settings.bSaveSettings = TRUE;
+ Settings.bNewestEventsFirst = TRUE;
+ Settings.nVSplitPos = 250; /* Splitter default positions */
+ Settings.nHSplitPos = 250;
+ ZeroMemory(&Settings.wpPos, sizeof(Settings.wpPos));
+ Settings.wpPos.length = sizeof(Settings.wpPos);
+ Settings.wpPos.rcNormalPosition.left = CW_USEDEFAULT;
+ Settings.wpPos.rcNormalPosition.top = CW_USEDEFAULT;
+ Settings.wpPos.rcNormalPosition.right = CW_USEDEFAULT;
+ Settings.wpPos.rcNormalPosition.bottom = CW_USEDEFAULT;
+
+ /* Try to create/open the Event Viewer user key */
+ if (RegCreateKeyExW(HKEY_CURRENT_USER,
+ EVNTVWR_PARAM_KEY,
+ 0,
+ NULL,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ | KEY_WRITE,
+ NULL,
+ &hKeyEventVwr,
+ NULL) != ERROR_SUCCESS)
{
- case EVENTLOG_ERROR_TYPE:
- LoadStringW(hInst, IDS_EVENTLOG_ERROR_TYPE, eventTypeText, MAX_LOADSTRING);
- break;
- case EVENTLOG_WARNING_TYPE:
- LoadStringW(hInst, IDS_EVENTLOG_WARNING_TYPE, eventTypeText, MAX_LOADSTRING);
- break;
- case EVENTLOG_INFORMATION_TYPE:
- LoadStringW(hInst, IDS_EVENTLOG_INFORMATION_TYPE, eventTypeText, MAX_LOADSTRING);
- break;
- case EVENTLOG_AUDIT_SUCCESS:
- LoadStringW(hInst, IDS_EVENTLOG_AUDIT_SUCCESS, eventTypeText, MAX_LOADSTRING);
- break;
- case EVENTLOG_AUDIT_FAILURE:
- LoadStringW(hInst, IDS_EVENTLOG_AUDIT_FAILURE, eventTypeText, MAX_LOADSTRING);
- break;
- case EVENTLOG_SUCCESS:
- LoadStringW(hInst, IDS_EVENTLOG_SUCCESS, eventTypeText, MAX_LOADSTRING);
- break;
- default:
- LoadStringW(hInst, IDS_EVENTLOG_UNKNOWN_TYPE, eventTypeText, MAX_LOADSTRING);
- break;
+ return FALSE;
}
-}
-
-BOOL
-GetEventUserName(EVENTLOGRECORD *pelr,
- OUT LPWSTR pszUser)
-{
- PSID lpSid;
- WCHAR szName[1024];
- WCHAR szDomain[1024];
- SID_NAME_USE peUse;
- DWORD cbName = 1024;
- DWORD cbDomain = 1024;
- /* Point to the SID. */
- lpSid = (PSID)((LPBYTE)pelr + pelr->UserSidOffset);
+ // Result = RegQueryValueExW(hKeyEventVwr, L"Filter", NULL, &dwType, (LPBYTE)&szFilter, &dwSize); // REG_SZ
+ // Result = RegQueryValueExW(hKeyEventVwr, L"Find", NULL, &dwType, (LPBYTE)&szFind, &dwSize); // REG_SZ
+ // Result = RegQueryValueExW(hKeyEventVwr, L"Module", NULL, &dwType, (LPBYTE)&szModule, &dwSize); // REG_SZ
- /* User SID */
- if (pelr->UserSidLength > 0)
+ dwSize = sizeof(Value);
+ Result = RegQueryValueExW(hKeyEventVwr, L"DetailsPane", NULL, &dwType, (LPBYTE)&Value, &dwSize);
+ if ((Result == ERROR_SUCCESS) && (dwType == REG_SZ || dwType == REG_DWORD))
{
- if (LookupAccountSidW(NULL,
- lpSid,
- szName,
- &cbName,
- szDomain,
- &cbDomain,
- &peUse))
+ if (dwType == REG_SZ)
{
- wcscpy(pszUser, szName);
- return TRUE;
+ ValueU.Buffer = (PWSTR)&Value;
+ ValueU.Length = ValueU.MaximumLength = dwSize;
+ RtlUnicodeStringToInteger(&ValueU, 10, &Value);
}
+ Settings.bShowDetailsPane = !!Value;
}
- return FALSE;
-}
+ dwSize = sizeof(Value);
+ Result = RegQueryValueExW(hKeyEventVwr, L"ShowGrid", NULL, &dwType, (LPBYTE)&Value, &dwSize);
+ if ((Result == ERROR_SUCCESS) && (dwType == REG_SZ || dwType == REG_DWORD))
+ {
+ if (dwType == REG_SZ)
+ {
+ ValueU.Buffer = (PWSTR)&Value;
+ ValueU.Length = ValueU.MaximumLength = dwSize;
+ RtlUnicodeStringToInteger(&ValueU, 10, &Value);
+ }
+ Settings.bShowGrid = !!Value;
+ }
+ dwSize = sizeof(Value);
+ Result = RegQueryValueExW(hKeyEventVwr, L"SortOrder", NULL, &dwType, (LPBYTE)&Value, &dwSize);
+ if ((Result == ERROR_SUCCESS) && (dwType == REG_SZ || dwType == REG_DWORD))
+ {
+ if (dwType == REG_SZ)
+ {
+ ValueU.Buffer = (PWSTR)&Value;
+ ValueU.Length = ValueU.MaximumLength = dwSize;
+ RtlUnicodeStringToInteger(&ValueU, 10, &Value);
+ }
+ Settings.bNewestEventsFirst = !!Value;
+ }
-static DWORD WINAPI
-ShowStatusMessageThread(IN LPVOID lpParameter)
-{
- HWND *phWnd = (HWND *)lpParameter;
- HWND hWnd;
- MSG Msg;
-
- hWnd = CreateDialogParam(hInst,
- MAKEINTRESOURCE(IDD_PROGRESSBOX),
- GetDesktopWindow(),
- StatusMessageWindowProc,
- (LPARAM)NULL);
- if (!hWnd)
- return 0;
+ /* Retrieve the splitter positions */
+ dwSize = sizeof(Value);
+ Result = RegQueryValueExW(hKeyEventVwr, L"VSplitPos", NULL, &dwType, (LPBYTE)&Value, &dwSize);
+ if ((Result == ERROR_SUCCESS) && (dwType == REG_SZ || dwType == REG_DWORD))
+ {
+ if (dwType == REG_SZ)
+ {
+ ValueU.Buffer = (PWSTR)&Value;
+ ValueU.Length = ValueU.MaximumLength = dwSize;
+ RtlUnicodeStringToInteger(&ValueU, 10, &Value);
+ }
+ Settings.nVSplitPos = Value;
+ }
- *phWnd = hWnd;
+ dwSize = sizeof(Value);
+ Result = RegQueryValueExW(hKeyEventVwr, L"HSplitPos", NULL, &dwType, (LPBYTE)&Value, &dwSize);
+ if ((Result == ERROR_SUCCESS) && (dwType == REG_SZ || dwType == REG_DWORD))
+ {
+ if (dwType == REG_SZ)
+ {
+ ValueU.Buffer = (PWSTR)&Value;
+ ValueU.Length = ValueU.MaximumLength = dwSize;
+ RtlUnicodeStringToInteger(&ValueU, 10, &Value);
+ }
+ Settings.nHSplitPos = Value;
+ }
- ShowWindow(hWnd, SW_SHOW);
+ /* Retrieve the geometry of the main window */
+ dwSize = sizeof(buffer);
+ Result = RegQueryValueExW(hKeyEventVwr, L"Window", NULL, &dwType, (LPBYTE)buffer, &dwSize);
+ if ((Result != ERROR_SUCCESS) || (dwType != REG_SZ))
+ buffer[0] = UNICODE_NULL;
+
+ if (swscanf(buffer, L"%d %d %d %d %d",
+ &Settings.wpPos.rcNormalPosition.left,
+ &Settings.wpPos.rcNormalPosition.top,
+ &Settings.wpPos.rcNormalPosition.right,
+ &Settings.wpPos.rcNormalPosition.bottom,
+ &Settings.wpPos.showCmd) != 5)
+ {
+ /* Parsing failed, use defaults */
+ Settings.wpPos.rcNormalPosition.left = CW_USEDEFAULT;
+ Settings.wpPos.rcNormalPosition.top = CW_USEDEFAULT;
+ Settings.wpPos.rcNormalPosition.right = CW_USEDEFAULT;
+ Settings.wpPos.rcNormalPosition.bottom = CW_USEDEFAULT;
+ Settings.wpPos.showCmd = nDefCmdShow; // SW_SHOWNORMAL;
+ }
- /* Message loop for the Status window */
- while (GetMessage(&Msg, NULL, 0, 0))
+ dwSize = sizeof(Value);
+ Result = RegQueryValueExW(hKeyEventVwr, L"SaveSettings", NULL, &dwType, (LPBYTE)&Value, &dwSize);
+ if ((Result == ERROR_SUCCESS) && (dwType == REG_SZ || dwType == REG_DWORD))
{
- TranslateMessage(&Msg);
- DispatchMessage(&Msg);
+ if (dwType == REG_SZ)
+ {
+ ValueU.Buffer = (PWSTR)&Value;
+ ValueU.Length = ValueU.MaximumLength = dwSize;
+ RtlUnicodeStringToInteger(&ValueU, 10, &Value);
+ }
+ Settings.bSaveSettings = !!Value;
}
- return 0;
+ RegCloseKey(hKeyEventVwr);
+ return TRUE;
}
-
BOOL
-QueryEventMessages(LPWSTR lpMachineName,
- LPWSTR lpLogName)
+SaveSettings(VOID)
{
- HWND hwndDlg;
- HANDLE hEventLog;
- EVENTLOGRECORD *pevlr;
- DWORD dwRead, dwNeeded, dwThisRecord, dwTotalRecords = 0, dwCurrentRecord = 0, dwRecordsToRead = 0, dwFlags, dwMaxLength;
- LPWSTR lpSourceName;
- LPWSTR lpComputerName;
- LPSTR lpData;
- BOOL bResult = TRUE; /* Read succeeded. */
- int i;
-
- WCHAR szWindowTitle[MAX_PATH];
- WCHAR szStatusText[MAX_PATH];
- WCHAR szLocalDate[MAX_PATH];
- WCHAR szLocalTime[MAX_PATH];
- WCHAR szEventID[MAX_PATH];
- WCHAR szEventTypeText[MAX_PATH];
- WCHAR szCategoryID[MAX_PATH];
- WCHAR szUsername[MAX_PATH];
- WCHAR szEventText[EVENT_MESSAGE_FILE_BUFFER];
- WCHAR szCategory[MAX_PATH];
- WCHAR szData[MAX_PATH];
-
- SYSTEMTIME time;
- LVITEMW lviEventItem;
-
- dwFlags = EVENTLOG_FORWARDS_READ | EVENTLOG_SEQUENTIAL_READ;
-
- lpSourceLogName = lpLogName;
- lpComputerName = lpMachineName;
-
- /* Open the event log. */
- hEventLog = OpenEventLogW(lpMachineName,
- lpLogName);
- if (hEventLog == NULL)
+ HKEY hKeyEventVwr;
+ DWORD dwSize;
+ WCHAR buffer[100];
+
+ /* Try to create/open the Event Viewer user key */
+ if (RegCreateKeyExW(HKEY_CURRENT_USER,
+ EVNTVWR_PARAM_KEY,
+ 0,
+ NULL,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ | KEY_WRITE,
+ NULL,
+ &hKeyEventVwr,
+ NULL) != ERROR_SUCCESS)
{
- MessageBoxW(NULL,
- L"Could not open the event log.",
- L"Event Log",
- MB_OK | MB_ICONINFORMATION);
return FALSE;
}
- /* Disable listview redraw */
- SendMessage(hwndListView, WM_SETREDRAW, FALSE, 0);
+ dwSize = sizeof(Settings.bSaveSettings);
+ RegSetValueExW(hKeyEventVwr, L"SaveSettings", 0, REG_DWORD, (LPBYTE)&Settings.bSaveSettings, dwSize);
- /* Clear the list view */
- (void)ListView_DeleteAllItems (hwndListView);
- FreeRecords();
+ /* Do not save more settings if we are not asked to do so */
+ if (!Settings.bSaveSettings)
+ goto Quit;
- GetOldestEventLogRecord(hEventLog, &dwThisRecord);
+ dwSize = sizeof(Settings.bShowDetailsPane);
+ RegSetValueExW(hKeyEventVwr, L"DetailsPane", 0, REG_DWORD, (LPBYTE)&Settings.bShowDetailsPane, dwSize);
- /* Get the total number of event log records. */
- GetNumberOfEventLogRecords (hEventLog , &dwTotalRecords);
- g_TotalRecords = dwTotalRecords;
+ dwSize = sizeof(Settings.bShowGrid);
+ RegSetValueExW(hKeyEventVwr, L"ShowGrid", 0, REG_DWORD, (LPBYTE)&Settings.bShowGrid, dwSize);
- g_RecordPtrs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwTotalRecords * sizeof(PVOID));
+ dwSize = sizeof(Settings.bNewestEventsFirst);
+ RegSetValueExW(hKeyEventVwr, L"SortOrder", 0, REG_DWORD, (LPBYTE)&Settings.bNewestEventsFirst, dwSize);
- /* If we have at least 1000 records show the waiting dialog */
- if (dwTotalRecords > 1000)
- {
- CloseHandle(CreateThread(NULL,
- 0,
- ShowStatusMessageThread,
- (LPVOID)&hwndDlg,
- 0,
- NULL));
- }
+ Settings.nVSplitPos = nVSplitPos;
+ dwSize = sizeof(Settings.nVSplitPos);
+ RegSetValueExW(hKeyEventVwr, L"VSplitPos", 0, REG_DWORD, (LPBYTE)&Settings.nVSplitPos, dwSize);
- while (dwCurrentRecord < dwTotalRecords)
- {
- pevlr = HeapAlloc(GetProcessHeap(), 0, sizeof(EVENTLOGRECORD) * dwTotalRecords);
- g_RecordPtrs[dwCurrentRecord] = pevlr;
+ Settings.nHSplitPos = nHSplitPos;
+ dwSize = sizeof(Settings.nHSplitPos);
+ RegSetValueExW(hKeyEventVwr, L"HSplitPos", 0, REG_DWORD, (LPBYTE)&Settings.nHSplitPos, dwSize);
- bResult = ReadEventLog(hEventLog, // Event log handle
- dwFlags, // Sequential read
- 0, // Ignored for sequential read
- pevlr, // Pointer to buffer
- sizeof(EVENTLOGRECORD), // Size of buffer
- &dwRead, // Number of bytes read
- &dwNeeded); // Bytes in the next record
- if((!bResult) && (GetLastError () == ERROR_INSUFFICIENT_BUFFER))
- {
- HeapFree(GetProcessHeap(), 0, pevlr);
- pevlr = HeapAlloc(GetProcessHeap(), 0, dwNeeded);
- g_RecordPtrs[dwCurrentRecord] = pevlr;
-
- ReadEventLogW(hEventLog, // event log handle
- dwFlags, // read flags
- 0, // offset; default is 0
- pevlr, // pointer to buffer
- dwNeeded, // size of buffer
- &dwRead, // number of bytes read
- &dwNeeded); // bytes in next record
- }
+ StringCbPrintfW(buffer, sizeof(buffer),
+ L"%d %d %d %d %d",
+ Settings.wpPos.rcNormalPosition.left,
+ Settings.wpPos.rcNormalPosition.top,
+ Settings.wpPos.rcNormalPosition.right,
+ Settings.wpPos.rcNormalPosition.bottom,
+ Settings.wpPos.showCmd);
- while (dwRead > 0)
- {
- LoadStringW(hInst, IDS_NOT_AVAILABLE, szUsername, MAX_PATH);
- LoadStringW(hInst, IDS_NOT_AVAILABLE, szEventText, MAX_PATH);
- LoadStringW(hInst, IDS_NONE, szCategory, MAX_PATH);
+ dwSize = wcslen(buffer) * sizeof(WCHAR);
+ RegSetValueExW(hKeyEventVwr, L"Window", 0, REG_SZ, (LPBYTE)buffer, dwSize);
- // Get the event source name.
- lpSourceName = (LPWSTR)((LPBYTE)pevlr + sizeof(EVENTLOGRECORD));
+Quit:
+ RegCloseKey(hKeyEventVwr);
+ return TRUE;
+}
+
+int APIENTRY
+wWinMain(HINSTANCE hInstance,
+ HINSTANCE hPrevInstance,
+ LPWSTR lpCmdLine,
+ int nCmdShow)
+{
+ HANDLE hThread;
+ INITCOMMONCONTROLSEX iccx;
+ HMODULE hRichEdit;
+ HACCEL hAccelTable;
+ MSG msg;
+
+ UNREFERENCED_PARAMETER(hPrevInstance);
+ UNREFERENCED_PARAMETER(lpCmdLine);
+
+ /* Whenever any of the common controls are used in your app,
+ * you must call InitCommonControlsEx() to register the classes
+ * for those controls. */
+ iccx.dwSize = sizeof(iccx);
+ iccx.dwICC = ICC_LISTVIEW_CLASSES;
+ InitCommonControlsEx(&iccx);
+
+ /* Load the RichEdit DLL to add support for RichEdit controls */
+ hRichEdit = LoadLibraryW(L"riched20.dll");
+ if (!hRichEdit)
+ return -1;
+
+ msg.wParam = (WPARAM)-1;
+
+ /* Store the instance handle in the global variable */
+ hInst = hInstance;
+
+ /* Initialize global strings */
+ LoadStringW(hInstance, IDS_APP_TITLE, szTitle, ARRAYSIZE(szTitle));
+ LoadStringW(hInstance, IDS_APP_TITLE_EX, szTitleTemplate, ARRAYSIZE(szTitleTemplate));
+ LoadStringW(hInstance, IDS_STATUS_MSG, szStatusBarTemplate, ARRAYSIZE(szStatusBarTemplate));
+ LoadStringW(hInstance, IDS_LOADING_WAIT, szLoadingWait, ARRAYSIZE(szLoadingWait));
+ LoadStringW(hInstance, IDS_NO_ITEMS, szEmptyList, ARRAYSIZE(szEmptyList));
+
+ /*
+ * Process the command-line arguments. Note that we need the full
+ * command-line, with the program file included, and not just what
+ * WinMain() provides in its lpCmdLine parameter.
+ */
+ if (!ProcessCmdLine(GetCommandLineW()))
+ goto Quit;
+
+ if (!MyRegisterClass(hInstance))
+ goto Quit;
+
+ /* Load the settings */
+ LoadSettings(nCmdShow);
+
+ /* Perform application initialization */
+ if (!InitInstance(hInstance))
+ goto Quit;
+
+ hAccelTable = LoadAcceleratorsW(hInstance, MAKEINTRESOURCEW(IDA_EVENTVWR));
+
+ /* Create the Start/Stop enumerator thread */
+ // Manual-reset event
+ hStartStopEnumEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
+ if (!hStartStopEnumEvent)
+ goto Cleanup;
+
+ // Auto-reset event
+ hStartEnumEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
+ if (!hStartEnumEvent)
+ goto Cleanup;
+
+ hThread = CreateThread(NULL, 0,
+ StartStopEnumEventsThread,
+ NULL, 0, NULL);
+ if (!hThread)
+ goto Cleanup;
+
+ /* Retrieve the available event logs on this computer and create filters for them */
+ InitializeListHead(&EventLogList);
+ InitializeListHead(&EventLogFilterList);
+ BuildLogListAndFilterList(lpComputerName);
+
+ /* Open the user-specified logs if any are present on the command-line */
+ if (lpszzUserLogsToLoad)
+ {
+ LPWSTR lpUserLog;
+ for (lpUserLog = lpszzUserLogsToLoad; *lpUserLog; lpUserLog += wcslen(lpUserLog) + 1)
+ {
+ OpenUserEventLogFile(lpUserLog);
+ }
+
+ /* Now cleanup the list of user logs */
+ cbUserLogsSize = 0;
+ HeapFree(GetProcessHeap(), 0, lpszzUserLogsToLoad);
+ lpszzUserLogsToLoad = NULL;
+ }
+
+ /* Main message loop */
+ while (GetMessageW(&msg, NULL, 0, 0))
+ {
+ if (!TranslateAcceleratorW(hwndMainWindow, hAccelTable, &msg))
+ {
+ TranslateMessage(&msg);
+ DispatchMessageW(&msg);
+ }
+ }
+
+ /* Save the settings */
+ SaveSettings();
+
+ /* Disconnect from computer */
+ if (hkMachine && hkMachine != HKEY_LOCAL_MACHINE)
+ {
+ /* We are connected to some other computer, close the old connection */
+ RegCloseKey(hkMachine);
+ hkMachine = NULL;
+ }
+
+ /* Stop the enumerator thread */
+ SetEvent(hStartStopEnumEvent);
+ WaitForSingleObject(hThread, INFINITE);
+ CloseHandle(hThread);
+
+ /* Free the filters list and the event logs list */
+ FreeLogFilterList();
+ FreeLogList();
+
+Cleanup:
+ /* Handle cleanup */
+ if (hStartEnumEvent)
+ CloseHandle(hStartEnumEvent);
+ if (hStartStopEnumEvent)
+ CloseHandle(hStartStopEnumEvent);
+
+Quit:
+ /* Final cleanup */
+ if (lpszzUserLogsToLoad)
+ {
+ cbUserLogsSize = 0;
+ HeapFree(GetProcessHeap(), 0, lpszzUserLogsToLoad);
+ lpszzUserLogsToLoad = NULL;
+ }
+ if (lpComputerName)
+ {
+ HeapFree(GetProcessHeap(), 0, lpComputerName);
+ lpComputerName = NULL;
+ }
+ FreeLibrary(hRichEdit);
+
+ return (int)msg.wParam;
+}
+
+
+/* GENERIC HELPER FUNCTIONS ***************************************************/
+
+VOID
+EventTimeToSystemTime(IN DWORD EventTime,
+ OUT PSYSTEMTIME pSystemTime)
+{
+ SYSTEMTIME st1970 = { 1970, 1, 0, 1, 0, 0, 0, 0 };
+ FILETIME ftLocal;
+ union
+ {
+ FILETIME ft;
+ ULONGLONG ll;
+ } u1970, uUCT;
+
+ uUCT.ft.dwHighDateTime = 0;
+ uUCT.ft.dwLowDateTime = EventTime;
+ SystemTimeToFileTime(&st1970, &u1970.ft);
+ uUCT.ll = uUCT.ll * 10000000 + u1970.ll;
+ FileTimeToLocalFileTime(&uUCT.ft, &ftLocal);
+ FileTimeToSystemTime(&ftLocal, pSystemTime);
+}
+
+/*
+ * This function takes in entry a path to a single DLL, in which
+ * the message string of ID dwMessageId has to be searched.
+ * The other parameters are similar to those of the FormatMessageW API.
+ */
+LPWSTR
+GetMessageStringFromDll(
+ IN LPCWSTR lpMessageDll,
+ IN DWORD dwFlags, // If we always use the same flags, just remove this param...
+ IN DWORD dwMessageId,
+ IN DWORD nSize,
+ IN va_list* Arguments OPTIONAL)
+{
+ HMODULE hLibrary;
+ DWORD dwLength;
+ LPWSTR lpMsgBuf = NULL;
+
+ hLibrary = LoadLibraryExW(lpMessageDll, NULL,
+ /* LOAD_LIBRARY_AS_IMAGE_RESOURCE | */ LOAD_LIBRARY_AS_DATAFILE);
+ if (hLibrary == NULL)
+ return NULL;
+
+ /* Sanitize dwFlags */
+ dwFlags &= ~FORMAT_MESSAGE_FROM_STRING;
+ dwFlags |= FORMAT_MESSAGE_FROM_HMODULE;
+
+ _SEH2_TRY
+ {
+ /*
+ * Retrieve the message string without appending extra newlines.
+ * Wrap in SEH to protect from invalid string parameters.
+ */
+ _SEH2_TRY
+ {
+ dwLength = FormatMessageW(dwFlags,
+ /* FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
+ FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, */
+ hLibrary,
+ dwMessageId,
+ LANG_USER_DEFAULT,
+ (LPWSTR)&lpMsgBuf,
+ nSize,
+ Arguments);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ dwLength = 0;
+
+ /*
+ * An exception occurred while calling FormatMessage, this is usually
+ * the sign that a parameter was invalid, either 'lpMsgBuf' was NULL
+ * but we did not pass the flag FORMAT_MESSAGE_ALLOCATE_BUFFER, or the
+ * array pointer 'Arguments' was NULL or did not contain enough elements,
+ * and we did not pass the flag FORMAT_MESSAGE_IGNORE_INSERTS, and the
+ * message string expected too many inserts.
+ * In this last case only, we can call again FormatMessage but ignore
+ * explicitly the inserts. The string that we will return to the user
+ * will not be pre-formatted.
+ */
+ if (((dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) || lpMsgBuf) &&
+ !(dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS))
+ {
+ /* Remove any possible harmful flags and always ignore inserts */
+ dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY;
+ dwFlags |= FORMAT_MESSAGE_IGNORE_INSERTS;
+
+ /* If this call also throws an exception, we are really dead */
+ dwLength = FormatMessageW(dwFlags,
+ hLibrary,
+ dwMessageId,
+ LANG_USER_DEFAULT,
+ (LPWSTR)&lpMsgBuf,
+ nSize,
+ NULL /* Arguments */);
+ }
+ }
+ _SEH2_END;
+ }
+ _SEH2_FINALLY
+ {
+ FreeLibrary(hLibrary);
+ }
+ _SEH2_END;
+
+ if (dwLength == 0)
+ {
+ ASSERT(lpMsgBuf == NULL);
+ lpMsgBuf = NULL;
+ }
+ else
+ {
+ LPWSTR ptr;
+
+ ASSERT(lpMsgBuf);
+
+ /* Trim any trailing whitespace */
+ ptr = lpMsgBuf + dwLength - 1;
+ while (iswspace(*ptr))
+ *ptr-- = UNICODE_NULL;
+ }
+
+ return lpMsgBuf;
+}
+
+/*
+ * This function takes in entry a comma-separated list of DLLs, in which
+ * the message string of ID dwMessageId has to be searched.
+ * The other parameters are similar to those of the FormatMessageW API.
+ */
+LPWSTR
+GetMessageStringFromDllList(
+ IN LPCWSTR lpMessageDllList,
+ IN DWORD dwFlags, // If we always use the same flags, just remove this param...
+ IN DWORD dwMessageId,
+ IN DWORD nSize,
+ IN va_list* Arguments OPTIONAL)
+{
+ BOOL Success = FALSE;
+ SIZE_T cbLength;
+ LPWSTR szMessageDllList;
+ LPWSTR szDll;
+ LPWSTR lpMsgBuf = NULL;
+
+ /* Allocate a local buffer for the DLL list that can be tokenized */
+ // TODO: Optimize that!! Maybe we can cleverly use lpMessageDllList in read/write mode
+ // and cleverly temporarily replace the ';' by UNICODE_NULL, do our job, then reverse the change.
+ cbLength = (wcslen(lpMessageDllList) + 1) * sizeof(WCHAR);
+ szMessageDllList = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbLength);
+ if (!szMessageDllList)
+ return NULL;
+ RtlCopyMemory(szMessageDllList, lpMessageDllList, cbLength);
+
+ /* Loop through the list of message DLLs */
+ szDll = wcstok(szMessageDllList, EVENT_DLL_SEPARATOR);
+ while ((szDll != NULL) && !Success)
+ {
+ // Uses LANG_USER_DEFAULT
+ lpMsgBuf = GetMessageStringFromDll(szDll,
+ dwFlags,
+ dwMessageId,
+ nSize,
+ Arguments);
+ if (lpMsgBuf)
+ {
+ /* The ID was found and the message was formatted */
+ Success = TRUE;
+ break;
+ }
+
+ /*
+ * The DLL could not be loaded, or the message could not be found,
+ * try the next DLL, if any.
+ */
+ szDll = wcstok(NULL, EVENT_DLL_SEPARATOR);
+ }
+
+ HeapFree(GetProcessHeap(), 0, szMessageDllList);
+
+ return lpMsgBuf;
+}
+
+
+typedef struct
+{
+ LPWSTR pStartingAddress; // Pointer to the beginning of a parameter string in pMessage
+ LPWSTR pEndingAddress; // Pointer to the end of a parameter string in pMessage
+ DWORD pParameterID; // Parameter identifier found in pMessage
+ LPWSTR pParameter; // Actual parameter string
+} param_strings_format_data;
+
+DWORD
+ApplyParameterStringsToMessage(
+ IN LPCWSTR lpMessageDllList,
+ IN BOOL bMessagePreFormatted,
+ IN CONST LPCWSTR pMessage,
+ OUT LPWSTR* pFinalMessage)
+{
+ /*
+ * This code is heavily adapted from the MSDN example:
+ * https://msdn.microsoft.com/en-us/library/windows/desktop/bb427356.aspx
+ * with bugs removed.
+ */
+
+ DWORD Status = ERROR_SUCCESS;
+ DWORD dwParamCount = 0; // Number of insertion strings found in pMessage
+ size_t cchBuffer = 0; // Size of the buffer in characters
+ size_t cchParams = 0; // Number of characters in all the parameter strings
+ size_t cch = 0;
+ DWORD i = 0;
+ param_strings_format_data* pParamData = NULL; // Array of pointers holding information about each parameter string in pMessage
+ LPWSTR pTempMessage = (LPWSTR)pMessage;
+ LPWSTR pTempFinalMessage = NULL;
+
+ *pFinalMessage = NULL;
+
+ /* Determine the number of parameter insertion strings in pMessage */
+ if (bMessagePreFormatted)
+ {
+ while ((pTempMessage = wcschr(pTempMessage, L'%')))
+ {
+ pTempMessage++;
+ if (iswdigit(*pTempMessage))
+ {
+ dwParamCount++;
+ while (iswdigit(*++pTempMessage)) ;
+ }
+ }
+ }
+ else
+ {
+ while ((pTempMessage = wcsstr(pTempMessage, L"%%")))
+ {
+ pTempMessage += 2;
+ if (iswdigit(*pTempMessage))
+ {
+ dwParamCount++;
+ while (iswdigit(*++pTempMessage)) ;
+ }
+ }
+ }
+
+ /* If there are no parameter insertion strings in pMessage, just return */
+ if (dwParamCount == 0)
+ {
+ // *pFinalMessage = NULL;
+ goto Cleanup;
+ }
+
+ /* Allocate the array of parameter string format data */
+ pParamData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwParamCount * sizeof(param_strings_format_data));
+ if (!pParamData)
+ {
+ Status = ERROR_OUTOFMEMORY;
+ goto Cleanup;
+ }
+
+ /*
+ * Retrieve each parameter in pMessage and the beginning and end of the
+ * insertion string, as well as the message identifier of the parameter.
+ */
+ pTempMessage = (LPWSTR)pMessage;
+ if (bMessagePreFormatted)
+ {
+ while ((pTempMessage = wcschr(pTempMessage, L'%')) && (i < dwParamCount))
+ {
+ pTempMessage++;
+ if (iswdigit(*pTempMessage))
+ {
+ pParamData[i].pStartingAddress = pTempMessage-1;
+ pParamData[i].pParameterID = (DWORD)_wtol(pTempMessage);
+
+ while (iswdigit(*++pTempMessage)) ;
+
+ pParamData[i].pEndingAddress = pTempMessage;
+ i++;
+ }
+ }
+ }
+ else
+ {
+ while ((pTempMessage = wcsstr(pTempMessage, L"%%")) && (i < dwParamCount))
+ {
+ pTempMessage += 2;
+ if (iswdigit(*pTempMessage))
+ {
+ pParamData[i].pStartingAddress = pTempMessage-2;
+ pParamData[i].pParameterID = (DWORD)_wtol(pTempMessage);
+
+ while (iswdigit(*++pTempMessage)) ;
+
+ pParamData[i].pEndingAddress = pTempMessage;
+ i++;
+ }
+ }
+ }
+
+ /* Retrieve each parameter string */
+ for (i = 0; i < dwParamCount; i++)
+ {
+ // pParamData[i].pParameter = GetMessageString(pParamData[i].pParameterID, 0, NULL);
+ pParamData[i].pParameter =
+ GetMessageStringFromDllList(lpMessageDllList,
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
+ FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
+ pParamData[i].pParameterID,
+ 0, NULL);
+ if (!pParamData[i].pParameter)
+ {
+ /* Skip the insertion string */
+ continue;
+ }
+
+ cchParams += wcslen(pParamData[i].pParameter);
+ }
+
+ /*
+ * Allocate the final message buffer, the size of which is based on the
+ * length of the original message and the length of each parameter string.
+ */
+ cchBuffer = wcslen(pMessage) + cchParams + 1;
+ *pFinalMessage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cchBuffer * sizeof(WCHAR));
+ if (!*pFinalMessage)
+ {
+ Status = ERROR_OUTOFMEMORY;
+ goto Cleanup;
+ }
+
+ pTempFinalMessage = *pFinalMessage;
+
+ /* Build the final message string */
+ pTempMessage = (LPWSTR)pMessage;
+ for (i = 0; i < dwParamCount; i++)
+ {
+ /* Append the segment from pMessage */
+ cch = pParamData[i].pStartingAddress - pTempMessage;
+ StringCchCopyNW(pTempFinalMessage, cchBuffer, pTempMessage, cch);
+ pTempMessage = pParamData[i].pEndingAddress;
+ cchBuffer -= cch;
+ pTempFinalMessage += cch;
+
+ /* Append the parameter string */
+ if (pParamData[i].pParameter)
+ {
+ StringCchCopyW(pTempFinalMessage, cchBuffer, pParamData[i].pParameter);
+ cch = wcslen(pParamData[i].pParameter); // pTempFinalMessage
+ }
+ else
+ {
+ /*
+ * We failed to retrieve the parameter string before, so just
+ * place back the original string placeholder.
+ */
+ cch = pParamData[i].pEndingAddress /* == pTempMessage */ - pParamData[i].pStartingAddress;
+ StringCchCopyNW(pTempFinalMessage, cchBuffer, pParamData[i].pStartingAddress, cch);
+ // cch = wcslen(pTempFinalMessage);
+ }
+ cchBuffer -= cch;
+ pTempFinalMessage += cch;
+ }
+
+ /* Append the last segment from pMessage */
+ StringCchCopyW(pTempFinalMessage, cchBuffer, pTempMessage);
+
+Cleanup:
+
+ // if (Status != ERROR_SUCCESS)
+ // *pFinalMessage = NULL;
+
+ if (pParamData)
+ {
+ for (i = 0; i < dwParamCount; i++)
+ {
+ if (pParamData[i].pParameter)
+ LocalFree(pParamData[i].pParameter);
+ }
+
+ HeapFree(GetProcessHeap(), 0, pParamData);
+ }
+
+ return Status;
+}
+
+
+/*
+ * The following functions were adapted from
+ * shell32!dialogs/filedefext.cpp:``SH_...'' functions.
+ */
+
+UINT
+FormatInteger(LONGLONG Num, LPWSTR pwszResult, UINT cchResultMax)
+{
+ WCHAR wszNumber[24];
+ WCHAR wszDecimalSep[8], wszThousandSep[8];
+ NUMBERFMTW nf;
+ WCHAR wszGrouping[12];
+ INT cchGrouping;
+ INT cchResult;
+ INT i;
+
+ // Print the number in uniform mode
+ swprintf(wszNumber, L"%I64u", Num);
+
+ // Get system strings for decimal and thousand separators.
+ GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, wszDecimalSep, _countof(wszDecimalSep));
+ GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, wszThousandSep, _countof(wszThousandSep));
+
+ // Initialize format for printing the number in bytes
+ ZeroMemory(&nf, sizeof(nf));
+ nf.lpDecimalSep = wszDecimalSep;
+ nf.lpThousandSep = wszThousandSep;
+
+ // Get system string for groups separator
+ cchGrouping = GetLocaleInfoW(LOCALE_USER_DEFAULT,
+ LOCALE_SGROUPING,
+ wszGrouping,
+ _countof(wszGrouping));
+
+ // Convert grouping specs from string to integer
+ for (i = 0; i < cchGrouping; i++)
+ {
+ WCHAR wch = wszGrouping[i];
+
+ if (wch >= L'0' && wch <= L'9')
+ nf.Grouping = nf.Grouping * 10 + (wch - L'0');
+ else if (wch != L';')
+ break;
+ }
+
+ if ((nf.Grouping % 10) == 0)
+ nf.Grouping /= 10;
+ else
+ nf.Grouping *= 10;
+
+ // Format the number
+ cchResult = GetNumberFormatW(LOCALE_USER_DEFAULT,
+ 0,
+ wszNumber,
+ &nf,
+ pwszResult,
+ cchResultMax);
+
+ if (!cchResult)
+ return 0;
+
+ // GetNumberFormatW returns number of characters including UNICODE_NULL
+ return cchResult - 1;
+}
+
+UINT
+FormatByteSize(LONGLONG cbSize, LPWSTR pwszResult, UINT cchResultMax)
+{
+ INT cchWritten;
+ LPWSTR pwszEnd;
+ size_t cchRemaining;
+
+ /* Write formated bytes count */
+ cchWritten = FormatInteger(cbSize, pwszResult, cchResultMax);
+ if (!cchWritten)
+ return 0;
+
+ /* Copy " bytes" to buffer */
+ pwszEnd = pwszResult + cchWritten;
+ cchRemaining = cchResultMax - cchWritten;
+ StringCchCopyExW(pwszEnd, cchRemaining, L" ", &pwszEnd, &cchRemaining, 0);
+ cchWritten = LoadStringW(hInst, IDS_BYTES_FORMAT, pwszEnd, cchRemaining);
+ cchRemaining -= cchWritten;
+
+ return cchResultMax - cchRemaining;
+}
+
+LPWSTR
+FormatFileSizeWithBytes(const PULARGE_INTEGER lpQwSize, LPWSTR pwszResult, UINT cchResultMax)
+{
+ UINT cchWritten;
+ LPWSTR pwszEnd;
+ size_t cchRemaining;
+
+ /* Format bytes in KBs, MBs etc */
+ if (StrFormatByteSizeW(lpQwSize->QuadPart, pwszResult, cchResultMax) == NULL)
+ return NULL;
+
+ /* If there is less bytes than 1KB, we have nothing to do */
+ if (lpQwSize->QuadPart < 1024)
+ return pwszResult;
+
+ /* Concatenate " (" */
+ cchWritten = wcslen(pwszResult);
+ pwszEnd = pwszResult + cchWritten;
+ cchRemaining = cchResultMax - cchWritten;
+ StringCchCopyExW(pwszEnd, cchRemaining, L" (", &pwszEnd, &cchRemaining, 0);
+
+ /* Write formated bytes count */
+ cchWritten = FormatByteSize(lpQwSize->QuadPart, pwszEnd, cchRemaining);
+ pwszEnd += cchWritten;
+ cchRemaining -= cchWritten;
+
+ /* Copy ")" to the buffer */
+ StringCchCopyW(pwszEnd, cchRemaining, L")");
+
+ return pwszResult;
+}
+
+/* Adapted from shell32!dialogs/filedefext.cpp:``CFileDefExt::GetFileTimeString'' */
+BOOL
+GetFileTimeString(LPFILETIME lpFileTime, LPWSTR pwszResult, UINT cchResult)
+{
+ FILETIME ft;
+ SYSTEMTIME st;
+ int cchWritten;
+ size_t cchRemaining = cchResult;
+ LPWSTR pwszEnd = pwszResult;
+
+ if (!FileTimeToLocalFileTime(lpFileTime, &ft) || !FileTimeToSystemTime(&ft, &st))
+ return FALSE;
+
+ cchWritten = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, pwszEnd, cchRemaining);
+ if (cchWritten)
+ --cchWritten; // GetDateFormatW returns count with terminating zero
+ // else
+ // ERR("GetDateFormatW failed\n");
+
+ cchRemaining -= cchWritten;
+ pwszEnd += cchWritten;
+
+ StringCchCopyExW(pwszEnd, cchRemaining, L", ", &pwszEnd, &cchRemaining, 0);
+
+ cchWritten = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, pwszEnd, cchRemaining);
+ if (cchWritten)
+ --cchWritten; // GetTimeFormatW returns count with terminating zero
+ // else
+ // ERR("GetTimeFormatW failed\n");
+
+ return TRUE;
+}
+
+
+HTREEITEM
+TreeViewAddItem(IN HWND hTreeView,
+ IN HTREEITEM hParent,
+ IN LPWSTR lpText,
+ IN INT Image,
+ IN INT SelectedImage,
+ IN LPARAM lParam)
+{
+ TV_INSERTSTRUCTW Insert;
+
+ ZeroMemory(&Insert, sizeof(Insert));
+
+ Insert.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+ Insert.hInsertAfter = TVI_LAST;
+ Insert.hParent = hParent;
+ Insert.item.pszText = lpText;
+ Insert.item.iImage = Image;
+ Insert.item.iSelectedImage = SelectedImage;
+ Insert.item.lParam = lParam;
+
+ Insert.item.mask |= TVIF_STATE;
+ Insert.item.stateMask = TVIS_OVERLAYMASK;
+ Insert.item.state = INDEXTOOVERLAYMASK(1);
+
+ return TreeView_InsertItem(hTreeView, &Insert);
+}
+
+
+/* LOG HELPER FUNCTIONS *******************************************************/
+
+PEVENTLOG
+AllocEventLog(IN PCWSTR ComputerName OPTIONAL,
+ IN PCWSTR LogName,
+ IN BOOL Permanent)
+{
+ PEVENTLOG EventLog;
+ UINT cchName;
+
+ /* Allocate a new event log entry */
+ EventLog = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*EventLog));
+ if (!EventLog)
+ return NULL;
+
+ /* Allocate the computer name string (optional) and copy it */
+ if (ComputerName)
+ {
+ cchName = wcslen(ComputerName) + 1;
+ EventLog->ComputerName = HeapAlloc(GetProcessHeap(), 0, cchName * sizeof(WCHAR));
+ if (EventLog->ComputerName)
+ StringCchCopyW(EventLog->ComputerName, cchName, ComputerName);
+ }
+
+ /* Allocate the event log name string and copy it */
+ cchName = wcslen(LogName) + 1;
+ EventLog->LogName = HeapAlloc(GetProcessHeap(), 0, cchName * sizeof(WCHAR));
+ if (!EventLog->LogName)
+ {
+ if (EventLog->ComputerName)
+ HeapFree(GetProcessHeap(), 0, EventLog->ComputerName);
+ HeapFree(GetProcessHeap(), 0, EventLog);
+ return NULL;
+ }
+ StringCchCopyW(EventLog->LogName, cchName, LogName);
+
+ EventLog->Permanent = Permanent;
+
+ return EventLog;
+}
+
+VOID
+EventLog_Free(IN PEVENTLOG EventLog)
+{
+ if (EventLog->LogName)
+ HeapFree(GetProcessHeap(), 0, EventLog->LogName);
+
+ if (EventLog->ComputerName)
+ HeapFree(GetProcessHeap(), 0, EventLog->ComputerName);
+
+ if (EventLog->FileName)
+ HeapFree(GetProcessHeap(), 0, EventLog->FileName);
+
+ HeapFree(GetProcessHeap(), 0, EventLog);
+}
+
+
+PWSTR
+AllocAndCopyMultiStr(IN PCWSTR MultiStr OPTIONAL)
+{
+ PWSTR pStr;
+ ULONG Length;
+
+ if (!MultiStr)
+ return NULL;
+
+ pStr = (PWSTR)MultiStr;
+ while (*pStr) pStr += (wcslen(pStr) + 1);
+ Length = MultiStr - pStr + 2;
+
+ pStr = HeapAlloc(GetProcessHeap(), 0, Length * sizeof(WCHAR));
+ // NOTE: If we failed allocating the string, then fall back into no filter!
+ if (pStr)
+ RtlCopyMemory(pStr, MultiStr, Length * sizeof(WCHAR));
+
+ return pStr;
+}
+
+PEVENTLOGFILTER
+AllocEventLogFilter(// IN PCWSTR FilterName,
+ IN BOOL Information,
+ IN BOOL Warning,
+ IN BOOL Error,
+ IN BOOL AuditSuccess,
+ IN BOOL AuditFailure,
+ IN PCWSTR Sources OPTIONAL,
+ IN PCWSTR Users OPTIONAL,
+ IN PCWSTR ComputerNames OPTIONAL,
+ IN ULONG NumOfEventLogs,
+ IN PEVENTLOG* EventLogs)
+{
+ PEVENTLOGFILTER EventLogFilter;
+
+ /* Allocate a new event log filter entry, big enough to accommodate the list of logs */
+ EventLogFilter = HeapAlloc(GetProcessHeap(),
+ HEAP_ZERO_MEMORY,
+ FIELD_OFFSET(EVENTLOGFILTER, EventLogs[NumOfEventLogs]));
+ if (!EventLogFilter)
+ return NULL;
+
+ EventLogFilter->Information = Information;
+ EventLogFilter->Warning = Warning;
+ EventLogFilter->Error = Error;
+ EventLogFilter->AuditSuccess = AuditSuccess;
+ EventLogFilter->AuditFailure = AuditFailure;
+
+ /* Allocate and copy the sources, users, and computers multi-strings */
+ EventLogFilter->Sources = AllocAndCopyMultiStr(Sources);
+ EventLogFilter->Users = AllocAndCopyMultiStr(Users);
+ EventLogFilter->ComputerNames = AllocAndCopyMultiStr(ComputerNames);
+
+ /* Copy the list of event logs */
+ EventLogFilter->NumOfEventLogs = NumOfEventLogs;
+ RtlCopyMemory(EventLogFilter->EventLogs, EventLogs, NumOfEventLogs * sizeof(PEVENTLOG));
+
+ /* Initialize the filter reference count */
+ EventLogFilter->ReferenceCount = 1;
+
+ return EventLogFilter;
+}
+
+VOID
+EventLogFilter_Free(IN PEVENTLOGFILTER EventLogFilter)
+{
+ if (EventLogFilter->Sources)
+ HeapFree(GetProcessHeap(), 0, EventLogFilter->Sources);
+
+ if (EventLogFilter->Users)
+ HeapFree(GetProcessHeap(), 0, EventLogFilter->Users);
+
+ if (EventLogFilter->ComputerNames)
+ HeapFree(GetProcessHeap(), 0, EventLogFilter->ComputerNames);
+
+ HeapFree(GetProcessHeap(), 0, EventLogFilter);
+}
+
+LONG EventLogFilter_AddRef(IN PEVENTLOGFILTER EventLogFilter)
+{
+ ASSERT(EventLogFilter);
+ return InterlockedIncrement(&EventLogFilter->ReferenceCount);
+}
+
+LONG EventLogFilter_Release(IN PEVENTLOGFILTER EventLogFilter)
+{
+ LONG RefCount;
+
+ ASSERT(EventLogFilter);
+
+ /* When the reference count reaches zero, delete the filter */
+ RefCount = InterlockedDecrement(&EventLogFilter->ReferenceCount);
+ if (RefCount <= 0)
+ {
+ /* Remove the filter from the list */
+ /** RemoveEntryList(&EventLogFilter->ListEntry); **/
+ EventLogFilter_Free(EventLogFilter);
+ }
+
+ return RefCount;
+}
+
+void
+TrimNulls(LPWSTR s)
+{
+ WCHAR *c;
+
+ if (s != NULL)
+ {
+ c = s + wcslen(s) - 1;
+ while (c >= s && iswspace(*c))
+ --c;
+ *++c = L'\0';
+ }
+}
+
+DWORD
+GetExpandedFilePathName(
+ IN LPCWSTR ComputerName OPTIONAL,
+ IN LPCWSTR lpFileName,
+ OUT LPWSTR lpFullFileName OPTIONAL,
+ IN DWORD nSize)
+{
+ DWORD dwLength;
+
+ /* Determine the needed size after expansion of any environment strings */
+ dwLength = ExpandEnvironmentStringsW(lpFileName, NULL, 0);
+ if (dwLength == 0)
+ {
+ /* We failed, bail out */
+ return 0;
+ }
+
+ /* If the file path is on a remote computer, estimate its length */
+ // FIXME: Use WNetGetUniversalName instead?
+ if (ComputerName && *ComputerName)
+ {
+ /* Skip any leading backslashes */
+ while (*ComputerName == L'\\')
+ ++ComputerName;
+
+ if (*ComputerName)
+ {
+ /* Count 2 backslashes plus the computer name and one backslash separator */
+ dwLength += 2 + wcslen(ComputerName) + 1;
+ }
+ }
+
+ /* Check whether we have enough space */
+ if (dwLength > nSize)
+ {
+ /* No, return the needed size in characters (includes NULL-terminator) */
+ return dwLength;
+ }
+
+
+ /* Now expand the file path */
+ ASSERT(dwLength <= nSize);
+
+ /* Expand any existing environment strings */
+ if (ExpandEnvironmentStringsW(lpFileName, lpFullFileName, dwLength) == 0)
+ {
+ /* We failed, bail out */
+ return 0;
+ }
+
+ /* If the file path is on a remote computer, retrieve the network share form of the file name */
+ // FIXME: Use WNetGetUniversalName instead?
+ if (ComputerName && *ComputerName)
+ {
+ /* Note that we previously skipped any potential leading backslashes */
+
+ /* Replace ':' by '$' in the drive letter */
+ if (*lpFullFileName && lpFullFileName[1] == L':')
+ lpFullFileName[1] = L'$';
+
+ /* Prepend the computer name */
+ RtlMoveMemory(lpFullFileName + 2 + wcslen(ComputerName) + 1,
+ lpFullFileName, dwLength * sizeof(WCHAR) - (2 + wcslen(ComputerName) + 1) * sizeof(WCHAR));
+ lpFullFileName[0] = L'\\';
+ lpFullFileName[1] = L'\\';
+ wcsncpy(lpFullFileName + 2, ComputerName, wcslen(ComputerName));
+ lpFullFileName[2 + wcslen(ComputerName)] = L'\\';
+ }
+
+ /* Return the number of stored characters (includes NULL-terminator) */
+ return dwLength;
+}
+
+BOOL
+GetEventMessageFileDLL(IN LPCWSTR lpLogName,
+ IN LPCWSTR SourceName,
+ IN LPCWSTR EntryName,
+ OUT PWCHAR lpModuleName) // TODO: Add IN DWORD BufLen
+{
+ BOOL Success = FALSE;
+ LONG Result;
+ DWORD dwType, dwSize;
+ WCHAR szModuleName[MAX_PATH];
+ WCHAR szKeyName[MAX_PATH];
+ HKEY hLogKey = NULL;
+ HKEY hSourceKey = NULL;
+
+ StringCbCopyW(szKeyName, sizeof(szKeyName), EVENTLOG_BASE_KEY);
+ StringCbCatW(szKeyName, sizeof(szKeyName), lpLogName);
+
+ Result = RegOpenKeyExW(hkMachine,
+ szKeyName,
+ 0,
+ KEY_READ,
+ &hLogKey);
+ if (Result != ERROR_SUCCESS)
+ return FALSE;
+
+ Result = RegOpenKeyExW(hLogKey,
+ SourceName,
+ 0,
+ KEY_QUERY_VALUE,
+ &hSourceKey);
+ if (Result != ERROR_SUCCESS)
+ {
+ RegCloseKey(hLogKey);
+ return FALSE;
+ }
+
+ dwSize = sizeof(szModuleName);
+ Result = RegQueryValueExW(hSourceKey,
+ EntryName,
+ NULL,
+ &dwType,
+ (LPBYTE)szModuleName,
+ &dwSize);
+ if ((Result != ERROR_SUCCESS) || (dwType != REG_EXPAND_SZ && dwType != REG_SZ))
+ {
+ szModuleName[0] = UNICODE_NULL;
+ }
+ else
+ {
+ /* NULL-terminate the string and expand it */
+ szModuleName[dwSize / sizeof(WCHAR) - 1] = UNICODE_NULL;
+ GetExpandedFilePathName(lpComputerName, szModuleName, lpModuleName, ARRAYSIZE(szModuleName));
+ Success = TRUE;
+ }
+
+ RegCloseKey(hSourceKey);
+ RegCloseKey(hLogKey);
+
+ return Success;
+}
+
+BOOL
+GetEventCategory(IN LPCWSTR KeyName,
+ IN LPCWSTR SourceName,
+ IN PEVENTLOGRECORD pevlr,
+ OUT PWCHAR CategoryName) // TODO: Add IN DWORD BufLen
+{
+ BOOL Success = FALSE;
+ WCHAR szMessageDLL[MAX_PATH];
+ LPWSTR lpMsgBuf = NULL;
+
+ if (!GetEventMessageFileDLL(KeyName, SourceName, EVENT_CATEGORY_MESSAGE_FILE, szMessageDLL))
+ goto Quit;
+
+ /* Retrieve the message string without appending extra newlines */
+ lpMsgBuf =
+ GetMessageStringFromDllList(szMessageDLL,
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
+ FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
+ pevlr->EventCategory,
+ EVENT_MESSAGE_FILE_BUFFER,
+ NULL);
+ if (lpMsgBuf)
+ {
+ /* Trim the string */
+ TrimNulls(lpMsgBuf);
+
+ /* Copy the category name */
+ StringCchCopyW(CategoryName, MAX_PATH, lpMsgBuf);
+
+ /* Free the buffer allocated by FormatMessage */
+ LocalFree(lpMsgBuf);
+
+ /* The ID was found and the message was formatted */
+ Success = TRUE;
+ }
+
+Quit:
+ if (!Success)
+ {
+ if (pevlr->EventCategory != 0)
+ {
+ StringCchPrintfW(CategoryName, MAX_PATH, L"(%lu)", pevlr->EventCategory);
+ Success = TRUE;
+ }
+ }
+
+ return Success;
+}
+
+
+BOOL // NOTE: Used by evtdetctl.c
+GetEventMessage(IN LPCWSTR KeyName,
+ IN LPCWSTR SourceName,
+ IN PEVENTLOGRECORD pevlr,
+ OUT PWCHAR EventText) // TODO: Add IN DWORD BufLen
+{
+ BOOL Success = FALSE;
+ DWORD i;
+ size_t cch;
+ WCHAR SourceModuleName[1024];
+ WCHAR ParameterModuleName[1024];
+ BOOL IsParamModNameCached = FALSE;
+ LPWSTR lpMsgBuf = NULL;
+ LPWSTR szStringArray, szMessage;
+ LPWSTR *szArguments;
+
+ /* Get the event string array */
+ szStringArray = (LPWSTR)((LPBYTE)pevlr + pevlr->StringOffset);
+
+ /* NOTE: GetEventMessageFileDLL can return a comma-separated list of DLLs */
+ if (!GetEventMessageFileDLL(KeyName, SourceName, EVENT_MESSAGE_FILE, SourceModuleName))
+ goto Quit;
+
+ /* Allocate space for insertion strings */
+ szArguments = HeapAlloc(GetProcessHeap(), 0, pevlr->NumStrings * sizeof(LPVOID));
+ if (!szArguments)
+ goto Quit;
+
+ if (!IsParamModNameCached)
+ {
+ /* Now that the parameter file list is loaded, no need to reload it at the next run! */
+ IsParamModNameCached = GetEventMessageFileDLL(KeyName, SourceName, EVENT_PARAMETER_MESSAGE_FILE, ParameterModuleName);
+ // FIXME: If the string loading failed the first time, no need to retry it just after???
+ }
+
+ if (IsParamModNameCached)
+ {
+ /* Not yet support for reading messages from parameter message DLL */
+ }
+
+ szMessage = szStringArray;
+ /*
+ * HACK:
+ * We do some hackish preformatting of the cached event strings...
+ * That's because after we pass the string to FormatMessage
+ * (via GetMessageStringFromDllList) with the FORMAT_MESSAGE_ARGUMENT_ARRAY
+ * flag, instead of ignoring the insertion parameters and do the formatting
+ * by ourselves. Therefore, the resulting string should have the parameter
+ * string placeholders starting with a single '%' instead of a mix of one
+ * and two '%'.
+ */
+ /* HACK part 1: Compute the full length of the string array */
+ cch = 0;
+ for (i = 0; i < pevlr->NumStrings; i++)
+ {
+ szMessage += wcslen(szMessage) + 1;
+ }
+ cch = szMessage - szStringArray;
+
+ /* HACK part 2: Now do the HACK proper! */
+ szMessage = szStringArray;
+ for (i = 0; i < pevlr->NumStrings; i++)
+ {
+ lpMsgBuf = szMessage;
+ while ((lpMsgBuf = wcsstr(lpMsgBuf, L"%%")))
+ {
+ if (iswdigit(lpMsgBuf[2]))
+ {
+ RtlMoveMemory(lpMsgBuf, lpMsgBuf+1, ((szStringArray + cch) - lpMsgBuf - 1) * sizeof(WCHAR));
+ }
+ }
+
+ szArguments[i] = szMessage;
+ szMessage += wcslen(szMessage) + 1;
+ }
+
+ /* Retrieve the message string without appending extra newlines */
+ lpMsgBuf =
+ GetMessageStringFromDllList(SourceModuleName,
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
+ FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_MAX_WIDTH_MASK,
+ pevlr->EventID,
+ 0,
+ (va_list*)szArguments);
+ if (lpMsgBuf)
+ {
+ /* Trim the string */
+ TrimNulls(lpMsgBuf);
+
+ szMessage = NULL;
+ Success = (ApplyParameterStringsToMessage(ParameterModuleName,
+ TRUE,
+ lpMsgBuf,
+ &szMessage) == ERROR_SUCCESS);
+ if (Success && szMessage)
+ {
+ /* Free the buffer allocated by FormatMessage */
+ LocalFree(lpMsgBuf);
+ lpMsgBuf = szMessage;
+ }
+
+ /* Copy the event text */
+ StringCchCopyW(EventText, EVENT_MESSAGE_EVENTTEXT_BUFFER, lpMsgBuf);
+
+ /* Free the buffer allocated by FormatMessage */
+ LocalFree(lpMsgBuf);
+ }
+
+ HeapFree(GetProcessHeap(), 0, szArguments);
+
+Quit:
+ if (!Success)
+ {
+ /* Get a read-only pointer to the "event-not-found" string */
+ lpMsgBuf = HeapAlloc(GetProcessHeap(), 0, EVENT_MESSAGE_EVENTTEXT_BUFFER * sizeof(WCHAR));
+ LoadStringW(hInst, IDS_EVENTSTRINGIDNOTFOUND, lpMsgBuf, EVENT_MESSAGE_EVENTTEXT_BUFFER);
+ StringCchPrintfW(EventText, EVENT_MESSAGE_EVENTTEXT_BUFFER, lpMsgBuf, (pevlr->EventID & 0xFFFF), SourceName);
+
+ /* Append the strings */
+ szMessage = szStringArray;
+ for (i = 0; i < pevlr->NumStrings; i++)
+ {
+ StringCchCatW(EventText, EVENT_MESSAGE_EVENTTEXT_BUFFER, szMessage);
+ StringCchCatW(EventText, EVENT_MESSAGE_EVENTTEXT_BUFFER, L"\n");
+ szMessage += wcslen(szMessage) + 1;
+ }
+ }
+
+ return Success;
+}
+
+VOID
+GetEventType(IN WORD dwEventType,
+ OUT PWCHAR eventTypeText) // TODO: Add IN DWORD BufLen
+{
+ switch (dwEventType)
+ {
+ case EVENTLOG_ERROR_TYPE:
+ LoadStringW(hInst, IDS_EVENTLOG_ERROR_TYPE, eventTypeText, MAX_LOADSTRING);
+ break;
+ case EVENTLOG_WARNING_TYPE:
+ LoadStringW(hInst, IDS_EVENTLOG_WARNING_TYPE, eventTypeText, MAX_LOADSTRING);
+ break;
+ case EVENTLOG_INFORMATION_TYPE:
+ LoadStringW(hInst, IDS_EVENTLOG_INFORMATION_TYPE, eventTypeText, MAX_LOADSTRING);
+ break;
+ case EVENTLOG_SUCCESS:
+ LoadStringW(hInst, IDS_EVENTLOG_SUCCESS, eventTypeText, MAX_LOADSTRING);
+ break;
+ case EVENTLOG_AUDIT_SUCCESS:
+ LoadStringW(hInst, IDS_EVENTLOG_AUDIT_SUCCESS, eventTypeText, MAX_LOADSTRING);
+ break;
+ case EVENTLOG_AUDIT_FAILURE:
+ LoadStringW(hInst, IDS_EVENTLOG_AUDIT_FAILURE, eventTypeText, MAX_LOADSTRING);
+ break;
+ default:
+ LoadStringW(hInst, IDS_EVENTLOG_UNKNOWN_TYPE, eventTypeText, MAX_LOADSTRING);
+ break;
+ }
+}
+
+BOOL
+GetEventUserName(IN PEVENTLOGRECORD pelr,
+ IN OUT PSID *pLastSid,
+ OUT PWCHAR pszUser) // TODO: Add IN DWORD BufLen
+{
+ PSID pCurrentSid;
+ PWSTR StringSid;
+ WCHAR szName[1024];
+ WCHAR szDomain[1024];
+ SID_NAME_USE peUse;
+ DWORD cchName = ARRAYSIZE(szName);
+ DWORD cchDomain = ARRAYSIZE(szDomain);
+ BOOL Success = FALSE;
+
+ /* Point to the SID */
+ pCurrentSid = (PSID)((LPBYTE)pelr + pelr->UserSidOffset);
+
+ if (!IsValidSid(pCurrentSid))
+ {
+ *pLastSid = NULL;
+ return FALSE;
+ }
+ else if (*pLastSid && EqualSid(*pLastSid, pCurrentSid))
+ {
+ return TRUE;
+ }
+
+ /* User SID */
+ if (pelr->UserSidLength > 0)
+ {
+ /*
+ * Try to retrieve the user account name and domain name corresponding
+ * to the SID. If it cannot be retrieved, try to convert the SID to a
+ * string-form. It should not be bigger than the user-provided buffer
+ * 'pszUser', otherwise we return an error.
+ */
+ if (LookupAccountSidW(lpComputerName,
+ pCurrentSid,
+ szName,
+ &cchName,
+ szDomain,
+ &cchDomain,
+ &peUse))
+ {
+ StringCchCopyW(pszUser, MAX_PATH, szName);
+ Success = TRUE;
+ }
+ else if (ConvertSidToStringSidW(pCurrentSid, &StringSid))
+ {
+ /* Copy the string only if the user-provided buffer is big enough */
+ if (wcslen(StringSid) + 1 <= MAX_PATH) // + 1 for NULL-terminator
+ {
+ StringCchCopyW(pszUser, MAX_PATH, StringSid);
+ Success = TRUE;
+ }
+ else
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ Success = FALSE;
+ }
+
+ /* Free the allocated buffer */
+ LocalFree(StringSid);
+ }
+ }
+
+ *pLastSid = Success ? pCurrentSid : NULL;
+
+ return Success;
+}
+
+
+static VOID FreeRecords(VOID)
+{
+ DWORD iIndex;
+
+ if (!g_RecordPtrs)
+ return;
+
+ for (iIndex = 0; iIndex < g_TotalRecords; iIndex++)
+ {
+ if (g_RecordPtrs[iIndex])
+ HeapFree(GetProcessHeap(), 0, g_RecordPtrs[iIndex]);
+ }
+ HeapFree(GetProcessHeap(), 0, g_RecordPtrs);
+ g_RecordPtrs = NULL;
+ g_TotalRecords = 0;
+}
+
+BOOL
+FilterByType(IN PEVENTLOGFILTER EventLogFilter,
+ IN PEVENTLOGRECORD pevlr)
+{
+ if ((pevlr->EventType == EVENTLOG_SUCCESS && !EventLogFilter->Information ) ||
+ (pevlr->EventType == EVENTLOG_INFORMATION_TYPE && !EventLogFilter->Information ) ||
+ (pevlr->EventType == EVENTLOG_WARNING_TYPE && !EventLogFilter->Warning ) ||
+ (pevlr->EventType == EVENTLOG_ERROR_TYPE && !EventLogFilter->Error ) ||
+ (pevlr->EventType == EVENTLOG_AUDIT_SUCCESS && !EventLogFilter->AuditSuccess) ||
+ (pevlr->EventType == EVENTLOG_AUDIT_FAILURE && !EventLogFilter->AuditFailure))
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+BOOL
+FilterByString(IN PCWSTR FilterString, // This is a multi-string
+ IN PWSTR String)
+{
+ PCWSTR pStr;
+
+ /* The filter string is NULL so it does not filter anything */
+ if (!FilterString)
+ return TRUE;
+
+ /*
+ * If the filter string filters for an empty string AND the source string
+ * is an empty string, we have a match (particular case of the last one).
+ */
+ if (!*FilterString && !*String)
+ return TRUE;
+
+ // if (*FilterString || *String)
+
+ /*
+ * If the filter string is empty BUT the source string is not empty,
+ * OR vice-versa, we cannot have a match.
+ */
+ if ( (!*FilterString && *String) || (*FilterString && !*String) )
+ return FALSE;
+
+ /*
+ * If the filter string filters for at least a non-empty string,
+ * browse it and search for a string that matches the source string.
+ */
+ // else if (*FilterString && *String)
+ {
+ pStr = FilterString;
+ while (*pStr)
+ {
+ if (wcsicmp(pStr, String) == 0)
+ {
+ /* We have a match, break the loop */
+ break;
+ }
+
+ pStr += (wcslen(pStr) + 1);
+ }
+ if (!*pStr) // && *String
+ {
+ /* We do not have a match */
+ return FALSE;
+ }
+ }
+
+ /* We have a match */
+ return TRUE;
+}
+
+/*
+ * The events enumerator thread.
+ */
+static DWORD WINAPI
+EnumEventsThread(IN LPVOID lpParameter)
+{
+ PEVENTLOGFILTER EventLogFilter = (PEVENTLOGFILTER)lpParameter;
+ PEVENTLOG EventLog;
+
+ ULONG LogIndex;
+ HANDLE hEventLog;
+ PEVENTLOGRECORD pEvlr = NULL;
+ PBYTE pEvlrEnd;
+ PBYTE pEvlrBuffer;
+ DWORD dwWanted, dwRead, dwNeeded, dwStatus = ERROR_SUCCESS;
+ DWORD dwTotalRecords = 0, dwCurrentRecord = 0;
+ DWORD dwFlags, dwMaxLength;
+ size_t cchRemaining;
+ LPWSTR lpszSourceName;
+ LPWSTR lpszComputerName;
+ BOOL bResult = TRUE; /* Read succeeded */
+ HANDLE hProcessHeap = GetProcessHeap();
+ PSID pLastSid = NULL;
+
+ UINT uStep = 0, uStepAt = 0, uPos = 0;
+
+ WCHAR szWindowTitle[MAX_PATH];
+ WCHAR szStatusText[MAX_PATH];
+ WCHAR szLocalDate[MAX_PATH];
+ WCHAR szLocalTime[MAX_PATH];
+ WCHAR szEventID[MAX_PATH];
+ WCHAR szEventTypeText[MAX_LOADSTRING];
+ WCHAR szCategoryID[MAX_PATH];
+ WCHAR szUsername[MAX_PATH];
+ WCHAR szNoUsername[MAX_PATH];
+ WCHAR szCategory[MAX_PATH];
+ WCHAR szNoCategory[MAX_PATH];
+ PWCHAR lpTitleTemplateEnd;
+
+ SYSTEMTIME time;
+ LVITEMW lviEventItem;
+
+ /* Save the current event log filter globally */
+ EventLogFilter_AddRef(EventLogFilter);
+ ActiveFilter = EventLogFilter;
+
+
+ /** HACK!! **/
+ EventLog = EventLogFilter->EventLogs[0];
+
+ // FIXME: Use something else instead of EventLog->LogName !!
+
+ /*
+ * Use a different formatting, whether the event log filter holds
+ * only one log, or many logs (the latter case is WIP TODO!)
+ */
+ if (EventLogFilter->NumOfEventLogs <= 1)
+ {
+ StringCchPrintfExW(szWindowTitle,
+ ARRAYSIZE(szWindowTitle),
+ &lpTitleTemplateEnd,
+ &cchRemaining,
+ 0,
+ szTitleTemplate, szTitle, EventLog->LogName); /* i = number of characters written */
+ dwMaxLength = (DWORD)cchRemaining;
+ if (!EventLog->ComputerName)
+ GetComputerNameW(lpTitleTemplateEnd, &dwMaxLength);
+ else
+ StringCchCopyW(lpTitleTemplateEnd, dwMaxLength, EventLog->ComputerName);
+
+ StringCbPrintfW(szStatusText,
+ sizeof(szStatusText),
+ szStatusBarTemplate,
+ EventLog->LogName,
+ 0,
+ 0);
+ }
+ else
+ {
+ // TODO: Use a different title & implement filtering for multi-log filters !!
+ // (EventLogFilter->NumOfEventLogs > 1)
+ MessageBoxW(hwndMainWindow,
+ L"Many-logs filtering is not implemented yet!!",
+ L"Event Log",
+ MB_OK | MB_ICONINFORMATION);
+ }
+
+ /* Set the window title */
+ SetWindowTextW(hwndMainWindow, szWindowTitle);
+
+ /* Update the status bar */
+ StatusBar_SetText(hwndStatus, 0, szStatusText);
+
+
+ /* Disable list view redraw */
+ SendMessageW(hwndListView, WM_SETREDRAW, FALSE, 0);
+
+ /* Clear the list view and free the cached records */
+ ListView_DeleteAllItems(hwndListView);
+ FreeRecords();
+
+ SendMessageW(hwndListView, LVM_PROGRESS, 0, TRUE);
+ ProgressBar_SetRange(hwndStatusProgress, 0);
+ StatusBar_SetText(hwndStatus, 0, NULL);
+ ShowWindow(hwndStatusProgress, SW_SHOW);
+
+ /* Do a loop over the logs enumerated in the filter */
+ // FIXME: For now we only support 1 event log per filter!
+ LogIndex = 0;
+ // for (LogIndex = 0; LogIndex < EventLogFilter->NumOfEventLogs; ++LogIndex)
+ {
+
+ EventLog = EventLogFilter->EventLogs[LogIndex];
+
+ /* Open the event log */
+ if (EventLog->Permanent)
+ hEventLog = OpenEventLogW(EventLog->ComputerName, EventLog->LogName);
+ else
+ hEventLog = OpenBackupEventLogW(EventLog->ComputerName, EventLog->LogName); // FileName
+
+ if (hEventLog == NULL)
+ {
+ ShowWin32Error(GetLastError());
+ goto Cleanup;
+ }
+
+ // GetOldestEventLogRecord(hEventLog, &dwThisRecord);
+
+ /* Get the total number of event log records */
+ GetNumberOfEventLogRecords(hEventLog, &dwTotalRecords);
+
+ if (dwTotalRecords > 0)
+ {
+ EnableMenuItem(hMainMenu, IDM_CLEAR_EVENTS, MF_BYCOMMAND | MF_ENABLED);
+ EnableMenuItem(hMainMenu, IDM_SAVE_EVENTLOG, MF_BYCOMMAND | MF_ENABLED);
+ }
+ else
+ {
+ EnableMenuItem(hMainMenu, IDM_CLEAR_EVENTS, MF_BYCOMMAND | MF_GRAYED);
+ EnableMenuItem(hMainMenu, IDM_SAVE_EVENTLOG, MF_BYCOMMAND | MF_GRAYED);
+ }
+
+ /* Set up the event records cache */
+ g_RecordPtrs = HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, dwTotalRecords * sizeof(*g_RecordPtrs));
+ if (!g_RecordPtrs)
+ {
+ // ShowWin32Error(GetLastError());
+ goto Quit;
+ }
+ g_TotalRecords = dwTotalRecords;
+
+ if (WaitForSingleObject(hStopEnumEvent, 0) == WAIT_OBJECT_0)
+ goto Quit;
+
+ LoadStringW(hInst, IDS_NOT_AVAILABLE, szNoUsername, ARRAYSIZE(szNoUsername));
+ LoadStringW(hInst, IDS_NONE, szNoCategory, ARRAYSIZE(szNoCategory));
+
+ ProgressBar_SetRange(hwndStatusProgress, MAKELPARAM(0, 100));
+ uStepAt = (dwTotalRecords / 100) + 1;
+
+ dwFlags = EVENTLOG_SEQUENTIAL_READ | (Settings.bNewestEventsFirst ? EVENTLOG_FORWARDS_READ : EVENTLOG_BACKWARDS_READ);
+
+ /* 0x7ffff is the maximum buffer size ReadEventLog will accept */
+ dwWanted = 0x7ffff;
+ pEvlr = HeapAlloc(hProcessHeap, 0, dwWanted);
+
+ if (!pEvlr)
+ goto Quit;
- // Get the computer name
- lpComputerName = (LPWSTR)((LPBYTE)pevlr + sizeof(EVENTLOGRECORD) + (wcslen(lpSourceName) + 1) * sizeof(WCHAR));
+ while (dwStatus == ERROR_SUCCESS)
+ {
+ bResult = ReadEventLogW(hEventLog, dwFlags, 0, pEvlr, dwWanted, &dwRead, &dwNeeded);
+ dwStatus = GetLastError();
+
+ if (!bResult && dwStatus == ERROR_INSUFFICIENT_BUFFER)
+ {
+ pEvlr = HeapReAlloc(hProcessHeap, 0, pEvlr, dwNeeded);
+ dwWanted = dwNeeded;
+
+ if (!pEvlr)
+ break;
+
+ bResult = ReadEventLogW(hEventLog, dwFlags, 0, pEvlr, dwNeeded, &dwRead, &dwNeeded);
+
+ if (!bResult)
+ break;
+ }
+ else if (!bResult)
+ {
+ /* Exit on other errors (ERROR_HANDLE_EOF) */
+ break;
+ }
+
+ pEvlrBuffer = (LPBYTE)pEvlr;
+ pEvlrEnd = pEvlrBuffer + dwRead;
+
+ while (pEvlrBuffer < pEvlrEnd)
+ {
+ PEVENTLOGRECORD pEvlrTmp = (PEVENTLOGRECORD)pEvlrBuffer;
+ PWSTR lpszUsername, lpszCategoryName;
+ g_RecordPtrs[dwCurrentRecord] = NULL;
+
+ // ProgressBar_StepIt(hwndStatusProgress);
+ uStep++;
+ if (uStep % uStepAt == 0)
+ {
+ ++uPos;
+ ProgressBar_SetPos(hwndStatusProgress, uPos);
+ }
- // This ist the data section of the current event
- lpData = (LPSTR)((LPBYTE)pevlr + pevlr->DataOffset);
+ if (WaitForSingleObject(hStopEnumEvent, 0) == WAIT_OBJECT_0)
+ goto Quit;
- // Compute the event type
- EventTimeToSystemTime(pevlr->TimeWritten, &time);
+ /* Filter by event type */
+ if (!FilterByType(EventLogFilter, pEvlrTmp))
+ goto SkipEvent;
- // Get the username that generated the event
- GetEventUserName(pevlr, szUsername);
+ /* Get the event source name and filter it */
+ lpszSourceName = (LPWSTR)(pEvlrBuffer + sizeof(EVENTLOGRECORD));
+ if (!FilterByString(EventLogFilter->Sources, lpszSourceName))
+ goto SkipEvent;
- GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time, NULL, szLocalDate, MAX_PATH);
- GetTimeFormatW(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &time, NULL, szLocalTime, MAX_PATH);
+ /* Get the computer name and filter it */
+ lpszComputerName = (LPWSTR)(pEvlrBuffer + sizeof(EVENTLOGRECORD) + (wcslen(lpszSourceName) + 1) * sizeof(WCHAR));
+ if (!FilterByString(EventLogFilter->ComputerNames, lpszComputerName))
+ goto SkipEvent;
- GetEventType(pevlr->EventType, szEventTypeText);
- GetEventCategory(lpLogName, lpSourceName, pevlr, szCategory);
+ /* Compute the event time */
+ EventTimeToSystemTime(pEvlrTmp->TimeWritten, &time);
+ GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time, NULL, szLocalDate, ARRAYSIZE(szLocalDate));
+ GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &time, NULL, szLocalTime, ARRAYSIZE(szLocalTime));
- swprintf(szEventID, L"%u", (pevlr->EventID & 0xFFFF));
- swprintf(szCategoryID, L"%u", pevlr->EventCategory);
+ /* Get the username that generated the event, and filter it */
+ lpszUsername = GetEventUserName(pEvlrTmp, &pLastSid, szUsername) ? szUsername : szNoUsername;
+
+ if (!FilterByString(EventLogFilter->Users, lpszUsername))
+ goto SkipEvent;
+
+ // TODO: Filter by event ID and category
+ GetEventType(pEvlrTmp->EventType, szEventTypeText);
+
+ lpszCategoryName = GetEventCategory(EventLog->LogName, lpszSourceName, pEvlrTmp, szCategory) ? szCategory : szNoCategory;
+
+ StringCbPrintfW(szEventID, sizeof(szEventID), L"%u", (pEvlrTmp->EventID & 0xFFFF));
+ StringCbPrintfW(szCategoryID, sizeof(szCategoryID), L"%u", pEvlrTmp->EventCategory);
+
+ g_RecordPtrs[dwCurrentRecord] = HeapAlloc(hProcessHeap, 0, pEvlrTmp->Length);
+ RtlCopyMemory(g_RecordPtrs[dwCurrentRecord], pEvlrTmp, pEvlrTmp->Length);
lviEventItem.mask = LVIF_IMAGE | LVIF_TEXT | LVIF_PARAM;
lviEventItem.iItem = 0;
lviEventItem.iSubItem = 0;
- lviEventItem.lParam = (LPARAM)pevlr;
+ lviEventItem.lParam = (LPARAM)g_RecordPtrs[dwCurrentRecord];
lviEventItem.pszText = szEventTypeText;
- switch (pevlr->EventType)
- {
- case EVENTLOG_ERROR_TYPE:
- lviEventItem.iImage = 2;
- break;
+ switch (pEvlrTmp->EventType)
+ {
+ case EVENTLOG_SUCCESS:
+ case EVENTLOG_INFORMATION_TYPE:
+ lviEventItem.iImage = 0;
+ break;
+
+ case EVENTLOG_WARNING_TYPE:
+ lviEventItem.iImage = 1;
+ break;
+
+ case EVENTLOG_ERROR_TYPE:
+ lviEventItem.iImage = 2;
+ break;
+
+ case EVENTLOG_AUDIT_SUCCESS:
+ lviEventItem.iImage = 3;
+ break;
+
+ case EVENTLOG_AUDIT_FAILURE:
+ lviEventItem.iImage = 4;
+ break;
+ }
+
+ lviEventItem.iItem = ListView_InsertItem(hwndListView, &lviEventItem);
+
+ ListView_SetItemText(hwndListView, lviEventItem.iItem, 1, szLocalDate);
+ ListView_SetItemText(hwndListView, lviEventItem.iItem, 2, szLocalTime);
+ ListView_SetItemText(hwndListView, lviEventItem.iItem, 3, lpszSourceName);
+ ListView_SetItemText(hwndListView, lviEventItem.iItem, 4, lpszCategoryName);
+ ListView_SetItemText(hwndListView, lviEventItem.iItem, 5, szEventID);
+ ListView_SetItemText(hwndListView, lviEventItem.iItem, 6, lpszUsername);
+ ListView_SetItemText(hwndListView, lviEventItem.iItem, 7, lpszComputerName);
+
+SkipEvent:
+ pEvlrBuffer += pEvlrTmp->Length;
+ dwCurrentRecord++;
+ }
+ }
+
+Quit:
+
+ if (pEvlr)
+ HeapFree(hProcessHeap, 0, pEvlr);
+
+ /* Close the event log */
+ CloseEventLog(hEventLog);
+
+ } // end-for (LogIndex)
+
+ /* All events loaded */
+
+Cleanup:
+
+ ShowWindow(hwndStatusProgress, SW_HIDE);
+ SendMessageW(hwndListView, LVM_PROGRESS, 0, FALSE);
+
+ // FIXME: Use something else instead of EventLog->LogName !!
+
+ /*
+ * Use a different formatting, whether the event log filter holds
+ * only one log, or many logs (the latter case is WIP TODO!)
+ */
+ if (EventLogFilter->NumOfEventLogs <= 1)
+ {
+ StringCbPrintfW(szStatusText,
+ sizeof(szStatusText),
+ szStatusBarTemplate,
+ EventLog->LogName,
+ dwTotalRecords,
+ ListView_GetItemCount(hwndListView));
+ }
+ else
+ {
+ // TODO: Use a different title & implement filtering for multi-log filters !!
+ // (EventLogFilter->NumOfEventLogs > 1)
+ }
+
+ /* Update the status bar */
+ StatusBar_SetText(hwndStatus, 0, szStatusText);
+
+ /* Resume list view redraw */
+ SendMessageW(hwndListView, WM_SETREDRAW, TRUE, 0);
+
+ EventLogFilter_Release(EventLogFilter);
+
+ CloseHandle(hStopEnumEvent);
+ InterlockedExchangePointer((PVOID*)&hStopEnumEvent, NULL);
+
+ return 0;
+}
+
+/*
+ * The purpose of this thread is to serialize the creation of the events
+ * enumeration thread, since the Event Log Viewer currently only supports
+ * one view, one event list, one enumeration.
+ */
+static DWORD WINAPI
+StartStopEnumEventsThread(IN LPVOID lpParameter)
+{
+ HANDLE WaitHandles[2];
+ DWORD WaitResult;
+
+ WaitHandles[0] = hStartStopEnumEvent; // End-of-application event
+ WaitHandles[1] = hStartEnumEvent; // Command event
+
+ while (TRUE)
+ {
+ WaitResult = WaitForMultipleObjects(ARRAYSIZE(WaitHandles),
+ WaitHandles,
+ FALSE, // WaitAny
+ INFINITE);
+ switch (WaitResult)
+ {
+ case WAIT_OBJECT_0 + 0:
+ {
+ /* End-of-application event signaled, quit this thread */
+
+ /* Stop the previous enumeration */
+ if (hEnumEventsThread)
+ {
+ if (hStopEnumEvent)
+ {
+ SetEvent(hStopEnumEvent);
+ WaitForSingleObject(hEnumEventsThread, INFINITE);
+ // NOTE: The following is done by the enumeration thread just before terminating.
+ // hStopEnumEvent = NULL;
+ }
+
+ CloseHandle(hEnumEventsThread);
+ hEnumEventsThread = NULL;
+ }
+
+ /* Clear the list view and free the cached records */
+ ListView_DeleteAllItems(hwndListView);
+ FreeRecords();
+
+ /* Reset the active filter */
+ ActiveFilter = NULL;
+
+ return 0;
+ }
+
+ case WAIT_OBJECT_0 + 1:
+ {
+ /* Restart a new enumeration if needed */
+ PEVENTLOGFILTER EventLogFilter;
+
+ /* Stop the previous enumeration */
+ if (hEnumEventsThread)
+ {
+ if (hStopEnumEvent)
+ {
+ SetEvent(hStopEnumEvent);
+ WaitForSingleObject(hEnumEventsThread, INFINITE);
+ // NOTE: The following is done by the enumeration thread just before terminating.
+ // hStopEnumEvent = NULL;
+ }
+
+ CloseHandle(hEnumEventsThread);
+ hEnumEventsThread = NULL;
+ }
+
+ /* Clear the list view and free the cached records */
+ ListView_DeleteAllItems(hwndListView);
+ FreeRecords();
+
+ /* Reset the active filter */
+ ActiveFilter = NULL;
+
+ EventLogFilter = InterlockedExchangePointer((PVOID*)&EnumFilter, NULL);
+ if (!EventLogFilter)
+ break;
+
+ // Manual-reset event
+ hStopEnumEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
+ if (!hStopEnumEvent)
+ break;
+
+ hEnumEventsThread = CreateThread(NULL,
+ 0,
+ EnumEventsThread,
+ (LPVOID)EventLogFilter,
+ CREATE_SUSPENDED,
+ NULL);
+ if (!hEnumEventsThread)
+ {
+ CloseHandle(hStopEnumEvent);
+ hStopEnumEvent = NULL;
+ break;
+ }
+ // CloseHandle(hEnumEventsThread);
+ ResumeThread(hEnumEventsThread);
+
+ break;
+ }
+
+ default:
+ {
+ /* Unknown command, must never go there! */
+ return GetLastError();
+ }
+ }
+ }
+
+ return 0;
+}
+
+VOID
+EnumEvents(IN PEVENTLOGFILTER EventLogFilter)
+{
+ /* Signal the enumerator thread we want to enumerate events */
+ InterlockedExchangePointer((PVOID*)&EnumFilter, EventLogFilter);
+ SetEvent(hStartEnumEvent);
+ return;
+}
+
+
+PEVENTLOGFILTER
+GetSelectedFilter(OUT HTREEITEM* phti OPTIONAL)
+{
+ TVITEMEXW tvItemEx;
+ HTREEITEM hti;
+
+ if (phti)
+ *phti = NULL;
+
+ /* Get index of selected item */
+ hti = TreeView_GetSelection(hwndTreeView);
+ if (hti == NULL)
+ return NULL; // No filter
+
+ tvItemEx.mask = TVIF_PARAM;
+ tvItemEx.hItem = hti;
+
+ TreeView_GetItem(hwndTreeView, &tvItemEx);
+
+ if (phti)
+ *phti = tvItemEx.hItem;
+
+ return (PEVENTLOGFILTER)tvItemEx.lParam;
+}
+
+
+VOID
+OpenUserEventLogFile(IN LPCWSTR lpszFileName)
+{
+ WIN32_FIND_DATAW FindData;
+ HANDLE hFind;
+ PEVENTLOG EventLog;
+ PEVENTLOGFILTER EventLogFilter;
+ SIZE_T cchFileName;
+ HTREEITEM hItem = NULL;
+
+ /* Check whether the file actually exists */
+ hFind = FindFirstFileW(lpszFileName, &FindData);
+ if (hFind == INVALID_HANDLE_VALUE)
+ {
+ ShowWin32Error(GetLastError());
+ return;
+ }
+ FindClose(hFind);
+
+ /* Allocate a new event log entry */
+ EventLog = AllocEventLog(NULL, lpszFileName, FALSE);
+ if (EventLog == NULL)
+ {
+ ShowWin32Error(ERROR_NOT_ENOUGH_MEMORY);
+ return;
+ }
+
+ /* Allocate a new event log filter entry for this event log */
+ EventLogFilter = AllocEventLogFilter(// LogName,
+ TRUE, TRUE, TRUE, TRUE, TRUE,
+ NULL, NULL, NULL,
+ 1, &EventLog);
+ if (EventLogFilter == NULL)
+ {
+ ShowWin32Error(ERROR_NOT_ENOUGH_MEMORY);
+ EventLog_Free(EventLog);
+ return;
+ }
+
+ /* Add the event log and the filter into their lists */
+ InsertTailList(&EventLogList, &EventLog->ListEntry);
+ InsertTailList(&EventLogFilterList, &EventLogFilter->ListEntry);
+
+ /* Retrieve and cache the event log file */
+ cchFileName = wcslen(lpszFileName) + 1;
+ EventLog->FileName = HeapAlloc(GetProcessHeap(), 0, cchFileName * sizeof(WCHAR));
+ if (EventLog->FileName)
+ StringCchCopyW(EventLog->FileName, cchFileName, lpszFileName);
+
+ hItem = TreeViewAddItem(hwndTreeView, htiUserLogs,
+ (LPWSTR)lpszFileName,
+ 2, 3, (LPARAM)EventLogFilter);
+
+ /* Select the event log */
+ if (hItem)
+ {
+ // TreeView_Expand(hwndTreeView, htiUserLogs, TVE_EXPAND);
+ TreeView_SelectItem(hwndTreeView, hItem);
+ TreeView_EnsureVisible(hwndTreeView, hItem);
+ }
+ InvalidateRect(hwndTreeView, NULL, FALSE);
+ SetFocus(hwndTreeView);
+}
+
+VOID
+OpenUserEventLog(VOID)
+{
+ WCHAR szFileName[MAX_PATH];
+
+ ZeroMemory(szFileName, sizeof(szFileName));
+
+ sfn.lpstrFile = szFileName;
+ sfn.nMaxFile = ARRAYSIZE(szFileName);
+
+ if (!GetOpenFileNameW(&sfn))
+ return;
+ sfn.lpstrFile[sfn.nMaxFile-1] = UNICODE_NULL;
+
+ OpenUserEventLogFile(sfn.lpstrFile);
+}
+
+VOID
+SaveEventLog(IN PEVENTLOGFILTER EventLogFilter)
+{
+ PEVENTLOG EventLog;
+ HANDLE hEventLog;
+ WCHAR szFileName[MAX_PATH];
+
+ /* Bail out if there is no available filter */
+ if (!EventLogFilter)
+ return;
+
+ ZeroMemory(szFileName, sizeof(szFileName));
+
+ sfn.lpstrFile = szFileName;
+ sfn.nMaxFile = ARRAYSIZE(szFileName);
+
+ if (!GetSaveFileNameW(&sfn))
+ return;
+
+ EventLogFilter_AddRef(EventLogFilter);
+
+ EventLog = EventLogFilter->EventLogs[0];
+ hEventLog = OpenEventLogW(EventLog->ComputerName, EventLog->LogName);
+
+ EventLogFilter_Release(EventLogFilter);
+
+ if (!hEventLog)
+ {
+ ShowWin32Error(GetLastError());
+ return;
+ }
+
+ if (!BackupEventLogW(hEventLog, szFileName))
+ ShowWin32Error(GetLastError());
+
+ CloseEventLog(hEventLog);
+}
+
+VOID
+CloseUserEventLog(IN PEVENTLOGFILTER EventLogFilter, IN HTREEITEM hti)
+{
+ /* Bail out if there is no available filter */
+ if (!EventLogFilter)
+ return;
+
+ if (InterlockedCompareExchangePointer((PVOID*)&ActiveFilter, NULL, NULL) == EventLogFilter)
+ {
+ /* Signal the enumerator thread we want to stop enumerating events */
+ // EnumEvents(NULL);
+ InterlockedExchangePointer((PVOID*)&EnumFilter, NULL);
+ SetEvent(hStartEnumEvent);
+ }
- case EVENTLOG_AUDIT_FAILURE:
- lviEventItem.iImage = 2;
- break;
+ /*
+ * The deletion of the item automatically triggers a TVN_SELCHANGED
+ * notification, that will reset the ActiveFilter (in case the item
+ * selected is a filter). Otherwise we reset it there.
+ */
+ TreeView_DeleteItem(hwndTreeView, hti);
+
+ /* Remove the filter from the list */
+ RemoveEntryList(&EventLogFilter->ListEntry);
+ EventLogFilter_Release(EventLogFilter);
+
+ // /* Select the default event log */
+ // // TreeView_Expand(hwndTreeView, htiUserLogs, TVE_EXPAND);
+ // TreeView_SelectItem(hwndTreeView, hItem);
+ // TreeView_EnsureVisible(hwndTreeView, hItem);
+ InvalidateRect(hwndTreeView, NULL, FALSE);
+ SetFocus(hwndTreeView);
+}
- case EVENTLOG_WARNING_TYPE:
- lviEventItem.iImage = 1;
- break;
- case EVENTLOG_INFORMATION_TYPE:
- lviEventItem.iImage = 0;
- break;
+BOOL
+ClearEvents(IN PEVENTLOGFILTER EventLogFilter)
+{
+ BOOL Success;
+ PEVENTLOG EventLog;
+ HANDLE hEventLog;
+ WCHAR szFileName[MAX_PATH];
+ WCHAR szMessage[MAX_LOADSTRING];
- case EVENTLOG_AUDIT_SUCCESS:
- lviEventItem.iImage = 0;
- break;
+ /* Bail out if there is no available filter */
+ if (!EventLogFilter)
+ return FALSE;
- case EVENTLOG_SUCCESS:
- lviEventItem.iImage = 0;
- break;
- }
+ ZeroMemory(szFileName, sizeof(szFileName));
+ ZeroMemory(szMessage, sizeof(szMessage));
- lviEventItem.iItem = ListView_InsertItem(hwndListView, &lviEventItem);
+ LoadStringW(hInst, IDS_CLEAREVENTS_MSG, szMessage, ARRAYSIZE(szMessage));
- ListView_SetItemText(hwndListView, lviEventItem.iItem, 1, szLocalDate);
- ListView_SetItemText(hwndListView, lviEventItem.iItem, 2, szLocalTime);
- ListView_SetItemText(hwndListView, lviEventItem.iItem, 3, lpSourceName);
- ListView_SetItemText(hwndListView, lviEventItem.iItem, 4, szCategory);
- ListView_SetItemText(hwndListView, lviEventItem.iItem, 5, szEventID);
- ListView_SetItemText(hwndListView, lviEventItem.iItem, 6, szUsername); //User
- ListView_SetItemText(hwndListView, lviEventItem.iItem, 7, lpComputerName); //Computer
- MultiByteToWideChar(CP_ACP,
- 0,
- lpData,
- pevlr->DataLength,
- szData,
- MAX_PATH);
- ListView_SetItemText(hwndListView, lviEventItem.iItem, 8, szData); //Event Text
-
- dwRead -= pevlr->Length;
- pevlr = (EVENTLOGRECORD *)((LPBYTE) pevlr + pevlr->Length);
- }
+ sfn.lpstrFile = szFileName;
+ sfn.nMaxFile = ARRAYSIZE(szFileName);
- dwRecordsToRead--;
- dwCurrentRecord++;
- }
+ switch (MessageBoxW(hwndMainWindow, szMessage, szTitle, MB_YESNOCANCEL | MB_ICONINFORMATION))
+ {
+ case IDCANCEL:
+ return FALSE;
- // All events loaded
- EndDialog(hwndDlg, 0);
+ case IDNO:
+ sfn.lpstrFile = NULL;
+ break;
+ case IDYES:
+ if (!GetSaveFileNameW(&sfn))
+ return FALSE;
+ break;
+ }
- i = swprintf(szWindowTitle, L"%s - %s Log on \\\\", szTitle, lpLogName); /* i = number of characters written */
- /* lpComputerName can be NULL here if no records was read */
- dwMaxLength = sizeof(szWindowTitle) / sizeof(WCHAR) - i;
- if(!lpComputerName)
- GetComputerNameW(szWindowTitle+i, &dwMaxLength);
- else
- _snwprintf(szWindowTitle+i, dwMaxLength, L"%s", lpComputerName);
+ EventLogFilter_AddRef(EventLogFilter);
- swprintf(szStatusText, L"%s has %d event(s)", lpLogName, dwTotalRecords);
+ EventLog = EventLogFilter->EventLogs[0];
+ hEventLog = OpenEventLogW(EventLog->ComputerName, EventLog->LogName);
- // Update the status bar
- SendMessageW(hwndStatus, SB_SETTEXT, (WPARAM)0, (LPARAM)szStatusText);
+ EventLogFilter_Release(EventLogFilter);
- // Set the window title
- SetWindowTextW(hwndMainWindow, szWindowTitle);
+ if (!hEventLog)
+ {
+ ShowWin32Error(GetLastError());
+ return FALSE;
+ }
- // Resume list view redraw
- SendMessageW(hwndListView, WM_SETREDRAW, TRUE, 0);
+ Success = ClearEventLogW(hEventLog, sfn.lpstrFile);
+ if (!Success)
+ ShowWin32Error(GetLastError());
- // Close the event log.
CloseEventLog(hEventLog);
-
- return TRUE;
+ return Success;
}
VOID
-Refresh(VOID)
+Refresh(IN PEVENTLOGFILTER EventLogFilter)
{
- QueryEventMessages(lpComputerName,
- lpSourceLogName);
+ /* Bail out if there is no available filter */
+ if (!EventLogFilter)
+ return;
+
+ /* Reenumerate the events through the filter */
+ EnumEvents(EventLogFilter);
}
-//
-// FUNCTION: MyRegisterClass()
-//
-// PURPOSE: Registers the window class.
-//
-// COMMENTS:
-//
-// This function and its usage are only necessary if you want this code
-// to be compatible with Win32 systems prior to the 'RegisterClassEx'
-// function that was added to Windows 95. It is important to call this function
-// so that the application will get 'well formed' small icons associated
-// with it.
-//
ATOM
MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
- wcex.cbSize = sizeof(WNDCLASSEX);
-
+ wcex.cbSize = sizeof(wcex);
wcex.style = 0;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
- wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_EVENTVWR));
- wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
- wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
- wcex.lpszMenuName = MAKEINTRESOURCE(IDC_EVENTVWR);
- wcex.lpszClassName = szWindowClass;
- wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
+ wcex.hIcon = LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_EVENTVWR));
+ wcex.hCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_ARROW));
+ wcex.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); // COLOR_WINDOW + 1
+ wcex.lpszMenuName = MAKEINTRESOURCEW(IDM_EVENTVWR);
+ wcex.lpszClassName = EVENTVWR_WNDCLASS;
+ wcex.hIconSm = (HICON)LoadImageW(hInstance,
+ MAKEINTRESOURCEW(IDI_EVENTVWR),
+ IMAGE_ICON,
+ 16,
+ 16,
+ LR_SHARED);
return RegisterClassExW(&wcex);
}
-//
-// FUNCTION: InitInstance(HINSTANCE, int)
-//
-// PURPOSE: Saves instance handle and creates main window
-//
-// COMMENTS:
-//
-// In this function, we save the instance handle in a global variable and
-// create and display the main program window.
-//
BOOL
-InitInstance(HINSTANCE hInstance,
- int nCmdShow)
+GetDisplayNameFileAndID(IN LPCWSTR lpLogName,
+ OUT PWCHAR lpModuleName, // TODO: Add IN DWORD BufLen
+ OUT PDWORD pdwMessageID)
+{
+ BOOL Success = FALSE;
+ LONG Result;
+ HKEY hLogKey;
+ WCHAR *KeyPath;
+ SIZE_T cbKeyPath;
+ DWORD dwType, cbData;
+ DWORD dwMessageID = 0;
+ WCHAR szModuleName[MAX_PATH];
+
+ /* Use a default value for the message ID */
+ *pdwMessageID = 0;
+
+ cbKeyPath = (wcslen(EVENTLOG_BASE_KEY) + wcslen(lpLogName) + 1) * sizeof(WCHAR);
+ KeyPath = HeapAlloc(GetProcessHeap(), 0, cbKeyPath);
+ if (!KeyPath)
+ return FALSE;
+
+ StringCbCopyW(KeyPath, cbKeyPath, EVENTLOG_BASE_KEY);
+ StringCbCatW(KeyPath, cbKeyPath, lpLogName);
+
+ Result = RegOpenKeyExW(hkMachine, KeyPath, 0, KEY_QUERY_VALUE, &hLogKey);
+ HeapFree(GetProcessHeap(), 0, KeyPath);
+ if (Result != ERROR_SUCCESS)
+ return FALSE;
+
+ cbData = sizeof(szModuleName);
+ Result = RegQueryValueExW(hLogKey,
+ L"DisplayNameFile",
+ NULL,
+ &dwType,
+ (LPBYTE)szModuleName,
+ &cbData);
+ if ((Result != ERROR_SUCCESS) || (dwType != REG_EXPAND_SZ && dwType != REG_SZ))
+ {
+ szModuleName[0] = UNICODE_NULL;
+ }
+ else
+ {
+ /* NULL-terminate the string and expand it */
+ szModuleName[cbData / sizeof(WCHAR) - 1] = UNICODE_NULL;
+ GetExpandedFilePathName(lpComputerName, szModuleName, lpModuleName, ARRAYSIZE(szModuleName));
+ Success = TRUE;
+ }
+
+ /*
+ * If we have a 'DisplayNameFile', query for 'DisplayNameID';
+ * otherwise it's not really useful. 'DisplayNameID' is optional.
+ */
+ if (Success)
+ {
+ cbData = sizeof(dwMessageID);
+ Result = RegQueryValueExW(hLogKey,
+ L"DisplayNameID",
+ NULL,
+ &dwType,
+ (LPBYTE)&dwMessageID,
+ &cbData);
+ if ((Result != ERROR_SUCCESS) || (dwType != REG_DWORD))
+ dwMessageID = 0;
+
+ *pdwMessageID = dwMessageID;
+ }
+
+ RegCloseKey(hLogKey);
+
+ return Success;
+}
+
+
+VOID
+BuildLogListAndFilterList(IN LPCWSTR lpComputerName)
+{
+ LONG Result;
+ HKEY hEventLogKey, hLogKey;
+ DWORD dwNumLogs = 0;
+ DWORD dwIndex, dwMaxKeyLength;
+ DWORD dwType;
+ PEVENTLOG EventLog;
+ PEVENTLOGFILTER EventLogFilter;
+ LPWSTR LogName = NULL;
+ WCHAR szModuleName[MAX_PATH];
+ DWORD lpcName;
+ DWORD dwMessageID;
+ LPWSTR lpDisplayName;
+ HTREEITEM hRootNode = NULL, hItem = NULL, hItemDefault = NULL;
+
+ if (hkMachine && hkMachine != HKEY_LOCAL_MACHINE)
+ {
+ /* We are connected to some other computer, close the old connection */
+ RegCloseKey(hkMachine);
+ hkMachine = NULL;
+ }
+ if (!lpComputerName || !*lpComputerName)
+ {
+ /* Use the local computer registry */
+ hkMachine = HKEY_LOCAL_MACHINE;
+ }
+ else
+ {
+ /* Connect to the remote computer registry */
+ Result = RegConnectRegistry(lpComputerName, HKEY_LOCAL_MACHINE, &hkMachine);
+ if (Result != ERROR_SUCCESS)
+ {
+ /* Connection failed, display a message and bail out */
+ hkMachine = NULL;
+ ShowWin32Error(GetLastError());
+ return;
+ }
+ }
+
+ /* Open the EventLog key */
+ Result = RegOpenKeyExW(hkMachine, EVENTLOG_BASE_KEY, 0, KEY_READ, &hEventLogKey);
+ if (Result != ERROR_SUCCESS)
+ {
+ return;
+ }
+
+ /* Retrieve the number of event logs enumerated as registry keys */
+ Result = RegQueryInfoKeyW(hEventLogKey, NULL, NULL, NULL, &dwNumLogs, &dwMaxKeyLength,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ if (Result != ERROR_SUCCESS)
+ {
+ goto Quit;
+ }
+ if (!dwNumLogs)
+ goto Quit;
+
+ /* Take the NULL terminator into account */
+ ++dwMaxKeyLength;
+
+ /* Allocate the temporary buffer */
+ LogName = HeapAlloc(GetProcessHeap(), 0, dwMaxKeyLength * sizeof(WCHAR));
+ if (!LogName)
+ goto Quit;
+
+ /* Enumerate and retrieve each event log name */
+ for (dwIndex = 0; dwIndex < dwNumLogs; dwIndex++)
+ {
+ lpcName = dwMaxKeyLength;
+ Result = RegEnumKeyExW(hEventLogKey, dwIndex, LogName, &lpcName, NULL, NULL, NULL, NULL);
+ if (Result != ERROR_SUCCESS)
+ continue;
+
+ /* Take the NULL terminator into account */
+ ++lpcName;
+
+ /* Allocate a new event log entry */
+ EventLog = AllocEventLog(lpComputerName, LogName, TRUE);
+ if (EventLog == NULL)
+ continue;
+
+ /* Allocate a new event log filter entry for this event log */
+ EventLogFilter = AllocEventLogFilter(// LogName,
+ TRUE, TRUE, TRUE, TRUE, TRUE,
+ NULL, NULL, NULL,
+ 1, &EventLog);
+ if (EventLogFilter == NULL)
+ {
+ EventLog_Free(EventLog);
+ continue;
+ }
+
+ /* Add the event log and the filter into their lists */
+ InsertTailList(&EventLogList, &EventLog->ListEntry);
+ InsertTailList(&EventLogFilterList, &EventLogFilter->ListEntry);
+
+ EventLog->FileName = NULL;
+
+ /* Retrieve and cache the event log file */
+ Result = RegOpenKeyExW(hEventLogKey,
+ LogName,
+ 0,
+ KEY_QUERY_VALUE,
+ &hLogKey);
+ if (Result == ERROR_SUCCESS)
+ {
+ lpcName = 0;
+ Result = RegQueryValueExW(hLogKey,
+ L"File",
+ NULL,
+ &dwType,
+ NULL,
+ &lpcName);
+ if ((Result != ERROR_SUCCESS) || (dwType != REG_EXPAND_SZ && dwType != REG_SZ))
+ {
+ // Windows' EventLog uses some kind of default value, we do not.
+ EventLog->FileName = NULL;
+ }
+ else
+ {
+ lpcName = ROUND_DOWN(lpcName, sizeof(WCHAR));
+ EventLog->FileName = HeapAlloc(GetProcessHeap(), 0, lpcName);
+ if (EventLog->FileName)
+ {
+ Result = RegQueryValueExW(hLogKey,
+ L"File",
+ NULL,
+ &dwType,
+ (LPBYTE)EventLog->FileName,
+ &lpcName);
+ if (Result != ERROR_SUCCESS)
+ {
+ HeapFree(GetProcessHeap(), 0, EventLog->FileName);
+ EventLog->FileName = NULL;
+ }
+ else
+ {
+ EventLog->FileName[lpcName / sizeof(WCHAR) - 1] = UNICODE_NULL;
+ }
+ }
+ }
+
+ RegCloseKey(hLogKey);
+ }
+
+ /* Get the display name for the event log */
+ lpDisplayName = NULL;
+
+ ZeroMemory(szModuleName, sizeof(szModuleName));
+ if (GetDisplayNameFileAndID(LogName, szModuleName, &dwMessageID))
+ {
+ /* Retrieve the message string without appending extra newlines */
+ lpDisplayName =
+ GetMessageStringFromDll(szModuleName,
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
+ FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
+ dwMessageID,
+ 0,
+ NULL);
+ }
+
+ /*
+ * Select the correct tree root node, whether the log is a System
+ * or an Application log. Default to Application log otherwise.
+ */
+ hRootNode = htiAppLogs;
+ for (lpcName = 0; lpcName < ARRAYSIZE(SystemLogs); ++lpcName)
+ {
+ /* Check whether the log name is part of the system logs */
+ if (wcsicmp(LogName, SystemLogs[lpcName]) == 0)
+ {
+ hRootNode = htiSystemLogs;
+ break;
+ }
+ }
+
+ hItem = TreeViewAddItem(hwndTreeView, hRootNode,
+ (lpDisplayName ? lpDisplayName : LogName),
+ 2, 3, (LPARAM)EventLogFilter);
+
+ /* Try to get the default event log: "Application" */
+ if ((hItemDefault == NULL) && (wcsicmp(LogName, SystemLogs[0]) == 0))
+ {
+ hItemDefault = hItem;
+ }
+
+ /* Free the buffer allocated by FormatMessage */
+ if (lpDisplayName)
+ LocalFree(lpDisplayName);
+ }
+
+ HeapFree(GetProcessHeap(), 0, LogName);
+
+Quit:
+ RegCloseKey(hEventLogKey);
+
+ /* Select the default event log */
+ if (hItemDefault)
+ {
+ // TreeView_Expand(hwndTreeView, hRootNode, TVE_EXPAND);
+ TreeView_SelectItem(hwndTreeView, hItemDefault);
+ TreeView_EnsureVisible(hwndTreeView, hItemDefault);
+ }
+ InvalidateRect(hwndTreeView, NULL, FALSE);
+ SetFocus(hwndTreeView);
+
+ return;
+}
+
+VOID
+FreeLogList(VOID)
+{
+ PLIST_ENTRY Entry;
+ PEVENTLOG EventLog;
+
+ while (!IsListEmpty(&EventLogList))
+ {
+ Entry = RemoveHeadList(&EventLogList);
+ EventLog = (PEVENTLOG)CONTAINING_RECORD(Entry, EVENTLOG, ListEntry);
+ EventLog_Free(EventLog);
+ }
+
+ return;
+}
+
+VOID
+FreeLogFilterList(VOID)
+{
+ PLIST_ENTRY Entry;
+ PEVENTLOGFILTER EventLogFilter;
+
+ while (!IsListEmpty(&EventLogFilterList))
+ {
+ Entry = RemoveHeadList(&EventLogFilterList);
+ EventLogFilter = (PEVENTLOGFILTER)CONTAINING_RECORD(Entry, EVENTLOGFILTER, ListEntry);
+ EventLogFilter_Free(EventLogFilter);
+ }
+
+ ActiveFilter = NULL;
+
+ return;
+}
+
+BOOL
+InitInstance(HINSTANCE hInstance)
{
+ RECT rcClient, rs;
+ LONG StatusHeight;
HIMAGELIST hSmall;
LVCOLUMNW lvc = {0};
WCHAR szTemp[256];
- hInst = hInstance; // Store instance handle in our global variable
-
- hwndMainWindow = CreateWindowW(szWindowClass,
- szTitle,
- WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
- CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
- NULL,
- NULL,
- hInstance,
- NULL);
+ /* Create the main window */
+ rs = Settings.wpPos.rcNormalPosition;
+ hwndMainWindow = CreateWindowW(EVENTVWR_WNDCLASS,
+ szTitle,
+ WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
+ rs.left, rs.top,
+ (rs.right != CW_USEDEFAULT && rs.left != CW_USEDEFAULT) ? rs.right - rs.left : CW_USEDEFAULT,
+ (rs.bottom != CW_USEDEFAULT && rs.top != CW_USEDEFAULT) ? rs.bottom - rs.top : CW_USEDEFAULT,
+ NULL,
+ NULL,
+ hInstance,
+ NULL);
if (!hwndMainWindow)
- {
return FALSE;
- }
+ /* Create the status bar */
hwndStatus = CreateWindowExW(0, // no extended styles
- STATUSCLASSNAMEW, // status bar
- L"Done.", // no text
- WS_CHILD | WS_BORDER | WS_VISIBLE, // styles
- 0, 0, 0, 0, // x, y, cx, cy
- hwndMainWindow, // parent window
- (HMENU)100, // window ID
- hInstance, // instance
- NULL); // window data
-
- // Create our listview child window. Note that I use WS_EX_CLIENTEDGE
- // and WS_BORDER to create the normal "sunken" look. Also note that
- // LVS_EX_ styles cannot be set in CreateWindowEx().
- hwndListView = CreateWindowExW(WS_EX_CLIENTEDGE,
- WC_LISTVIEWW,
- L"",
- LVS_SHOWSELALWAYS | WS_CHILD | WS_VISIBLE | LVS_REPORT,
- 0,
- 0,
- 243,
- 200,
- hwndMainWindow,
- NULL,
- hInstance,
- NULL);
-
- // After the ListView is created, we can add extended list view styles.
- (void)ListView_SetExtendedListViewStyle (hwndListView, LVS_EX_FULLROWSELECT);
+ STATUSCLASSNAMEW, // status bar
+ L"", // no text
+ WS_CHILD | WS_VISIBLE | CCS_BOTTOM | SBARS_SIZEGRIP, // styles
+ 0, 0, 0, 0, // x, y, cx, cy
+ hwndMainWindow, // parent window
+ (HMENU)100, // window ID
+ hInstance, // instance
+ NULL); // window data
+
+ GetClientRect(hwndMainWindow, &rcClient);
+ GetWindowRect(hwndStatus, &rs);
+ StatusHeight = rs.bottom - rs.top;
+
+ /* Create a progress bar in the status bar (hidden by default) */
+ StatusBar_GetItemRect(hwndStatus, 0, &rs);
+ hwndStatusProgress = CreateWindowExW(0, // no extended styles
+ PROGRESS_CLASSW, // status bar
+ NULL, // no text
+ WS_CHILD | PBS_SMOOTH, // styles
+ rs.left, rs.top, // x, y
+ rs.right - rs.left, rs.bottom - rs.top, // cx, cy
+ hwndStatus, // parent window
+ NULL, // window ID
+ hInstance, // instance
+ NULL); // window data
+ /* Remove its static edge */
+ SetWindowLongPtrW(hwndStatusProgress, GWL_EXSTYLE,
+ GetWindowLongPtrW(hwndStatusProgress, GWL_EXSTYLE) & ~WS_EX_STATICEDGE);
+ ProgressBar_SetStep(hwndStatusProgress, 1);
+
+ /* Initialize the splitter default positions */
+ nVSplitPos = Settings.nVSplitPos;
+ nHSplitPos = Settings.nHSplitPos;
+
+ /* Create the TreeView */
+ hwndTreeView = CreateWindowExW(WS_EX_CLIENTEDGE,
+ WC_TREEVIEWW,
+ NULL,
+ // WS_CHILD | WS_VISIBLE | TVS_HASLINES | TVS_SHOWSELALWAYS,
+ WS_CHILD | WS_VISIBLE | /* WS_TABSTOP | */ TVS_HASLINES | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_EDITLABELS | TVS_SHOWSELALWAYS,
+ 0, 0,
+ nVSplitPos - SPLIT_WIDTH/2,
+ (rcClient.bottom - rcClient.top) - StatusHeight,
+ hwndMainWindow,
+ NULL,
+ hInstance,
+ NULL);
+
+ /* Create the ImageList */
+ hSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
+ GetSystemMetrics(SM_CYSMICON),
+ ILC_COLOR32 | ILC_MASK, // ILC_COLOR24
+ 1, 1);
+
+ /* Add event type icons to the ImageList: closed/opened folder, event log (normal/viewed) */
+ ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_CLOSED_CATEGORY)));
+ ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_OPENED_CATEGORY)));
+ ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_EVENTLOG)));
+ ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_EVENTVWR)));
+
+ /* Assign the ImageList to the Tree View */
+ TreeView_SetImageList(hwndTreeView, hSmall, TVSIL_NORMAL);
+
+ /* Add the event logs nodes */
+ // "System Logs"
+ LoadStringW(hInstance, IDS_EVENTLOG_SYSTEM, szTemp, ARRAYSIZE(szTemp));
+ htiSystemLogs = TreeViewAddItem(hwndTreeView, NULL, szTemp, 0, 1, (LPARAM)NULL);
+ // "Application Logs"
+ LoadStringW(hInstance, IDS_EVENTLOG_APP, szTemp, ARRAYSIZE(szTemp));
+ htiAppLogs = TreeViewAddItem(hwndTreeView, NULL, szTemp, 0, 1, (LPARAM)NULL);
+ // "User Logs"
+ LoadStringW(hInstance, IDS_EVENTLOG_USER, szTemp, ARRAYSIZE(szTemp));
+ htiUserLogs = TreeViewAddItem(hwndTreeView, NULL, szTemp, 0, 1, (LPARAM)NULL);
+
+ /* Create the Event details pane (optional) */
+ hwndEventDetails = CreateEventDetailsCtrl(hInst, hwndMainWindow, (LPARAM)NULL);
+ if (hwndEventDetails)
+ {
+ SetWindowLongPtrW(hwndEventDetails, GWL_EXSTYLE,
+ GetWindowLongPtrW(hwndEventDetails, GWL_EXSTYLE) | WS_EX_CLIENTEDGE);
+ SetWindowPos(hwndEventDetails, NULL,
+ nVSplitPos + SPLIT_WIDTH/2,
+ nHSplitPos + SPLIT_WIDTH/2,
+ (rcClient.right - rcClient.left) - nVSplitPos - SPLIT_WIDTH/2,
+ (rcClient.bottom - rcClient.top) - nHSplitPos - SPLIT_WIDTH/2 - StatusHeight,
+ SWP_NOZORDER | SWP_NOACTIVATE | (Settings.bShowDetailsPane ? SWP_SHOWWINDOW : SWP_HIDEWINDOW));
+ }
- // Create the ImageList
+ /* Create the ListView */
+ hwndListView = CreateWindowExW(WS_EX_CLIENTEDGE,
+ WC_LISTVIEWW,
+ NULL,
+ WS_CHILD | WS_VISIBLE | LVS_SHOWSELALWAYS | LVS_REPORT,
+ nVSplitPos + SPLIT_WIDTH/2,
+ 0,
+ (rcClient.right - rcClient.left) - nVSplitPos - SPLIT_WIDTH/2,
+ hwndEventDetails && Settings.bShowDetailsPane
+ ? nHSplitPos - SPLIT_WIDTH/2
+ : (rcClient.bottom - rcClient.top) - StatusHeight,
+ hwndMainWindow,
+ NULL,
+ hInstance,
+ NULL);
+
+ /* Add the extended ListView styles */
+ ListView_SetExtendedListViewStyle(hwndListView, LVS_EX_HEADERDRAGDROP | LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP | (Settings.bShowGrid ? LVS_EX_GRIDLINES : 0));
+
+ /* Create the ImageList */
hSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
GetSystemMetrics(SM_CYSMICON),
- ILC_MASK,
- 1,
- 1);
+ ILC_COLOR32 | ILC_MASK, // ILC_COLOR24
+ 1, 1);
- // Add event type icons to ImageList
- ImageList_AddIcon (hSmall, LoadIcon(hInstance, MAKEINTRESOURCE(IDI_INFORMATIONICON)));
- ImageList_AddIcon (hSmall, LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WARNINGICON)));
- ImageList_AddIcon (hSmall, LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ERRORICON)));
+ /* Add event type icons to the ImageList */
+ ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_INFORMATIONICON)));
+ ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_WARNINGICON)));
+ ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_ERRORICON)));
+ ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_AUDITSUCCESSICON)));
+ ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_AUDITFAILUREICON)));
- // Assign ImageList to List View
- (void)ListView_SetImageList (hwndListView, hSmall, LVSIL_SMALL);
+ /* Assign the ImageList to the List View */
+ ListView_SetImageList(hwndListView, hSmall, LVSIL_SMALL);
- // Now set up the listview with its columns.
+ /* Now set up the listview with its columns */
lvc.mask = LVCF_TEXT | LVCF_WIDTH;
lvc.cx = 90;
LoadStringW(hInstance,
IDS_COLUMNTYPE,
szTemp,
- sizeof(szTemp) / sizeof(WCHAR));
+ ARRAYSIZE(szTemp));
lvc.pszText = szTemp;
- (void)ListView_InsertColumn(hwndListView, 0, &lvc);
+ ListView_InsertColumn(hwndListView, 0, &lvc);
lvc.cx = 70;
LoadStringW(hInstance,
IDS_COLUMNDATE,
szTemp,
- sizeof(szTemp) / sizeof(WCHAR));
+ ARRAYSIZE(szTemp));
lvc.pszText = szTemp;
- (void)ListView_InsertColumn(hwndListView, 1, &lvc);
+ ListView_InsertColumn(hwndListView, 1, &lvc);
lvc.cx = 70;
LoadStringW(hInstance,
IDS_COLUMNTIME,
szTemp,
- sizeof(szTemp) / sizeof(WCHAR));
+ ARRAYSIZE(szTemp));
lvc.pszText = szTemp;
- (void)ListView_InsertColumn(hwndListView, 2, &lvc);
+ ListView_InsertColumn(hwndListView, 2, &lvc);
lvc.cx = 150;
LoadStringW(hInstance,
IDS_COLUMNSOURCE,
szTemp,
- sizeof(szTemp) / sizeof(WCHAR));
+ ARRAYSIZE(szTemp));
lvc.pszText = szTemp;
- (void)ListView_InsertColumn(hwndListView, 3, &lvc);
+ ListView_InsertColumn(hwndListView, 3, &lvc);
lvc.cx = 100;
LoadStringW(hInstance,
IDS_COLUMNCATEGORY,
szTemp,
- sizeof(szTemp) / sizeof(WCHAR));
+ ARRAYSIZE(szTemp));
lvc.pszText = szTemp;
- (void)ListView_InsertColumn(hwndListView, 4, &lvc);
+ ListView_InsertColumn(hwndListView, 4, &lvc);
lvc.cx = 60;
LoadStringW(hInstance,
IDS_COLUMNEVENT,
szTemp,
- sizeof(szTemp) / sizeof(WCHAR));
+ ARRAYSIZE(szTemp));
lvc.pszText = szTemp;
- (void)ListView_InsertColumn(hwndListView, 5, &lvc);
+ ListView_InsertColumn(hwndListView, 5, &lvc);
lvc.cx = 120;
LoadStringW(hInstance,
IDS_COLUMNUSER,
szTemp,
- sizeof(szTemp) / sizeof(WCHAR));
+ ARRAYSIZE(szTemp));
lvc.pszText = szTemp;
- (void)ListView_InsertColumn(hwndListView, 6, &lvc);
+ ListView_InsertColumn(hwndListView, 6, &lvc);
lvc.cx = 100;
LoadStringW(hInstance,
IDS_COLUMNCOMPUTER,
szTemp,
- sizeof(szTemp) / sizeof(WCHAR));
+ ARRAYSIZE(szTemp));
lvc.pszText = szTemp;
- (void)ListView_InsertColumn(hwndListView, 7, &lvc);
+ ListView_InsertColumn(hwndListView, 7, &lvc);
- lvc.cx = 0;
- LoadStringW(hInstance,
- IDS_COLUMNEVENTDATA,
- szTemp,
- sizeof(szTemp) / sizeof(WCHAR));
- lvc.pszText = szTemp;
- (void)ListView_InsertColumn(hwndListView, 8, &lvc);
+ /* Initialize the save Dialog */
+ ZeroMemory(&sfn, sizeof(sfn));
+ ZeroMemory(szSaveFilter, sizeof(szSaveFilter));
- ShowWindow(hwndMainWindow, nCmdShow);
- UpdateWindow(hwndMainWindow);
+ LoadStringW(hInst, IDS_SAVE_FILTER, szSaveFilter, ARRAYSIZE(szSaveFilter));
- QueryEventMessages(lpComputerName, // Use the local computer.
- EVENT_SOURCE_APPLICATION); // The event log category
+ sfn.lStructSize = sizeof(sfn);
+ sfn.hwndOwner = hwndMainWindow;
+ sfn.hInstance = hInstance;
+ sfn.lpstrFilter = szSaveFilter;
+ sfn.lpstrInitialDir = NULL;
+ sfn.Flags = OFN_HIDEREADONLY | OFN_SHAREAWARE;
+ sfn.lpstrDefExt = NULL;
+
+ ShowWindow(hwndMainWindow, Settings.wpPos.showCmd);
+ UpdateWindow(hwndMainWindow);
return TRUE;
}
+VOID ResizeWnd(INT cx, INT cy)
+{
+ RECT rs;
+ LONG StatusHeight;
+ LONG_PTR dwExStyle;
+ HDWP hdwp;
+
+ /* Resize the status bar -- now done in WM_SIZE */
+ // SendMessageW(hwndStatus, WM_SIZE, 0, 0);
+ GetWindowRect(hwndStatus, &rs);
+ StatusHeight = rs.bottom - rs.top;
+
+ /*
+ * Move the progress bar -- Take into account for extra size due to the static edge
+ * (AdjustWindowRectEx() does not seem to work for the progress bar).
+ */
+ StatusBar_GetItemRect(hwndStatus, 0, &rs);
+ dwExStyle = GetWindowLongPtrW(hwndStatusProgress, GWL_EXSTYLE);
+ SetWindowLongPtrW(hwndStatusProgress, GWL_EXSTYLE, dwExStyle | WS_EX_STATICEDGE);
+ MoveWindow(hwndStatusProgress,
+ rs.left, rs.top, rs.right - rs.left, rs.bottom - rs.top,
+ IsWindowVisible(hwndStatusProgress) ? TRUE : FALSE);
+ SetWindowLongPtrW(hwndStatusProgress, GWL_EXSTYLE, dwExStyle);
+
+ /*
+ * TODO: Adjust the splitter positions:
+ * - Vertical splitter (1) : fixed position from the left window side.
+ * - Horizontal splitter (2): fixed position from the bottom window side.
+ */
+ nVSplitPos = min(max(nVSplitPos, SPLIT_WIDTH/2), cx - SPLIT_WIDTH/2); // OK
+ nHSplitPos = min(max(nHSplitPos, SPLIT_WIDTH/2), cy - SPLIT_WIDTH/2 - StatusHeight); // FIXME!
+
+ hdwp = BeginDeferWindowPos(3);
+
+ if (hdwp)
+ hdwp = DeferWindowPos(hdwp,
+ hwndTreeView,
+ HWND_TOP,
+ 0, 0,
+ nVSplitPos - SPLIT_WIDTH/2,
+ cy - StatusHeight,
+ SWP_NOZORDER | SWP_NOACTIVATE);
+
+ if (hdwp)
+ hdwp = DeferWindowPos(hdwp,
+ hwndListView,
+ HWND_TOP,
+ nVSplitPos + SPLIT_WIDTH/2, 0,
+ cx - nVSplitPos - SPLIT_WIDTH/2,
+ hwndEventDetails && Settings.bShowDetailsPane
+ ? nHSplitPos - SPLIT_WIDTH/2
+ : cy - StatusHeight,
+ SWP_NOZORDER | SWP_NOACTIVATE);
+
+ if (hwndEventDetails && Settings.bShowDetailsPane && hdwp)
+ hdwp = DeferWindowPos(hdwp,
+ hwndEventDetails,
+ HWND_TOP,
+ nVSplitPos + SPLIT_WIDTH/2,
+ nHSplitPos + SPLIT_WIDTH/2,
+ cx - nVSplitPos - SPLIT_WIDTH/2,
+ cy - nHSplitPos - SPLIT_WIDTH/2 - StatusHeight,
+ SWP_NOZORDER | SWP_NOACTIVATE);
+
+ if (hdwp)
+ EndDeferWindowPos(hdwp);
+}
+
-//
-// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
-//
-// PURPOSE: Processes messages for the main window.
-//
-// WM_COMMAND - process the application menu
-// WM_PAINT - Paint the main window
-// WM_DESTROY - post a quit message and return
-//
-//
LRESULT CALLBACK
-WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
RECT rect;
- NMHDR *hdr;
- switch (message)
+ switch (uMsg)
{
case WM_CREATE:
- CheckMenuRadioItem(GetMenu(hWnd),
- ID_LOG_APPLICATION,
- ID_LOG_SYSTEM,
- ID_LOG_APPLICATION,
- MF_BYCOMMAND);
+ hMainMenu = GetMenu(hWnd);
+ break;
+
+ case WM_DESTROY:
+ {
+ GetWindowPlacement(hwndMainWindow, &Settings.wpPos);
+ PostQuitMessage(0);
break;
+ }
case WM_NOTIFY:
- switch (((LPNMHDR)lParam)->code)
+ {
+ LPNMHDR hdr = (LPNMHDR)lParam;
+
+ if (hdr->hwndFrom == hwndListView)
{
- case NM_DBLCLK :
- hdr = (NMHDR FAR*)lParam;
- if (hdr->hwndFrom == hwndListView)
+ switch (hdr->code)
+ {
+ case LVN_ITEMCHANGED:
{
- LPNMITEMACTIVATE lpnmitem = (LPNMITEMACTIVATE)lParam;
+ LPNMLISTVIEW pnmv = (LPNMLISTVIEW)lParam;
- if (lpnmitem->iItem != -1)
+ if ( (pnmv->uChanged & LVIF_STATE) && /* The state has changed */
+ (pnmv->uNewState & LVIS_SELECTED) /* The item has been (de)selected */ )
{
- DialogBox(hInst,
- MAKEINTRESOURCE(IDD_EVENTDETAILDIALOG),
- hWnd,
- EventDetails);
+ if (hwndEventDetails)
+ SendMessageW(hwndEventDetails, EVT_DISPLAY, 0, 0);
}
+ break;
}
- break;
+
+ case NM_DBLCLK:
+ case NM_RETURN:
+ SendMessageW(hWnd, WM_COMMAND, IDM_EVENT_DETAILS, 0);
+ break;
+ }
+ }
+ else if (hdr->hwndFrom == hwndTreeView)
+ {
+ switch (hdr->code)
+ {
+ case TVN_BEGINLABELEDIT:
+ {
+ HTREEITEM hItem = ((LPNMTVDISPINFO)lParam)->item.hItem;
+
+ /* Disable label editing for root nodes */
+ return ((hItem == htiSystemLogs) ||
+ (hItem == htiAppLogs) ||
+ (hItem == htiUserLogs));
+ }
+
+ case TVN_ENDLABELEDIT:
+ {
+ TVITEMW item = ((LPNMTVDISPINFO)lParam)->item;
+ HTREEITEM hItem = item.hItem;
+
+ /* Disable label editing for root nodes */
+ if ((hItem == htiSystemLogs) ||
+ (hItem == htiAppLogs) ||
+ (hItem == htiUserLogs))
+ {
+ return FALSE;
+ }
+
+ if (item.pszText)
+ {
+ LPWSTR pszText = item.pszText;
+
+ /* Trim leading whitespace */
+ while (*pszText && iswspace(*pszText))
+ ++pszText;
+
+ if (!*pszText)
+ return FALSE;
+
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+
+ case TVN_SELCHANGED:
+ {
+ PEVENTLOGFILTER EventLogFilter =
+ (PEVENTLOGFILTER)((LPNMTREEVIEW)lParam)->itemNew.lParam;
+
+ // FIXME: It might be nice to reference here the filter,
+ // so that we don't have to reference/dereference it many times
+ // in the other functions???
+
+ // FIXME: This is a hack!!
+ if (hwndEventDetails && EventLogFilter)
+ {
+ SendMessageW(hwndEventDetails, EVT_SETFILTER, 0, (LPARAM)EventLogFilter);
+ }
+
+ if (EventLogFilter)
+ {
+ /*
+ * If we have selected a filter, enable the menu commands;
+ * they will possibly be updated after events enumeration.
+ */
+ EnableMenuItem(hMainMenu, IDM_SAVE_EVENTLOG, MF_BYCOMMAND | MF_ENABLED);
+ EnableMenuItem(hMainMenu, IDM_CLOSE_EVENTLOG, MF_BYCOMMAND | MF_ENABLED);
+ EnableMenuItem(hMainMenu, IDM_CLEAR_EVENTS, MF_BYCOMMAND | MF_ENABLED);
+ EnableMenuItem(hMainMenu, IDM_RENAME_EVENTLOG, MF_BYCOMMAND | MF_ENABLED);
+ EnableMenuItem(hMainMenu, IDM_EVENTLOG_SETTINGS, MF_BYCOMMAND | MF_ENABLED);
+ }
+ else
+ {
+ EnableMenuItem(hMainMenu, IDM_SAVE_EVENTLOG, MF_BYCOMMAND | MF_GRAYED);
+ EnableMenuItem(hMainMenu, IDM_CLOSE_EVENTLOG, MF_BYCOMMAND | MF_GRAYED);
+ EnableMenuItem(hMainMenu, IDM_CLEAR_EVENTS, MF_BYCOMMAND | MF_GRAYED);
+ EnableMenuItem(hMainMenu, IDM_RENAME_EVENTLOG, MF_BYCOMMAND | MF_GRAYED);
+ EnableMenuItem(hMainMenu, IDM_EVENTLOG_SETTINGS, MF_BYCOMMAND | MF_GRAYED);
+ }
+
+ /*
+ * The enumeration thread that is triggered by EnumEvents
+ * will set a new value for the 'ActiveFilter'.
+ */
+ if (EventLogFilter)
+ EnumEvents(EventLogFilter);
+
+ break;
+ }
+ }
}
break;
+ }
case WM_COMMAND:
- // Parse the menu selections:
+ {
+ /* Parse the menu selections */
switch (LOWORD(wParam))
{
- case ID_LOG_APPLICATION:
- if (QueryEventMessages(lpComputerName, // Use the local computer.
- EVENT_SOURCE_APPLICATION)) // The event log category
+ case IDM_OPEN_EVENTLOG:
+ OpenUserEventLog();
+ break;
+
+ case IDM_SAVE_EVENTLOG:
+ SaveEventLog(GetSelectedFilter(NULL));
+ break;
+
+ case IDM_CLOSE_EVENTLOG:
+ {
+ HTREEITEM hti;
+ PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(&hti);
+ CloseUserEventLog(EventLogFilter, hti);
+ break;
+ }
+
+ case IDM_CLEAR_EVENTS:
+ {
+ PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(NULL);
+ if (EventLogFilter && ClearEvents(EventLogFilter))
+ Refresh(EventLogFilter);
+ break;
+ }
+
+ case IDM_RENAME_EVENTLOG:
+ if (GetFocus() == hwndTreeView)
+ TreeView_EditLabel(hwndTreeView, TreeView_GetSelection(hwndTreeView));
+ break;
+
+ case IDM_EVENTLOG_SETTINGS:
+ {
+ PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(NULL);
+ // TODO: Check the returned value?
+ if (EventLogFilter)
+ EventLogProperties(hInst, hWnd, EventLogFilter);
+ break;
+ }
+
+ case IDM_LIST_NEWEST:
+ {
+ CheckMenuRadioItem(hMainMenu, IDM_LIST_NEWEST, IDM_LIST_OLDEST, IDM_LIST_NEWEST, MF_BYCOMMAND);
+ if (!Settings.bNewestEventsFirst)
+ {
+ Settings.bNewestEventsFirst = TRUE;
+ Refresh(GetSelectedFilter(NULL));
+ }
+ break;
+ }
+
+ case IDM_LIST_OLDEST:
+ {
+ CheckMenuRadioItem(hMainMenu, IDM_LIST_NEWEST, IDM_LIST_OLDEST, IDM_LIST_OLDEST, MF_BYCOMMAND);
+ if (Settings.bNewestEventsFirst)
+ {
+ Settings.bNewestEventsFirst = FALSE;
+ Refresh(GetSelectedFilter(NULL));
+ }
+ break;
+ }
+
+ case IDM_EVENT_DETAILS:
+ {
+ // LPNMITEMACTIVATE lpnmitem = (LPNMITEMACTIVATE)lParam;
+ PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(NULL);
+ if (/*lpnmitem->iItem != -1 &&*/ EventLogFilter)
{
- CheckMenuRadioItem(GetMenu(hWnd),
- ID_LOG_APPLICATION,
- ID_LOG_SYSTEM,
- ID_LOG_APPLICATION,
- MF_BYCOMMAND);
+ EventLogFilter_AddRef(EventLogFilter);
+ DialogBoxParamW(hInst,
+ MAKEINTRESOURCEW(IDD_EVENTDETAILS_DLG),
+ hWnd,
+ EventDetails,
+ (LPARAM)EventLogFilter);
+ EventLogFilter_Release(EventLogFilter);
}
break;
+ }
- case ID_LOG_SECURITY:
- if (QueryEventMessages(lpComputerName, // Use the local computer.
- EVENT_SOURCE_SECURITY)) // The event log category
- {
- CheckMenuRadioItem(GetMenu(hWnd),
- ID_LOG_APPLICATION,
- ID_LOG_SYSTEM,
- ID_LOG_SECURITY,
- MF_BYCOMMAND);
- }
+ case IDM_REFRESH:
+ Refresh(GetSelectedFilter(NULL));
break;
- case ID_LOG_SYSTEM:
- if (QueryEventMessages(lpComputerName, // Use the local computer.
- EVENT_SOURCE_SYSTEM)) // The event log category
+ case IDM_EVENT_DETAILS_VIEW:
+ {
+ Settings.bShowDetailsPane = !Settings.bShowDetailsPane;
+ CheckMenuItem(hMainMenu, IDM_EVENT_DETAILS_VIEW,
+ MF_BYCOMMAND | (Settings.bShowDetailsPane ? MF_CHECKED : MF_UNCHECKED));
+
+ GetClientRect(hWnd, &rect);
+ if (Settings.bShowDetailsPane)
+ {
+ ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
+ ShowWindow(hwndEventDetails, SW_SHOW);
+ }
+ else
{
- CheckMenuRadioItem(GetMenu(hWnd),
- ID_LOG_APPLICATION,
- ID_LOG_SYSTEM,
- ID_LOG_SYSTEM,
- MF_BYCOMMAND);
+ ShowWindow(hwndEventDetails, SW_HIDE);
+ ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
}
+
break;
+ }
- case IDM_REFRESH:
- Refresh();
+ case IDM_LIST_GRID_LINES:
+ {
+ Settings.bShowGrid = !Settings.bShowGrid;
+ CheckMenuItem(hMainMenu, IDM_LIST_GRID_LINES,
+ MF_BYCOMMAND | (Settings.bShowGrid ? MF_CHECKED : MF_UNCHECKED));
+
+ ListView_SetExtendedListViewStyleEx(hwndListView, LVS_EX_GRIDLINES, (Settings.bShowGrid ? LVS_EX_GRIDLINES : 0));
+ break;
+ }
+
+ case IDM_SAVE_SETTINGS:
+ {
+ Settings.bSaveSettings = !Settings.bSaveSettings;
+ CheckMenuItem(hMainMenu, IDM_SAVE_SETTINGS,
+ MF_BYCOMMAND | (Settings.bSaveSettings ? MF_CHECKED : MF_UNCHECKED));
break;
+ }
case IDM_ABOUT:
- DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
+ {
+ HICON hIcon;
+ WCHAR szCopyright[MAX_LOADSTRING];
+
+ hIcon = LoadIconW(hInst, MAKEINTRESOURCEW(IDI_EVENTVWR));
+ LoadStringW(hInst, IDS_COPYRIGHT, szCopyright, ARRAYSIZE(szCopyright));
+ ShellAboutW(hWnd, szTitle, szCopyright, hIcon);
+ DeleteObject(hIcon);
break;
+ }
case IDM_HELP:
- MessageBoxW(NULL,
- L"Help not implemented yet!",
- L"Event Log",
- MB_OK | MB_ICONINFORMATION);
- break;
+ MessageBoxW(hwndMainWindow,
+ L"Help not implemented yet!",
+ L"Event Log",
+ MB_OK | MB_ICONINFORMATION);
+ break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
- return DefWindowProc(hWnd, message, wParam, lParam);
+ return DefWindowProcW(hWnd, uMsg, wParam, lParam);
}
break;
+ }
- case WM_SIZE:
+ case WM_INITMENU:
+ {
+ if ((HMENU)wParam != hMainMenu)
+ break;
+
+ CheckMenuRadioItem(hMainMenu, IDM_LIST_NEWEST, IDM_LIST_OLDEST,
+ Settings.bNewestEventsFirst ? IDM_LIST_NEWEST : IDM_LIST_OLDEST,
+ MF_BYCOMMAND);
+
+ if (!hwndEventDetails)
+ {
+ EnableMenuItem(hMainMenu, IDM_EVENT_DETAILS_VIEW,
+ MF_BYCOMMAND | MF_GRAYED);
+ }
+ CheckMenuItem(hMainMenu, IDM_EVENT_DETAILS_VIEW,
+ MF_BYCOMMAND | (Settings.bShowDetailsPane ? MF_CHECKED : MF_UNCHECKED));
+
+ CheckMenuItem(hMainMenu, IDM_LIST_GRID_LINES,
+ MF_BYCOMMAND | (Settings.bShowGrid ? MF_CHECKED : MF_UNCHECKED));
+
+ CheckMenuItem(hMainMenu, IDM_SAVE_SETTINGS,
+ MF_BYCOMMAND | (Settings.bSaveSettings ? MF_CHECKED : MF_UNCHECKED));
+
+ break;
+ }
+
+#if 0
+ case WM_INITMENUPOPUP:
+ lParam = lParam;
+ break;
+
+ case WM_CONTEXTMENU:
+ lParam = lParam;
+ break;
+#endif
+
+ case WM_SETCURSOR:
+ {
+ POINT pt;
+
+ if (LOWORD(lParam) != HTCLIENT)
+ goto Default;
+
+ GetCursorPos(&pt);
+ ScreenToClient(hWnd, &pt);
+
+ /* Set the cursor for the vertical splitter */
+ if (pt.x >= nVSplitPos - SPLIT_WIDTH/2 && pt.x < nVSplitPos + SPLIT_WIDTH/2 + 1)
+ {
+ RECT rs;
+ GetClientRect(hWnd, &rect);
+ GetWindowRect(hwndStatus, &rs);
+ if (pt.y >= rect.top && pt.y < rect.bottom - (rs.bottom - rs.top))
+ {
+ SetCursor(LoadCursorW(NULL, IDC_SIZEWE));
+ return TRUE;
+ }
+ }
+ else
+ /* Set the cursor for the horizontal splitter, if the Event details pane is displayed */
+ if (hwndEventDetails && Settings.bShowDetailsPane &&
+ (pt.y >= nHSplitPos - SPLIT_WIDTH/2 && pt.y < nHSplitPos + SPLIT_WIDTH/2 + 1))
{
- // Gets the window rectangle
+ // RECT rs;
GetClientRect(hWnd, &rect);
+ // GetWindowRect(hwndStatus, &rs);
+ if (pt.x >= nVSplitPos + SPLIT_WIDTH/2 + 1 /* rect.left + (rs.bottom - rs.top) */ &&
+ pt.x < rect.right)
+ {
+ SetCursor(LoadCursorW(NULL, IDC_SIZENS));
+ return TRUE;
+ }
+ }
+ goto Default;
+ }
- // Relocate the listview
- MoveWindow(hwndListView,
- 0,
- 0,
- rect.right,
- rect.bottom - 20,
- 1);
+ case WM_LBUTTONDOWN:
+ {
+ INT x = GET_X_LPARAM(lParam);
+ INT y = GET_Y_LPARAM(lParam);
- // Resize the statusbar;
- SendMessage(hwndStatus, message, wParam, lParam);
+ /* Reset the splitter state */
+ bSplit = 0;
+
+ /* Capture the cursor for the vertical splitter */
+ if (x >= nVSplitPos - SPLIT_WIDTH/2 && x < nVSplitPos + SPLIT_WIDTH/2 + 1)
+ {
+ bSplit = 1;
+ SetCapture(hWnd);
+ }
+ else
+ /* Capture the cursor for the horizontal splitter, if the Event details pane is displayed */
+ if (hwndEventDetails && Settings.bShowDetailsPane &&
+ (y >= nHSplitPos - SPLIT_WIDTH/2 && y < nHSplitPos + SPLIT_WIDTH/2 + 1))
+ {
+ bSplit = 2;
+ SetCapture(hWnd);
}
break;
- case WM_DESTROY:
- FreeRecords();
- PostQuitMessage(0);
+ }
+
+ case WM_LBUTTONUP:
+ case WM_RBUTTONDOWN:
+ {
+ if (GetCapture() != hWnd)
+ break;
+
+ /* Adjust the correct splitter position */
+ if (bSplit == 1)
+ nVSplitPos = GET_X_LPARAM(lParam);
+ else if (bSplit == 2)
+ nHSplitPos = GET_Y_LPARAM(lParam);
+
+ /* If we are splitting, resize the windows */
+ if (bSplit != 0)
+ {
+ GetClientRect(hWnd, &rect);
+ ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
+ }
+
+ /* Reset the splitter state */
+ bSplit = 0;
+
+ ReleaseCapture();
break;
+ }
- default:
- return DefWindowProc(hWnd, message, wParam, lParam);
- }
+ case WM_MOUSEMOVE:
+ {
+ if (GetCapture() != hWnd)
+ break;
- return 0;
-}
+ /* Move the correct splitter */
+ if (bSplit == 1)
+ {
+ INT x = GET_X_LPARAM(lParam);
+ GetClientRect(hWnd, &rect);
-// Message handler for about box.
-INT_PTR CALLBACK
-About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
-{
- UNREFERENCED_PARAMETER(lParam);
- switch (message)
- {
- case WM_INITDIALOG:
+ x = min(max(x, SPLIT_WIDTH/2), rect.right - rect.left - SPLIT_WIDTH/2);
+ if (nVSplitPos != x)
+ {
+ nVSplitPos = x;
+ ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
+ }
+ }
+ else if (bSplit == 2)
{
- return (INT_PTR)TRUE;
+ RECT rs;
+ INT y = GET_Y_LPARAM(lParam);
+
+ GetClientRect(hWnd, &rect);
+ GetWindowRect(hwndStatus, &rs);
+
+ y = min(max(y, SPLIT_WIDTH/2), rect.bottom - rect.top - SPLIT_WIDTH/2 - (rs.bottom - rs.top));
+ if (nHSplitPos != y)
+ {
+ nHSplitPos = y;
+ ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
+ }
}
+ break;
+ }
- case WM_COMMAND:
- if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
+ case WM_SIZE:
+ {
+ if (wParam != SIZE_MINIMIZED)
{
- EndDialog(hDlg, LOWORD(wParam));
- return (INT_PTR)TRUE;
+ SendMessageW(hwndStatus, WM_SIZE, 0, 0);
+ ResizeWnd(LOWORD(lParam), HIWORD(lParam));
+ break;
}
- break;
+ /* Fall through the default case */
+ }
+
+ default: Default:
+ return DefWindowProcW(hWnd, uMsg, wParam, lParam);
}
- return (INT_PTR)FALSE;
+ return 0;
}
+
+static
VOID
-DisplayEvent(HWND hDlg)
+InitPropertiesDlg(HWND hDlg, PEVENTLOG EventLog)
{
- WCHAR szEventType[MAX_PATH];
- WCHAR szTime[MAX_PATH];
- WCHAR szDate[MAX_PATH];
- WCHAR szUser[MAX_PATH];
- WCHAR szComputer[MAX_PATH];
- WCHAR szSource[MAX_PATH];
- WCHAR szCategory[MAX_PATH];
- WCHAR szEventID[MAX_PATH];
- WCHAR szEventText[EVENT_MESSAGE_EVENTTEXT_BUFFER];
- WCHAR szEventData[MAX_PATH];
- BOOL bEventData = FALSE;
- LVITEMW li;
- EVENTLOGRECORD* pevlr;
- int iIndex;
+ LPWSTR lpLogName = EventLog->LogName;
+
+ DWORD Result, dwType;
+ DWORD dwMaxSize = 0, dwRetention = 0;
+ BOOL Success;
+ WIN32_FIND_DATAW FileInfo; // WIN32_FILE_ATTRIBUTE_DATA
+ ULARGE_INTEGER FileSize;
+ WCHAR wszBuf[MAX_PATH];
+ WCHAR szTemp[MAX_LOADSTRING];
+ LPWSTR FileName;
+
+ HKEY hLogKey;
+ WCHAR *KeyPath;
+ DWORD cbData;
+ SIZE_T cbKeyPath;
+
+ if (EventLog->Permanent)
+ {
- // Get index of selected item
- iIndex = (int)SendMessage (hwndListView, LVM_GETNEXTITEM, -1, LVNI_SELECTED | LVNI_FOCUSED);
+ cbKeyPath = (wcslen(EVENTLOG_BASE_KEY) + wcslen(lpLogName) + 1) * sizeof(WCHAR);
+ KeyPath = HeapAlloc(GetProcessHeap(), 0, cbKeyPath);
+ if (!KeyPath)
+ {
+ goto Quit;
+ }
- li.mask = LVIF_PARAM;
- li.iItem = iIndex;
- li.iSubItem = 0;
+ StringCbCopyW(KeyPath, cbKeyPath, EVENTLOG_BASE_KEY);
+ StringCbCatW(KeyPath, cbKeyPath, lpLogName);
- (void)ListView_GetItem(hwndListView, &li);
+ if (RegOpenKeyExW(hkMachine, KeyPath, 0, KEY_QUERY_VALUE, &hLogKey) != ERROR_SUCCESS)
+ {
+ HeapFree(GetProcessHeap(), 0, KeyPath);
+ goto Quit;
+ }
+ HeapFree(GetProcessHeap(), 0, KeyPath);
- pevlr = (EVENTLOGRECORD*)li.lParam;
- if (iIndex != -1)
+ cbData = sizeof(dwMaxSize);
+ Result = RegQueryValueExW(hLogKey,
+ L"MaxSize",
+ NULL,
+ &dwType,
+ (LPBYTE)&dwMaxSize,
+ &cbData);
+ if ((Result != ERROR_SUCCESS) || (dwType != REG_DWORD))
{
- ListView_GetItemText(hwndListView, iIndex, 0, szEventType, sizeof(szEventType) * sizeof(WCHAR));
- ListView_GetItemText(hwndListView, iIndex, 1, szDate, sizeof(szDate) * sizeof(WCHAR));
- ListView_GetItemText(hwndListView, iIndex, 2, szTime, sizeof(szTime) * sizeof(WCHAR));
- ListView_GetItemText(hwndListView, iIndex, 3, szSource, sizeof(szSource) * sizeof(WCHAR));
- ListView_GetItemText(hwndListView, iIndex, 4, szCategory, sizeof(szCategory) * sizeof(WCHAR));
- ListView_GetItemText(hwndListView, iIndex, 5, szEventID, sizeof(szEventID) * sizeof(WCHAR));
- ListView_GetItemText(hwndListView, iIndex, 6, szUser, sizeof(szUser) * sizeof(WCHAR));
- ListView_GetItemText(hwndListView, iIndex, 7, szComputer, sizeof(szComputer) * sizeof(WCHAR));
+ // dwMaxSize = 512 * 1024; /* 512 kBytes */
+ dwMaxSize = 0;
+ }
+ /* Convert in KB */
+ dwMaxSize /= 1024;
+
+ cbData = sizeof(dwRetention);
+ Result = RegQueryValueExW(hLogKey,
+ L"Retention",
+ NULL,
+ &dwType,
+ (LPBYTE)&dwRetention,
+ &cbData);
+ if ((Result != ERROR_SUCCESS) || (dwType != REG_DWORD))
+ {
+ /* On Windows 2003 it is 604800 (secs) == 7 days */
+ dwRetention = 0;
+ }
+ /* Convert in days, rounded up */ // ROUND_UP
+ // dwRetention = ROUND_UP(dwRetention, 24*3600) / (24*3600);
+ dwRetention = (dwRetention + 24*3600 - 1) / (24*3600);
- bEventData = !(pevlr->DataLength == 0);
- if (pevlr->DataLength > 0)
- {
- MultiByteToWideChar(CP_ACP,
- 0,
- (LPCSTR)((LPBYTE)pevlr + pevlr->DataOffset),
- pevlr->DataLength,
- szEventData,
- MAX_PATH);
- }
+ RegCloseKey(hLogKey);
+
+ }
- GetEventMessage(lpSourceLogName, szSource, pevlr, szEventText);
- EnableWindow(GetDlgItem(hDlg, IDC_BYTESRADIO), bEventData);
- EnableWindow(GetDlgItem(hDlg, IDC_WORDRADIO), bEventData);
+Quit:
- SetDlgItemTextW(hDlg, IDC_EVENTDATESTATIC, szDate);
- SetDlgItemTextW(hDlg, IDC_EVENTTIMESTATIC, szTime);
+ SetDlgItemTextW(hDlg, IDC_DISPLAYNAME, lpLogName); // FIXME!
+ SetDlgItemTextW(hDlg, IDC_LOGNAME, lpLogName);
- SetDlgItemTextW(hDlg, IDC_EVENTUSERSTATIC, szUser);
- SetDlgItemTextW(hDlg, IDC_EVENTSOURCESTATIC, szSource);
- SetDlgItemTextW(hDlg, IDC_EVENTCOMPUTERSTATIC, szComputer);
- SetDlgItemTextW(hDlg, IDC_EVENTCATEGORYSTATIC, szCategory);
- SetDlgItemTextW(hDlg, IDC_EVENTIDSTATIC, szEventID);
- SetDlgItemTextW(hDlg, IDC_EVENTTYPESTATIC, szEventType);
- SetDlgItemTextW(hDlg, IDC_EVENTTEXTEDIT, szEventText);
- SetDlgItemTextW(hDlg, IDC_EVENTDATAEDIT, szEventData);
+ FileName = EventLog->FileName;
+ if (FileName && *FileName)
+ {
+ /* Expand the file name. If the log file is on a remote computer, retrieve the network share form of the file name. */
+ GetExpandedFilePathName(EventLog->ComputerName, FileName, wszBuf, ARRAYSIZE(wszBuf));
+ FileName = wszBuf;
}
else
{
- MessageBoxW(NULL,
- L"No Items in ListView",
- L"Error",
- MB_OK | MB_ICONINFORMATION);
+ FileName = L"";
+ }
+ SetDlgItemTextW(hDlg, IDC_LOGFILE, FileName);
+
+ if (FileName && *FileName)
+ {
+ /*
+ * The general problem here (and in the shell as well) is that
+ * GetFileAttributesEx fails for files that are opened without
+ * shared access. To retrieve file information for those we need
+ * to use something else: FindFirstFile, on the full file name.
+ */
+ Success = GetFileAttributesExW(FileName,
+ GetFileExInfoStandard,
+ (LPWIN32_FILE_ATTRIBUTE_DATA)&FileInfo);
+ if (!Success)
+ {
+ HANDLE hFind = FindFirstFileW(FileName, &FileInfo);
+ Success = (hFind != INVALID_HANDLE_VALUE);
+ if (Success)
+ FindClose(hFind);
+ }
+ }
+ else
+ {
+ Success = FALSE;
}
-}
+ /* Starting there, FileName becomes invalid because we are reusing wszBuf */
-static
-INT_PTR CALLBACK
-StatusMessageWindowProc(IN HWND hwndDlg,
- IN UINT uMsg,
- IN WPARAM wParam,
- IN LPARAM lParam)
-{
- UNREFERENCED_PARAMETER(wParam);
+ if (Success)
+ {
+ FileSize.u.LowPart = FileInfo.nFileSizeLow;
+ FileSize.u.HighPart = FileInfo.nFileSizeHigh;
+ if (FormatFileSizeWithBytes(&FileSize, wszBuf, ARRAYSIZE(wszBuf)))
+ SetDlgItemTextW(hDlg, IDC_SIZE_LABEL, wszBuf);
+
+ LoadStringW(hInst, IDS_NOT_AVAILABLE, szTemp, ARRAYSIZE(szTemp));
+
+ if (GetFileTimeString(&FileInfo.ftCreationTime, wszBuf, ARRAYSIZE(wszBuf)))
+ SetDlgItemTextW(hDlg, IDC_CREATED_LABEL, wszBuf);
+ else
+ SetDlgItemTextW(hDlg, IDC_CREATED_LABEL, szTemp);
+
+ if (GetFileTimeString(&FileInfo.ftLastWriteTime, wszBuf, ARRAYSIZE(wszBuf)))
+ SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, wszBuf);
+ else
+ SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, szTemp);
+
+ if (GetFileTimeString(&FileInfo.ftLastAccessTime, wszBuf, ARRAYSIZE(wszBuf)))
+ SetDlgItemTextW(hDlg, IDC_ACCESSED_LABEL, wszBuf);
+ else
+ SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, szTemp);
+ }
+ else
+ {
+ LoadStringW(hInst, IDS_NOT_AVAILABLE, szTemp, ARRAYSIZE(szTemp));
- switch (uMsg)
+ SetDlgItemTextW(hDlg, IDC_SIZE_LABEL, szTemp);
+ SetDlgItemTextW(hDlg, IDC_CREATED_LABEL, szTemp);
+ SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, szTemp);
+ SetDlgItemTextW(hDlg, IDC_ACCESSED_LABEL, szTemp);
+ }
+
+ if (EventLog->Permanent)
{
- case WM_INITDIALOG:
+ SendDlgItemMessageW(hDlg, IDC_UPDOWN_MAXLOGSIZE, UDM_SETRANGE32, (WPARAM)1, (LPARAM)0x3FFFC0);
+ SendDlgItemMessageW(hDlg, IDC_UPDOWN_EVENTS_AGE, UDM_SETRANGE, 0, (LPARAM)MAKELONG(365, 1));
+
+ SetDlgItemInt(hDlg, IDC_EDIT_MAXLOGSIZE, dwMaxSize, FALSE);
+ SetDlgItemInt(hDlg, IDC_EDIT_EVENTS_AGE, dwRetention, FALSE);
+
+ if (dwRetention == 0)
+ {
+ CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_OVERWRITE_AS_NEEDED);
+ EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, FALSE);
+ EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, FALSE);
+ }
+ else if (dwRetention == INFINITE)
+ {
+ CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_NO_OVERWRITE);
+ EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, FALSE);
+ EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, FALSE);
+ }
+ else
{
- return TRUE;
+ CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_OVERWRITE_OLDER_THAN);
+ EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, TRUE);
+ EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, TRUE);
}
}
- return FALSE;
+ else
+ {
+ // TODO: Hide the unused controls! Or, just use another type of property sheet!
+ }
}
-
-// Message handler for event details box.
+/* Message handler for EventLog Properties dialog */
INT_PTR CALLBACK
-EventDetails(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
+EventLogPropProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
- UNREFERENCED_PARAMETER(lParam);
+ PEVENTLOG EventLog;
+
+ EventLog = (PEVENTLOG)GetWindowLongPtrW(hDlg, DWLP_USER);
- switch (message)
+ switch (uMsg)
{
case WM_INITDIALOG:
- // Show event info on dialog box
- DisplayEvent(hDlg);
+ {
+ EventLog = (PEVENTLOG)((LPPROPSHEETPAGE)lParam)->lParam;
+ SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)EventLog);
+
+ InitPropertiesDlg(hDlg, EventLog);
+
+ PropSheet_UnChanged(GetParent(hDlg), hDlg);
+ return (INT_PTR)TRUE;
+ }
+
+ case WM_DESTROY:
return (INT_PTR)TRUE;
case WM_COMMAND:
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
- case IDPREVIOUS:
- SendMessage(hwndListView, WM_KEYDOWN, VK_UP, 0);
+ case IDC_OVERWRITE_AS_NEEDED:
+ {
+ CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_OVERWRITE_AS_NEEDED);
+ EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, FALSE);
+ EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, FALSE);
+ break;
+ }
- // Show event info on dialog box
- DisplayEvent(hDlg);
- return (INT_PTR)TRUE;
+ case IDC_OVERWRITE_OLDER_THAN:
+ {
+ CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_OVERWRITE_OLDER_THAN);
+ EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, TRUE);
+ EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, TRUE);
+ break;
+ }
- case IDNEXT:
- SendMessage(hwndListView, WM_KEYDOWN, VK_DOWN, 0);
+ case IDC_NO_OVERWRITE:
+ {
+ CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_NO_OVERWRITE);
+ EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, FALSE);
+ EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, FALSE);
+ break;
+ }
- // Show event info on dialog box
- DisplayEvent(hDlg);
+ case IDHELP:
+ MessageBoxW(hDlg,
+ L"Help not implemented yet!",
+ L"Event Log",
+ MB_OK | MB_ICONINFORMATION);
return (INT_PTR)TRUE;
- case IDC_BYTESRADIO:
- return (INT_PTR)TRUE;
+ default:
+ break;
+ }
+ break;
+ }
+
+ return (INT_PTR)FALSE;
+}
+
+INT_PTR
+EventLogProperties(HINSTANCE hInstance, HWND hWndParent, PEVENTLOGFILTER EventLogFilter)
+{
+ INT_PTR ret = 0;
+ PROPSHEETHEADERW psh;
+ PROPSHEETPAGEW psp[1]; // 2
+
+ /*
+ * Bail out if there is no available filter, or if the filter
+ * contains more than one log.
+ */
+ if (!EventLogFilter)
+ return 0;
+
+ EventLogFilter_AddRef(EventLogFilter);
+
+ if (EventLogFilter->NumOfEventLogs > 1 ||
+ EventLogFilter->EventLogs[0] == NULL)
+ {
+ goto Quit;
+ }
+
+ /* Header */
+ psh.dwSize = sizeof(psh);
+ psh.dwFlags = PSH_PROPSHEETPAGE /*| PSH_USEICONID */ | PSH_PROPTITLE | PSH_HASHELP /*| PSH_NOCONTEXTHELP */ /*| PSH_USECALLBACK */;
+ psh.hInstance = hInstance;
+ psh.hwndParent = hWndParent;
+ // psh.pszIcon = MAKEINTRESOURCEW(IDI_APPICON); // Disabled because it only sets the small icon; the big icon is a stretched version of the small one.
+ psh.pszCaption = EventLogFilter->EventLogs[0]->LogName;
+ psh.nStartPage = 0;
+ psh.ppsp = psp;
+ psh.nPages = ARRAYSIZE(psp);
+ // psh.pfnCallback = PropSheetCallback;
+
+ /* Log properties page */
+ psp[0].dwSize = sizeof(psp[0]);
+ psp[0].dwFlags = PSP_HASHELP;
+ psp[0].hInstance = hInstance;
+ psp[0].pszTemplate = MAKEINTRESOURCEW(IDD_LOGPROPERTIES_GENERAL);
+ psp[0].pfnDlgProc = EventLogPropProc;
+ psp[0].lParam = (LPARAM)EventLogFilter->EventLogs[0];
+
+#if 0
+ /* TODO: Log sources page */
+ psp[1].dwSize = sizeof(psp[1]);
+ psp[1].dwFlags = PSP_HASHELP;
+ psp[1].hInstance = hInstance;
+ psp[1].pszTemplate = MAKEINTRESOURCEW(IDD_GENERAL_PAGE);
+ psp[1].pfnDlgProc = GeneralPageWndProc;
+ psp[0].lParam = (LPARAM)EventLogFilter->EventLogs[0];
+#endif
+
+ /* Create the property sheet */
+ ret = PropertySheetW(&psh);
+
+Quit:
+ EventLogFilter_Release(EventLogFilter);
+ return ret;
+}
+
+/* Message handler for Event Details dialog */
+static HWND hWndDetailsCtrl = NULL; // May go into the DWLP_USER
+static HWND hWndGrip = NULL;
+static INT cxMin, cyMin; // In window coordinates
+static INT cxOld, cyOld; // In client coordinates
+
+INT_PTR CALLBACK
+EventDetails(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ LONG_PTR dwStyle;
+ INT sbVXSize, sbHYSize;
+ RECT rcWnd, rect;
+
+ hWndDetailsCtrl = CreateEventDetailsCtrl(hInst, hDlg, lParam);
+ if (!hWndDetailsCtrl)
+ {
+ EndDialog(hDlg, 0);
+ return (INT_PTR)TRUE;
+ }
- case IDC_WORDRADIO:
+ /* Create a size grip if the dialog has a sizing border */
+ GetClientRect(hDlg, &rcWnd);
+ dwStyle = GetWindowLongPtrW(hDlg, GWL_STYLE);
+ sbVXSize = GetSystemMetrics(SM_CXVSCROLL);
+ sbHYSize = GetSystemMetrics(SM_CYHSCROLL);
+ if (dwStyle & WS_THICKFRAME /* == WS_SIZEBOX */)
+ {
+ hWndGrip = CreateWindowW(WC_SCROLLBARW,
+ NULL,
+ WS_CHILD | WS_VISIBLE | /**/ WS_CLIPSIBLINGS | /**/ SBS_SIZEGRIP | SBS_SIZEBOXBOTTOMRIGHTALIGN,
+ rcWnd.right - sbVXSize,
+ rcWnd.bottom - sbHYSize,
+ sbVXSize, sbHYSize,
+ hDlg,
+ NULL,
+ hInst,
+ NULL);
+ }
+
+ // SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)hWndDetailsCtrl);
+
+ /*
+ * Compute the minimum window size (in window coordinates) by
+ * adding the widths/heights of the "Help" and "Close" buttons,
+ * together with the margins, and add some minimal spacing
+ * between the buttons.
+ */
+ GetWindowRect(hDlg, &rcWnd);
+ cxMin = cyMin = 0;
+
+ GetWindowRect(GetDlgItem(hDlg, IDHELP), &rect);
+ cxMin += (rect.right - rect.left) + (rect.left - rcWnd.left); // == (rect.right - rcWnd.left);
+ cyMin += (rect.bottom - rect.top) + (rcWnd.bottom - rect.bottom); // == (rcWnd.bottom - rect.top);
+
+ GetWindowRect(GetDlgItem(hDlg, IDOK), &rect);
+ cxMin += (rect.right - rect.left) + (rcWnd.right - rect.right); // == (rcWnd.right - rect.left);
+ cyMin += (rect.bottom - rect.top) + (rcWnd.bottom - rect.bottom); // == (rcWnd.bottom - rect.top);
+
+ /*
+ * Convert the window rect from window to client coordinates
+ * in order to retrieve the sizes of the left and top margins,
+ * and add some extra space.
+ */
+ MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rcWnd, sizeof(RECT)/sizeof(POINT));
+
+ cxMin += -2*rcWnd.left; // Minimal spacing between the buttons == 2 * left margin
+ cyMin += -rcWnd.top + 12; // Add some space on top
+
+ GetClientRect(hDlg, &rcWnd);
+ cxOld = rcWnd.right - rcWnd.left;
+ cyOld = rcWnd.bottom - rcWnd.top;
+
+ /* Show event info on dialog control */
+ SendMessageW(hWndDetailsCtrl, EVT_DISPLAY, 0, 0);
+
+ // SetWindowPos(hWndDetailsCtrl, NULL,
+ // 0, 0,
+ // (rcWnd.right - rcWnd.left),
+ // (rcWnd.bottom - rcWnd.top),
+ // SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
+
+ /*
+ * Hide the placeholder static control and show the event details
+ * control instead. Note that the placeholder is here so far just
+ * to get the dimensions right in the dialog resource editor.
+ * I plan to remove it and use a custom control with a suitable
+ * window class for it, that would create the event details control
+ * instead.
+ */
+ ShowWindow(GetDlgItem(hDlg, IDC_STATIC), SW_HIDE);
+ ShowWindow(hWndDetailsCtrl, SW_SHOW);
+ return (INT_PTR)TRUE;
+ }
+
+ case WM_DESTROY:
+ if (IsWindow(hWndDetailsCtrl))
+ DestroyWindow(hWndDetailsCtrl);
+ hWndDetailsCtrl = NULL;
+ return (INT_PTR)TRUE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ case IDCANCEL:
+ EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
case IDHELP:
- MessageBoxW(NULL,
- L"Help not implemented yet!",
- L"Event Log",
- MB_OK | MB_ICONINFORMATION);
+ MessageBoxW(hDlg,
+ L"Help not implemented yet!",
+ L"Event Log",
+ MB_OK | MB_ICONINFORMATION);
return (INT_PTR)TRUE;
default:
break;
}
break;
+
+ case WM_SETCURSOR:
+ if (((HWND)wParam == hWndGrip) && (LOWORD(lParam) == HTCLIENT))
+ {
+ SetCursor(LoadCursorW(NULL, IDC_SIZENWSE));
+ SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, TRUE);
+ return (INT_PTR)TRUE;
+ }
+ break;
+
+ case WM_SIZING:
+ {
+ /* Forbid resizing the dialog smaller than its minimal size */
+ PRECT dragRect = (PRECT)lParam;
+
+ if ((wParam == WMSZ_LEFT) || (wParam == WMSZ_TOPLEFT) || (wParam == WMSZ_BOTTOMLEFT))
+ {
+ if (dragRect->right - dragRect->left < cxMin)
+ dragRect->left = dragRect->right - cxMin;
+ }
+
+ if ((wParam == WMSZ_RIGHT) || (wParam == WMSZ_TOPRIGHT) || (wParam == WMSZ_BOTTOMRIGHT))
+ {
+ if (dragRect->right - dragRect->left < cxMin)
+ dragRect->right = dragRect->left + cxMin;
+ }
+
+ if ((wParam == WMSZ_TOP) || (wParam == WMSZ_TOPLEFT) || (wParam == WMSZ_TOPRIGHT))
+ {
+ if (dragRect->bottom - dragRect->top < cyMin)
+ dragRect->top = dragRect->bottom - cyMin;
+ }
+
+ if ((wParam == WMSZ_BOTTOM) || (wParam == WMSZ_BOTTOMLEFT) || (wParam == WMSZ_BOTTOMRIGHT))
+ {
+ if (dragRect->bottom - dragRect->top < cyMin)
+ dragRect->bottom = dragRect->top + cyMin;
+ }
+
+ SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, TRUE);
+ return (INT_PTR)TRUE;
+ }
+
+ case WM_SIZE:
+ {
+ INT cx = LOWORD(lParam);
+ INT cy = HIWORD(lParam);
+
+ HDWP hdwp;
+ HWND hItemWnd;
+ RECT rect;
+
+ hdwp = BeginDeferWindowPos(4);
+
+ /* Resize the event details control window */
+
+ hItemWnd = hWndDetailsCtrl;
+ GetWindowRect(hItemWnd, &rect);
+ MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
+
+ if (hdwp)
+ hdwp = DeferWindowPos(hdwp,
+ hItemWnd,
+ HWND_TOP,
+ 0, 0,
+ (rect.right - rect.left) + (cx - cxOld),
+ (rect.bottom - rect.top) + (cy - cyOld),
+ SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
+
+ /* Move the buttons */
+
+ hItemWnd = GetDlgItem(hDlg, IDHELP);
+ GetWindowRect(hItemWnd, &rect);
+ MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
+
+ if (hdwp)
+ hdwp = DeferWindowPos(hdwp,
+ hItemWnd,
+ HWND_TOP,
+ rect.left,
+ rect.top + (cy - cyOld),
+ 0, 0,
+ SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
+
+ hItemWnd = GetDlgItem(hDlg, IDOK);
+ GetWindowRect(hItemWnd, &rect);
+ MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
+
+ if (hdwp)
+ hdwp = DeferWindowPos(hdwp,
+ hItemWnd,
+ HWND_TOP,
+ rect.left + (cx - cxOld),
+ rect.top + (cy - cyOld),
+ 0, 0,
+ SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
+
+ /* Move the size grip */
+ if (hWndGrip && hdwp)
+ {
+ GetWindowRect(hWndGrip, &rect);
+ MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
+
+ hdwp = DeferWindowPos(hdwp,
+ hWndGrip,
+ HWND_TOP,
+ rect.left + (cx - cxOld),
+ rect.top + (cy - cyOld),
+ 0, 0,
+ SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
+ }
+
+ if (hdwp)
+ EndDeferWindowPos(hdwp);
+
+ /* Hide the size grip if we are in maximized mode */
+ if (hWndGrip)
+ ShowWindow(hWndGrip, (wParam == SIZE_MAXIMIZED) ? SW_HIDE : SW_SHOW);
+
+ cxOld = cx;
+ cyOld = cy;
+
+ SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, 0);
+ return (INT_PTR)TRUE;
+ }
}
return (INT_PTR)FALSE;