[EVENTVWR] Additions to the Event Viewer.
[reactos.git] / base / applications / mscutils / eventvwr / eventvwr.c
index 7fd0b8d..f7e89d3 100644 (file)
  *  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 szWindowClass       = L"EVENTVWR"; /* The main window class name */
+static const WCHAR   EVENTLOG_BASE_KEY[] = L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\";
 
-//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;
+
+
+/* Global event records cache for the current active event log filter */
 DWORD g_TotalRecords = 0;
+PEVENTLOGRECORD* g_RecordPtrs = NULL;
+
+/* Lists of event logs and event log filters */
+LIST_ENTRY EventLogList;
+LIST_ENTRY EventLogFilterList;
+PEVENTLOGFILTER ActiveFilter = NULL;
+BOOL NewestEventsFirst = TRUE;
+
+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;
+
 
-LPWSTR lpSourceLogName = NULL;
-LPWSTR lpComputerName  = NULL;
+/* Forward declarations of functions included in this code module */
 
-/* Forward declarations of functions included in this code module: */
-ATOM MyRegisterClass(HINSTANCE hInstance);
+static DWORD WINAPI
+StartStopEnumEventsThread(IN LPVOID lpParameter);
+
+VOID OpenUserEventLogFile(IN LPCWSTR lpszFileName);
+
+VOID BuildLogListAndFilterList(IN LPCWSTR lpComputerName);
+VOID FreeLogList(VOID);
+VOID FreeLogFilterList(VOID);
+
+ATOM MyRegisterClass(HINSTANCE);
 BOOL InitInstance(HINSTANCE, int);
 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
-INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
+INT_PTR EventLogProperties(HINSTANCE, HWND, PEVENTLOGFILTER);
 INT_PTR CALLBACK EventDetails(HWND, UINT, WPARAM, LPARAM);
-static INT_PTR CALLBACK StatusMessageWindowProc (HWND, UINT, WPARAM, LPARAM);
 
 
+/* MAIN FUNCTIONS *************************************************************/
+
+VOID
+ShowWin32Error(IN DWORD dwError)
+{
+    LPWSTR lpMessageBuffer;
+
+    if (dwError == ERROR_SUCCESS)
+        return;
+
+    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
+DisplayUsage(VOID)
+{
+    LPWSTR lpBuffer;
+    LPCWSTR lpUsage;
+    INT iUsageLen = LoadStringW(hInst, IDS_USAGE, (LPWSTR)&lpUsage, 0);
+
+    if (iUsageLen == 0)
+        return;
+
+    lpBuffer = HeapAlloc(GetProcessHeap(), 0, (iUsageLen + 1) * sizeof(WCHAR));
+    if (!lpBuffer)
+        return;
+
+    StringCchCopyNW(lpBuffer, iUsageLen + 1, lpUsage, iUsageLen);
+    MessageBoxW(NULL, lpBuffer, szTitle, MB_OK | MB_ICONINFORMATION);
+
+    HeapFree(GetProcessHeap(), 0, lpBuffer);
+}
+
+BOOL
+ProcessCmdLine(IN LPWSTR lpCmdLine)
+{
+    BOOL Success = FALSE;
+    INT i, argc;
+    LPWSTR* argv;
+
+    /* Skip any leading whitespace */
+    if (lpCmdLine)
+    {
+        while (iswspace(*lpCmdLine))
+            ++lpCmdLine;
+    }
+
+    /* No command line means no processing needed */
+    if (!lpCmdLine || !*lpCmdLine)
+        return TRUE;
+
+    /* Build the arguments vector */
+    argv = CommandLineToArgvW(lpCmdLine, &argc);
+    if (!argv)
+        return FALSE;
+
+    /* Parse the command line for options (skip the program name) */
+    for (i = 1; i < argc; ++i)
+    {
+        /* Check for new options */
+        if (argv[i][0] == L'-' || argv[i][0] == L'/')
+        {
+            if (argv[i][1] == L'?' && argv[i][2] == 0)
+            {
+                /* Display help */
+                DisplayUsage();
+                goto Quit;
+            }
+            else
+            if (argv[i][2] == L':')
+            {
+                switch (towupper(argv[i][1]))
+                {
+                    case L'L':
+                    {
+                        LPWSTR lpNewBuffer;
+                        LPWSTR lpFileName = argv[i] + 3;
+                        SIZE_T cbFileName;
+
+                        /* 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;
+                        }
+
+                        /* Skip this one if we do not actually have a file name */
+                        if (!*lpFileName)
+                            continue;
+
+                        cbFileName = (wcslen(lpFileName) + 1) * sizeof(WCHAR);
+
+                        /* 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;
+                        }
+
+                        lpszzUserLogsToLoad = lpNewBuffer;
+                        lpNewBuffer = (LPWSTR)((ULONG_PTR)lpNewBuffer + cbUserLogsSize);
+                        cbUserLogsSize += cbFileName;
+
+                        /* Save the file name */
+                        StringCbCopyW(lpNewBuffer, cbFileName, lpFileName);
+
+                        continue;
+                    }
+
+                    default:
+                        break;
+                }
+            }
+
+            /* Unknown argument: display help and bail out */
+            DisplayUsage();
+            goto Quit;
+        }
+        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)
+            {
+                /* Store the computer name */
+                SIZE_T cbLength;
+
+                cbLength = (wcslen(argv[i]) + 1) * sizeof(WCHAR);
+                lpComputerName = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbLength);
+                if (lpComputerName)
+                {
+                    StringCbCopyW(lpComputerName, cbLength, argv[i]);
+                }
+                /* else, fall back to local computer */
+            }
+            else
+            {
+                /* Invalid syntax: display help and bail out */
+                DisplayUsage();
+                goto Quit;
+            }
+        }
+    }
+
+    Success = TRUE;
+
+Quit:
+    /* In case of failure, free anything we have allocated */
+    if (!Success)
+    {
+        if (lpszzUserLogsToLoad)
+        {
+            cbUserLogsSize = 0;
+            HeapFree(GetProcessHeap(), 0, lpszzUserLogsToLoad);
+            lpszzUserLogsToLoad = NULL;
+        }
+        if (lpComputerName)
+        {
+            HeapFree(GetProcessHeap(), 0, lpComputerName);
+            lpComputerName = NULL;
+        }
+    }
+
+    /* Free the arguments vector and exit */
+    LocalFree(argv);
+    return Success;
+}
+
 int APIENTRY
 wWinMain(HINSTANCE hInstance,
-          HINSTANCE hPrevInstance,
-          LPWSTR    lpCmdLine,
-          int       nCmdShow)
+         HINSTANCE hPrevInstance,
+         LPWSTR    lpCmdLine,
+         int       nCmdShow)
 {
-    MSG msg;
-    HACCEL hAccelTable;
+    HANDLE hThread;
     INITCOMMONCONTROLSEX iccx;
+    HMODULE hRichEdit;
+    HACCEL hAccelTable;
+    MSG msg;
 
     UNREFERENCED_PARAMETER(hPrevInstance);
     UNREFERENCED_PARAMETER(lpCmdLine);
@@ -86,51 +357,130 @@ wWinMain(HINSTANCE hInstance,
     /* 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.dwSize = sizeof(iccx);
     iccx.dwICC = ICC_LISTVIEW_CLASSES;
     InitCommonControlsEx(&iccx);
 
-    /* Initialize global strings */
-    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
-    MyRegisterClass(hInstance);
+    /* Load the RichEdit DLL to add support for RichEdit controls */
+    hRichEdit = LoadLibraryW(L"riched20.dll");
+    if (!hRichEdit)
+        return -1;
 
-    /* Perform application initialization: */
+    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;
+
+    /* Perform application initialization */
     if (!InitInstance(hInstance, nCmdShow))
+        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)
     {
-        return FALSE;
-    }
+        LPWSTR lpUserLog;
+        for (lpUserLog = lpszzUserLogsToLoad; *lpUserLog; lpUserLog += wcslen(lpUserLog) + 1)
+        {
+            OpenUserEventLogFile(lpUserLog);
+        }
 
-    hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_EVENTVWR));
+        /* Now cleanup the list of user logs */
+        cbUserLogsSize = 0;
+        HeapFree(GetProcessHeap(), 0, lpszzUserLogsToLoad);
+        lpszzUserLogsToLoad = NULL;
+    }
 
-    /* Main message loop: */
+    /* Main message loop */
     while (GetMessageW(&msg, NULL, 0, 0))
     {
-        if (!TranslateAcceleratorW(msg.hwnd, hAccelTable, &msg))
+        if (!TranslateAcceleratorW(hwndMainWindow, hAccelTable, &msg))
         {
             TranslateMessage(&msg);
-            DispatchMessage(&msg);
+            DispatchMessageW(&msg);
         }
     }
 
-    return (int)msg.wParam;
-}
+    SetEvent(hStartStopEnumEvent);
+    WaitForSingleObject(hThread, INFINITE);
+    CloseHandle(hThread);
 
-static void FreeRecords(void)
-{
-    DWORD iIndex;
+    /* Free the filters list and the event logs list */
+    FreeLogFilterList();
+    FreeLogList();
 
-    if (!g_RecordPtrs)
-        return;
+Cleanup:
+    /* Handle cleanup */
+    if (hStartEnumEvent)
+        CloseHandle(hStartEnumEvent);
+    if (hStartStopEnumEvent)
+        CloseHandle(hStartStopEnumEvent);
 
-    for (iIndex = 0; iIndex < g_TotalRecords; iIndex++)
-        HeapFree(GetProcessHeap(), 0, g_RecordPtrs[iIndex]);
-    HeapFree(GetProcessHeap(), 0, g_RecordPtrs);
-    g_RecordPtrs = NULL;
+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(DWORD EventTime,
-                      SYSTEMTIME *pSystemTime)
+EventTimeToSystemTime(IN DWORD EventTime,
+                      OUT PSYSTEMTIME pSystemTime)
 {
     SYSTEMTIME st1970 = { 1970, 1, 0, 1, 0, 0, 0, 0 };
     FILETIME ftLocal;
@@ -148,263 +498,967 @@ EventTimeToSystemTime(DWORD EventTime,
     FileTimeToSystemTime(&ftLocal, pSystemTime);
 }
 
-
-void
-TrimNulls(LPWSTR s)
+/*
+ * 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)
 {
-    WCHAR *c;
+    HMODULE hLibrary;
+    DWORD dwLength;
+    LPWSTR lpMsgBuf = NULL;
 
-    if (s != NULL)
-    {
-        c = s + wcslen(s) - 1;
-        while (c >= s && iswspace(*c))
-            --c;
-        *++c = L'\0';
-    }
-}
+    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;
 
-BOOL
-GetEventMessageFileDLL(IN LPCWSTR lpLogName,
-                       IN LPCWSTR SourceName,
-                       IN LPCWSTR EntryName,
-                       OUT LPWSTR ExpandedName)
-{
-    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)
+    _SEH2_TRY
+    {
+        /*
+         * Retrieve the message string without appending extra newlines.
+         * Wrap in SEH to protect from invalid string parameters.
+         */
+        _SEH2_TRY
         {
-            dwSize = MAX_PATH;
-            if (RegQueryValueExW(hSourceKey,
-                                EntryName,
-                                NULL,
-                                NULL,
-                                (LPBYTE)szModuleName,
-                                &dwSize) == ERROR_SUCCESS)
+            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))
             {
-                /* Returns a string containing the requested substituted environment variable. */
-                ExpandEnvironmentStringsW((LPCWSTR)szModuleName, ExpandedName, MAX_PATH);
-
-                /* Successful */
-                bReturn = TRUE;
+                /* 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;
     }
-    else
+    _SEH2_FINALLY
     {
-        MessageBoxW(NULL,
-                   L"Registry access failed!",
-                   L"Event Log",
-                   MB_OK | MB_ICONINFORMATION);
+        FreeLibrary(hLibrary);
     }
+    _SEH2_END;
 
-    if (hSourceKey != NULL)
-        RegCloseKey(hSourceKey);
-
-    if (hAppKey != NULL)
-        RegCloseKey(hAppKey);
+    if (dwLength == 0)
+    {
+        ASSERT(lpMsgBuf == NULL);
+        lpMsgBuf = NULL;
+    }
+    else
+    {
+        ASSERT(lpMsgBuf);
+    }
 
-    return bReturn;
+    return lpMsgBuf;
 }
 
-
-BOOL
-GetEventCategory(IN LPCWSTR KeyName,
-                 IN LPCWSTR SourceName,
-                 IN EVENTLOGRECORD *pevlr,
-                 OUT LPWSTR CategoryName)
+/*
+ * 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)
 {
-    HANDLE hLibrary = NULL;
-    WCHAR szMessageDLL[MAX_PATH];
-    LPVOID lpMsgBuf = NULL;
+    BOOL Success = FALSE;
+    SIZE_T cbLength;
+    LPWSTR szMessageDllList;
+    LPWSTR szDll;
+    LPWSTR lpMsgBuf = NULL;
 
-    if (GetEventMessageFileDLL (KeyName, SourceName, EVENT_CATEGORY_MESSAGE_FILE , szMessageDLL))
+    /* 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)
     {
-        hLibrary = LoadLibraryExW(szMessageDLL,
-                                 NULL,
-                                 DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE);
-        if (hLibrary != NULL)
+        // Uses LANG_USER_DEFAULT
+        lpMsgBuf = GetMessageStringFromDll(szDll,
+                                           dwFlags,
+                                           dwMessageId,
+                                           nSize,
+                                           Arguments);
+        if (lpMsgBuf)
         {
-            /* 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 (lpMsgBuf)
-                {
-                    /* Trim the string */
-                    TrimNulls((LPWSTR)lpMsgBuf);
-
-                    /* Copy the category name */
-                    wcscpy(CategoryName, (LPCWSTR)lpMsgBuf);
-                }
-                else
-                {
-                    wcscpy(CategoryName, (LPCWSTR)lpMsgBuf);
-                }
-            }
-            else
-            {
-                LoadStringW(hInst, IDS_NONE, CategoryName, MAX_PATH);
-            }
-
-            if (hLibrary != NULL)
-                FreeLibrary(hLibrary);
-
-            /* Free the buffer allocated by FormatMessage */
-            if (lpMsgBuf)
-                LocalFree(lpMsgBuf);
-
-            return TRUE;
+            /* 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);
     }
 
-    LoadStringW(hInst, IDS_NONE, CategoryName, MAX_PATH);
+    HeapFree(GetProcessHeap(), 0, szMessageDllList);
 
-    return FALSE;
+    return lpMsgBuf;
 }
 
 
-BOOL
-GetEventMessage(IN LPCWSTR KeyName,
-                IN LPCWSTR SourceName,
-                IN EVENTLOGRECORD *pevlr,
-                OUT LPWSTR EventText)
+typedef struct
 {
-    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;
-
-    /* TODO : GetEventMessageFileDLL can return a comma separated list of DLLs */
-    if (GetEventMessageFileDLL (KeyName, SourceName, EVENT_MESSAGE_FILE, SourceModuleName))
+    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)
     {
-        /* Get the event message */
-        szMessage = (LPWSTR)((LPBYTE)pevlr + pevlr->StringOffset);
-
-        /* Allocate space for parameters */
-        szArguments = malloc(sizeof(LPVOID) * pevlr->NumStrings);
-        if (!szArguments)
+        while ((pTempMessage = wcschr(pTempMessage, L'%')))
         {
-            return FALSE;
+            pTempMessage++;
+            if (iswdigit(*pTempMessage))
+            {
+                dwParamCount++;
+                while (iswdigit(*++pTempMessage)) ;
+            }
         }
-
-        for (i = 0; i < pevlr->NumStrings ; i++)
+    }
+    else
+    {
+        while ((pTempMessage = wcsstr(pTempMessage, L"%%")))
         {
-            if (wcsstr(szMessage , L"%%"))
+            pTempMessage += 2;
+            if (iswdigit(*pTempMessage))
             {
-                if (GetEventMessageFileDLL(KeyName, SourceName, EVENT_PARAMETER_MESSAGE_FILE, ParameterModuleName))
-                {
-                    /* Not yet support for reading messages from parameter message DLL */
-                }
+                dwParamCount++;
+                while (iswdigit(*++pTempMessage)) ;
             }
-
-            szArguments[i] = szMessage;
-            szMessage += wcslen(szMessage) + 1;
         }
+    }
+
+    /* 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;
+    }
 
-        szDll = wcstok(SourceModuleName, EVENT_DLL_SEPARATOR);
-        while ((szDll != NULL) && (!bDone))
+    /*
+     * 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))
         {
-            hLibrary = LoadLibraryExW(szDll,
-                                     NULL,
-                                     DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE);
-            if (hLibrary == NULL)
+            pTempMessage++;
+            if (iswdigit(*pTempMessage))
             {
-                /* The DLL could not be loaded try the next one (if any) */
-                szDll = wcstok(NULL, EVENT_DLL_SEPARATOR);
+                pParamData[i].pStartingAddress = pTempMessage-1;
+                pParamData[i].pParameterID = (DWORD)_wtol(pTempMessage);
+
+                while (iswdigit(*++pTempMessage)) ;
+
+                pParamData[i].pEndingAddress = pTempMessage;
+                i++;
             }
-            else
+        }
+    }
+    else
+    {
+        while ((pTempMessage = wcsstr(pTempMessage, L"%%")) && (i < dwParamCount))
+        {
+            pTempMessage += 2;
+            if (iswdigit(*pTempMessage))
             {
-                /* 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;
+                pParamData[i].pStartingAddress = pTempMessage-2;
+                pParamData[i].pParameterID = (DWORD)_wtol(pTempMessage);
 
-                        /* Trim the string */
-                        TrimNulls((LPWSTR)lpMsgBuf);
-
-                        /* Copy the event text */
-                        wcscpy(EventText ,lpMsgBuf);
-                    }
-                }
+                while (iswdigit(*++pTempMessage)) ;
 
-                FreeLibrary(hLibrary);
+                pParamData[i].pEndingAddress = pTempMessage;
+                i++;
             }
         }
+    }
 
-        if (!bDone)
+    /* 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)
         {
-            LoadStringW(hInst, IDS_EVENTSTRINGIDNOTFOUND, szStringIDNotFound, MAX_LOADSTRING);
-            swprintf(EventText, szStringIDNotFound, (pevlr->EventID & 0xFFFF), SourceName);
+            /* Skip the insertion string */
+            continue;
         }
 
-        free(szArguments);
+        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;
 
-        /* No more dlls to try, return result */
-        return bDone;
+    /* 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;
     }
 
-    LoadStringW(hInst, IDS_EVENTSTRINGIDNOTFOUND, szStringIDNotFound, MAX_LOADSTRING);
-    swprintf(EventText, szStringIDNotFound, (pevlr->EventID & 0xFFFF), SourceName);
+    /* Append the last segment from pMessage */
+    StringCchCopyW(pTempFinalMessage, cchBuffer, pTempMessage);
 
-    return FALSE;
-}
+Cleanup:
 
+    // if (Status != ERROR_SUCCESS)
+        // *pFinalMessage = NULL;
 
-VOID
-GetEventType(IN WORD dwEventType,
-             OUT LPWSTR eventTypeText)
-{
-    switch (dwEventType)
+    if (pParamData)
     {
-        case EVENTLOG_ERROR_TYPE:
-            LoadStringW(hInst, IDS_EVENTLOG_ERROR_TYPE, eventTypeText, MAX_LOADSTRING);
+        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';
+    }
+}
+
+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 Type, 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(HKEY_LOCAL_MACHINE,
+                           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,
+                              &Type,
+                              (LPBYTE)szModuleName,
+                              &dwSize);
+    if ((Result != ERROR_SUCCESS) || (Type != REG_EXPAND_SZ && Type != REG_SZ))
+    {
+        szModuleName[0] = UNICODE_NULL;
+    }
+    else
+    {
+        /* NULL-terminate the string and expand it */
+        szModuleName[dwSize / sizeof(WCHAR) - 1] = UNICODE_NULL;
+        ExpandEnvironmentStringsW(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);
@@ -412,782 +1466,2123 @@ GetEventType(IN WORD dwEventType,
         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;
-        case EVENTLOG_SUCCESS:
-            LoadStringW(hInst, IDS_EVENTLOG_SUCCESS, 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(NULL, // FIXME: Use computer name? From the particular event?
+                              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 | (NewestEventsFirst ? 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;
+
+    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);
+            }
+
+           if (WaitForSingleObject(hStopEnumEvent, 0) == WAIT_OBJECT_0)
+                goto Quit;
+
+            /* Filter by event type */
+            if (!FilterByType(EventLogFilter, pEvlrTmp))
+                goto SkipEvent;
+
+            /* Get the event source name and filter it */
+            lpszSourceName = (LPWSTR)(pEvlrBuffer + sizeof(EVENTLOGRECORD));
+            if (!FilterByString(EventLogFilter->Sources, lpszSourceName))
+                goto SkipEvent;
+
+            /* Get the computer name and filter it */
+            lpszComputerName = (LPWSTR)(pEvlrBuffer + sizeof(EVENTLOGRECORD) + (wcslen(lpszSourceName) + 1) * sizeof(WCHAR));
+            if (!FilterByString(EventLogFilter->ComputerNames, lpszComputerName))
+                goto SkipEvent;
+
+            /* 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));
+
+            /* 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)g_RecordPtrs[dwCurrentRecord];
+            lviEventItem.pszText = szEventTypeText;
+
+            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;
+    }
 
-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;
+    /* Add the event log and the filter into their lists */
+    InsertTailList(&EventLogList, &EventLog->ListEntry);
+    InsertTailList(&EventLogFilterList, &EventLogFilter->ListEntry);
 
-    /* Point to the SID. */
-    lpSid = (PSID)((LPBYTE)pelr + pelr->UserSidOffset);
+    /* 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);
 
-    /* User SID */
-    if (pelr->UserSidLength > 0)
+    hItem = TreeViewAddItem(hwndTreeView, htiUserLogs,
+                            (LPWSTR)lpszFileName,
+                            2, 3, (LPARAM)EventLogFilter);
+
+    /* Select the event log */
+    if (hItem)
     {
-        if (LookupAccountSidW(NULL,
-                             lpSid,
-                             szName,
-                             &cbName,
-                             szDomain,
-                             &cbDomain,
-                             &peUse))
-        {
-            wcscpy(pszUser, szName);
-            return TRUE;
-        }
+        // TreeView_Expand(hwndTreeView, htiUserLogs, TVE_EXPAND);
+        TreeView_SelectItem(hwndTreeView, hItem);
+        TreeView_EnsureVisible(hwndTreeView, hItem);
     }
-
-    return FALSE;
+    InvalidateRect(hwndTreeView, NULL, FALSE);
+    SetFocus(hwndTreeView);
 }
 
+VOID
+OpenUserEventLog(VOID)
+{
+    WCHAR szFileName[MAX_PATH];
+
+    ZeroMemory(szFileName, sizeof(szFileName));
 
-static DWORD WINAPI
-ShowStatusMessageThread(IN LPVOID lpParameter)
+    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)
 {
-    HWND *phWnd = (HWND *)lpParameter;
-    HWND hWnd;
-    MSG Msg;
-
-    hWnd = CreateDialogParam(hInst,
-                             MAKEINTRESOURCE(IDD_PROGRESSBOX),
-                             GetDesktopWindow(),
-                             StatusMessageWindowProc,
-                             (LPARAM)NULL);
-    if (!hWnd)
-        return 0;
+    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);
 
-    *phWnd = hWnd;
+    EventLog = EventLogFilter->EventLogs[0];
+    hEventLog = OpenEventLogW(EventLog->ComputerName, EventLog->LogName);
 
-    ShowWindow(hWnd, SW_SHOW);
+    EventLogFilter_Release(EventLogFilter);
 
-    /* Message loop for the Status window */
-    while (GetMessage(&Msg, NULL, 0, 0))
+    if (!hEventLog)
     {
-        TranslateMessage(&Msg);
-        DispatchMessage(&Msg);
+        ShowWin32Error(GetLastError());
+        return;
     }
 
-    return 0;
+    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);
+    }
+
+    /*
+     * 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);
 }
 
 
 BOOL
-QueryEventMessages(LPWSTR lpMachineName,
-                   LPWSTR lpLogName)
+ClearEvents(IN PEVENTLOGFILTER EventLogFilter)
 {
-    HWND hwndDlg;
+    BOOL Success;
+    PEVENTLOG EventLog;
     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 szFileName[MAX_PATH];
+    WCHAR szMessage[MAX_LOADSTRING];
 
-    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];
+    /* Bail out if there is no available filter */
+    if (!EventLogFilter)
+        return FALSE;
 
-    SYSTEMTIME time;
-    LVITEMW lviEventItem;
+    ZeroMemory(szFileName, sizeof(szFileName));
+    ZeroMemory(szMessage, sizeof(szMessage));
 
-    dwFlags = EVENTLOG_FORWARDS_READ | EVENTLOG_SEQUENTIAL_READ;
+    LoadStringW(hInst, IDS_CLEAREVENTS_MSG, szMessage, ARRAYSIZE(szMessage));
 
-    lpSourceLogName = lpLogName;
-    lpComputerName = lpMachineName;
+    sfn.lpstrFile = szFileName;
+    sfn.nMaxFile  = ARRAYSIZE(szFileName);
 
-    /* Open the event log. */
-    hEventLog = OpenEventLogW(lpMachineName,
-                             lpLogName);
-    if (hEventLog == NULL)
+    switch (MessageBoxW(hwndMainWindow, szMessage, szTitle, MB_YESNOCANCEL | MB_ICONINFORMATION))
     {
-        MessageBoxW(NULL,
-                   L"Could not open the event log.",
-                   L"Event Log",
-                   MB_OK | MB_ICONINFORMATION);
-        return FALSE;
-    }
+        case IDCANCEL:
+            return FALSE;
 
-    /* Disable listview redraw */
-    SendMessage(hwndListView, WM_SETREDRAW, FALSE, 0);
+        case IDNO:
+            sfn.lpstrFile = NULL;
+            break;
 
-    /* Clear the list view */
-    (void)ListView_DeleteAllItems (hwndListView);
-    FreeRecords();
+        case IDYES:
+            if (!GetSaveFileNameW(&sfn))
+                return FALSE;
+            break;
+    }
 
-    GetOldestEventLogRecord(hEventLog, &dwThisRecord);
+    EventLogFilter_AddRef(EventLogFilter);
 
-    /* Get the total number of event log records. */
-    GetNumberOfEventLogRecords (hEventLog , &dwTotalRecords);
-    g_TotalRecords = dwTotalRecords;
+    EventLog = EventLogFilter->EventLogs[0];
+    hEventLog = OpenEventLogW(EventLog->ComputerName, EventLog->LogName);
 
-    g_RecordPtrs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwTotalRecords * sizeof(PVOID));
+    EventLogFilter_Release(EventLogFilter);
 
-    /* If we have at least 1000 records show the waiting dialog */
-    if (dwTotalRecords > 1000)
+    if (!hEventLog)
     {
-        CloseHandle(CreateThread(NULL,
-                                 0,
-                                 ShowStatusMessageThread,
-                                 (LPVOID)&hwndDlg,
-                                 0,
-                                 NULL));
+        ShowWin32Error(GetLastError());
+        return FALSE;
     }
 
-    while (dwCurrentRecord < dwTotalRecords)
-    {
-        pevlr = HeapAlloc(GetProcessHeap(), 0, sizeof(EVENTLOGRECORD) * dwTotalRecords);
-        g_RecordPtrs[dwCurrentRecord] = pevlr;
+    Success = ClearEventLogW(hEventLog, sfn.lpstrFile);
+    if (!Success)
+        ShowWin32Error(GetLastError());
 
-        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
-        }
+    CloseEventLog(hEventLog);
+    return Success;
+}
 
-        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);
 
-            // Get the event source name.
-            lpSourceName = (LPWSTR)((LPBYTE)pevlr + sizeof(EVENTLOGRECORD));
+VOID
+Refresh(IN PEVENTLOGFILTER EventLogFilter)
+{
+    /* Bail out if there is no available filter */
+    if (!EventLogFilter)
+        return;
 
-            // Get the computer name
-            lpComputerName = (LPWSTR)((LPBYTE)pevlr + sizeof(EVENTLOGRECORD) + (wcslen(lpSourceName) + 1) * sizeof(WCHAR));
+    /* Reenumerate the events through the filter */
+    EnumEvents(EventLogFilter);
+}
 
-            // This ist the data section of the current event
-            lpData = (LPSTR)((LPBYTE)pevlr + pevlr->DataOffset);
 
-            // Compute the event type
-            EventTimeToSystemTime(pevlr->TimeWritten, &time);
+ATOM
+MyRegisterClass(HINSTANCE hInstance)
+{
+    WNDCLASSEXW wcex;
 
-            // Get the username that generated the event
-            GetEventUserName(pevlr, szUsername);
+    wcex.cbSize = sizeof(wcex);
+    wcex.style = 0;
+    wcex.lpfnWndProc = WndProc;
+    wcex.cbClsExtra = 0;
+    wcex.cbWndExtra = 0;
+    wcex.hInstance = hInstance;
+    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 = szWindowClass;
+    wcex.hIconSm = (HICON)LoadImageW(hInstance,
+                                     MAKEINTRESOURCEW(IDI_EVENTVWR),
+                                     IMAGE_ICON,
+                                     16,
+                                     16,
+                                     LR_SHARED);
 
-            GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time, NULL, szLocalDate, MAX_PATH);
-            GetTimeFormatW(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &time, NULL, szLocalTime, MAX_PATH);
+    return RegisterClassExW(&wcex);
+}
 
-            GetEventType(pevlr->EventType, szEventTypeText);
-            GetEventCategory(lpLogName, lpSourceName, pevlr, szCategory);
 
-            swprintf(szEventID, L"%u", (pevlr->EventID & 0xFFFF));
-            swprintf(szCategoryID, L"%u", pevlr->EventCategory);
+BOOL
+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 Type, 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;
 
-            lviEventItem.mask = LVIF_IMAGE | LVIF_TEXT | LVIF_PARAM;
-            lviEventItem.iItem = 0;
-            lviEventItem.iSubItem = 0;
-            lviEventItem.lParam = (LPARAM)pevlr;
-            lviEventItem.pszText = szEventTypeText;
+    StringCbCopyW(KeyPath, cbKeyPath, EVENTLOG_BASE_KEY);
+    StringCbCatW(KeyPath, cbKeyPath, lpLogName);
 
-            switch (pevlr->EventType)
-            {
-                case EVENTLOG_ERROR_TYPE:
-                    lviEventItem.iImage = 2;
-                    break;
+    Result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, KeyPath, 0, KEY_QUERY_VALUE, &hLogKey);
+    HeapFree(GetProcessHeap(), 0, KeyPath);
+    if (Result != ERROR_SUCCESS)
+        return FALSE;
 
-                case EVENTLOG_AUDIT_FAILURE:
-                    lviEventItem.iImage = 2;
-                    break;
+    cbData = sizeof(szModuleName);
+    Result = RegQueryValueExW(hLogKey,
+                              L"DisplayNameFile",
+                              NULL,
+                              &Type,
+                              (LPBYTE)szModuleName,
+                              &cbData);
+    if ((Result != ERROR_SUCCESS) || (Type != REG_EXPAND_SZ && Type != REG_SZ))
+    {
+        szModuleName[0] = UNICODE_NULL;
+    }
+    else
+    {
+        /* NULL-terminate the string and expand it */
+        szModuleName[cbData / sizeof(WCHAR) - 1] = UNICODE_NULL;
+        ExpandEnvironmentStringsW(szModuleName, lpModuleName, ARRAYSIZE(szModuleName));
+        Success = TRUE;
+    }
 
-                case EVENTLOG_WARNING_TYPE:
-                    lviEventItem.iImage = 1;
-                    break;
+    /*
+     * 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,
+                                  &Type,
+                                  (LPBYTE)&dwMessageID,
+                                  &cbData);
+        if ((Result != ERROR_SUCCESS) || (Type != REG_DWORD))
+            dwMessageID = 0;
 
-                case EVENTLOG_INFORMATION_TYPE:
-                    lviEventItem.iImage = 0;
-                    break;
+        *pdwMessageID = dwMessageID;
+    }
 
-                case EVENTLOG_AUDIT_SUCCESS:
-                    lviEventItem.iImage = 0;
-                    break;
+    RegCloseKey(hLogKey);
 
-                case EVENTLOG_SUCCESS:
-                    lviEventItem.iImage = 0;
-                    break;
-            }
+    return Success;
+}
 
-            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, 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);
-        }
+VOID
+BuildLogListAndFilterList(IN LPCWSTR lpComputerName)
+{
+    LONG Result;
+    HKEY hEventLogKey, hLogKey;
+    DWORD dwNumLogs = 0;
+    DWORD dwIndex, dwMaxKeyLength;
+    DWORD Type;
+    PEVENTLOG EventLog;
+    PEVENTLOGFILTER EventLogFilter;
+    LPWSTR LogName = NULL;
+    WCHAR szModuleName[MAX_PATH];
+    DWORD lpcName;
+    DWORD dwMessageID;
+    LPWSTR lpDisplayName;
+    HTREEITEM hRootNode = NULL, hItem = NULL, hItemDefault = NULL;
+
+    /* Open the EventLog key */
+    // TODO: Implement connection to remote computer...
+    // At the moment we only support the user local computer.
+    // FIXME: Use local or remote computer
+    Result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, EVENTLOG_BASE_KEY, 0, KEY_READ, &hEventLogKey);
+    if (Result != ERROR_SUCCESS)
+    {
+        return;
+    }
 
-        dwRecordsToRead--;
-        dwCurrentRecord++;
+    /* 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;
 
-    // All events loaded
-    EndDialog(hwndDlg, 0);
+    /* Take the NULL terminator into account */
+    ++dwMaxKeyLength;
 
+    /* Allocate the temporary buffer */
+    LogName = HeapAlloc(GetProcessHeap(), 0, dwMaxKeyLength * sizeof(WCHAR));
+    if (!LogName)
+        goto Quit;
 
-    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);
+    /* 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;
+        }
 
-    swprintf(szStatusText, L"%s has %d event(s)", lpLogName, dwTotalRecords);
+        /* Add the event log and the filter into their lists */
+        InsertTailList(&EventLogList, &EventLog->ListEntry);
+        InsertTailList(&EventLogFilterList, &EventLogFilter->ListEntry);
 
-    // Update the status bar
-    SendMessageW(hwndStatus, SB_SETTEXT, (WPARAM)0, (LPARAM)szStatusText);
+        EventLog->FileName = NULL;
 
-    // Set the window title
-    SetWindowTextW(hwndMainWindow, szWindowTitle);
+        /* 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,
+                                      &Type,
+                                      NULL,
+                                      &lpcName);
+            if ((Result != ERROR_SUCCESS) || (Type != REG_EXPAND_SZ && Type != 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,
+                                              &Type,
+                                              (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;
+                    }
+                }
+            }
 
-    // Resume list view redraw
-    SendMessageW(hwndListView, WM_SETREDRAW, TRUE, 0);
+            RegCloseKey(hLogKey);
+        }
 
-    // Close the event log.
-    CloseEventLog(hEventLog);
+        /* Get the display name for the event log */
+        lpDisplayName = NULL;
 
-    return TRUE;
-}
+        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
-Refresh(VOID)
+FreeLogList(VOID)
 {
-    QueryEventMessages(lpComputerName,
-                       lpSourceLogName);
-}
+    PLIST_ENTRY Entry;
+    PEVENTLOG EventLog;
+
+    while (!IsListEmpty(&EventLogList))
+    {
+        Entry = RemoveHeadList(&EventLogList);
+        EventLog = (PEVENTLOG)CONTAINING_RECORD(Entry, EVENTLOG, ListEntry);
+        EventLog_Free(EventLog);
+    }
 
+    return;
+}
 
-//
-//  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)
+VOID
+FreeLogFilterList(VOID)
 {
-    WNDCLASSEXW wcex;
+    PLIST_ENTRY Entry;
+    PEVENTLOGFILTER EventLogFilter;
 
-    wcex.cbSize = sizeof(WNDCLASSEX);
+    while (!IsListEmpty(&EventLogFilterList))
+    {
+        Entry = RemoveHeadList(&EventLogFilterList);
+        EventLogFilter = (PEVENTLOGFILTER)CONTAINING_RECORD(Entry, EVENTLOGFILTER, ListEntry);
+        EventLogFilter_Free(EventLogFilter);
+    }
 
-    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));
+    ActiveFilter = NULL;
 
-    return RegisterClassExW(&wcex);
+    return;
 }
 
-
-//
-//   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)
 {
+    RECT rcClient, rs;
+    LONG StatusHeight;
     HIMAGELIST hSmall;
     LVCOLUMNW lvc = {0};
     WCHAR szTemp[256];
 
-    hInst = hInstance; // Store instance handle in our global variable
-
+    /* Create the main window */
     hwndMainWindow = CreateWindowW(szWindowClass,
-                                  szTitle,
-                                  WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
-                                  CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
-                                  NULL,
-                                  NULL,
-                                  hInstance,
-                                  NULL);
+                                   szTitle,
+                                   WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
+                                   CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
+                                   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
+    ProgressBar_SetStep(hwndStatusProgress, 1);
+
+    /* Initialize the splitter default positions */
+    nVSplitPos = 250;
+    nHSplitPos = 250;
+
+    /* 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);
+    // hwndEventDetails = NULL;
+    if (hwndEventDetails)
+    {
+    // SetWindowLongPtrW(hwndEventDetails, GWL_STYLE,
+                      // GetWindowLongPtrW(hwndEventDetails, GWL_STYLE) | WS_BORDER);
+    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 | SWP_SHOWWINDOW);
+    }
 
-    // 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 ? 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);
+
+    /* 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));
+
+    LoadStringW(hInst, IDS_SAVE_FILTER, szSaveFilter, ARRAYSIZE(szSaveFilter));
+
+    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, nCmdShow);
     UpdateWindow(hwndMainWindow);
 
-    QueryEventMessages(lpComputerName,            // Use the local computer.
-                       EVENT_SOURCE_APPLICATION); // The event log category
-
     return TRUE;
 }
 
+VOID ResizeWnd(INT cx, INT cy)
+{
+    HDWP hdwp;
+    RECT rs;
+    LONG StatusHeight;
+
+    /* 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 */
+    StatusBar_GetItemRect(hwndStatus, 0, &rs);
+    MoveWindow(hwndStatusProgress,
+               rs.left, rs.top, rs.right-rs.left, rs.bottom-rs.top,
+               IsWindowVisible(hwndStatusProgress) ? TRUE : FALSE);
+
+    /*
+     * 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 ? nHSplitPos - SPLIT_WIDTH/2
+                                               : cy - StatusHeight,
+                              SWP_NOZORDER | SWP_NOACTIVATE);
+
+    if (hwndEventDetails && 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_NOTIFY:
-            switch (((LPNMHDR)lParam)->code)
-            {
-                case NM_DBLCLK :
-                    hdr = (NMHDR FAR*)lParam;
-                    if (hdr->hwndFrom == hwndListView)
+        case WM_DESTROY:
+            PostQuitMessage(0);
+            break;
+
+        case WM_NOTIFY:
+        {
+            LPNMHDR hdr = (LPNMHDR)lParam;
+
+            if (hdr->hwndFrom == hwndListView)
+            {
+                switch (hdr->code)
+                {
+                    case LVN_ITEMCHANGED:
+                    {
+                        LPNMLISTVIEW pnmv = (LPNMLISTVIEW)lParam;
+
+                        if ( (pnmv->uChanged  & LVIF_STATE) && /* The state has changed */
+                             (pnmv->uNewState & LVIS_SELECTED) /* The item has been (de)selected */ )
+                        {
+                            if (hwndEventDetails)
+                                SendMessageW(hwndEventDetails, EVT_DISPLAY, 0, 0);
+                        }
+                        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 all whitespace */
+                            while (*pszText && iswspace(*pszText))
+                                ++pszText;
+
+                            if (!*pszText)
+                                return FALSE;
+
+                            return TRUE;
+                        }
+                        else
+                        {
+                            return FALSE;
+                        }
+                    }
+
+                    case TVN_SELCHANGED:
                     {
-                        LPNMITEMACTIVATE lpnmitem = (LPNMITEMACTIVATE)lParam;
+                        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???
 
-                        if (lpnmitem->iItem != -1)
+                        // FIXME: This is a hack!!
+                        if (hwndEventDetails && EventLogFilter)
                         {
-                            DialogBox(hInst,
-                                      MAKEINTRESOURCE(IDD_EVENTDETAILDIALOG),
-                                      hWnd,
-                                      EventDetails);
+                            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;
+                }
             }
             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 (!NewestEventsFirst)
                     {
-                        CheckMenuRadioItem(GetMenu(hWnd),
-                                           ID_LOG_APPLICATION,
-                                           ID_LOG_SYSTEM,
-                                           ID_LOG_APPLICATION,
-                                           MF_BYCOMMAND);
+                        NewestEventsFirst = TRUE;
+                        Refresh(GetSelectedFilter(NULL));
                     }
                     break;
+                }
 
-                case ID_LOG_SECURITY:
-                    if (QueryEventMessages(lpComputerName,         // Use the local computer.
-                                           EVENT_SOURCE_SECURITY)) // The event log category
+                case IDM_LIST_OLDEST:
+                {
+                    CheckMenuRadioItem(hMainMenu, IDM_LIST_NEWEST, IDM_LIST_OLDEST, IDM_LIST_OLDEST, MF_BYCOMMAND);
+                    if (NewestEventsFirst)
                     {
-                        CheckMenuRadioItem(GetMenu(hWnd),
-                                           ID_LOG_APPLICATION,
-                                           ID_LOG_SYSTEM,
-                                           ID_LOG_SECURITY,
-                                           MF_BYCOMMAND);
+                        NewestEventsFirst = FALSE;
+                        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:
+                {
+                    // LPNMITEMACTIVATE lpnmitem = (LPNMITEMACTIVATE)lParam;
+                    PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(NULL);
+                    if (/*lpnmitem->iItem != -1 &&*/ EventLogFilter)
                     {
-                        CheckMenuRadioItem(GetMenu(hWnd),
-                                           ID_LOG_APPLICATION,
-                                           ID_LOG_SYSTEM,
-                                           ID_LOG_SYSTEM,
-                                           MF_BYCOMMAND);
+                        EventLogFilter_AddRef(EventLogFilter);
+                        DialogBoxParamW(hInst,
+                                        MAKEINTRESOURCEW(IDD_EVENTDETAILS_DLG),
+                                        hWnd,
+                                        EventDetails,
+                                        (LPARAM)EventLogFilter);
+                        EventLogFilter_Release(EventLogFilter);
                     }
                     break;
+                }
 
                 case IDM_REFRESH:
-                    Refresh();
+                    Refresh(GetSelectedFilter(NULL));
                     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_SETCURSOR:
+            if (LOWORD(lParam) == HTCLIENT)
             {
-                // Gets the window rectangle
-                GetClientRect(hWnd, &rect);
+                POINT pt;
+                GetCursorPos(&pt);
+                ScreenToClient(hWnd, &pt);
 
-                // Relocate the listview
-                MoveWindow(hwndListView,
-                           0,
-                           0,
-                           rect.right,
-                           rect.bottom - 20,
-                           1);
+                /* 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 &&
+                    (pt.y >= nHSplitPos - SPLIT_WIDTH/2 && pt.y < nHSplitPos + SPLIT_WIDTH/2 + 1))
+                {
+                    // 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;
 
-                // Resize the statusbar;
-                SendMessage(hwndStatus, message, wParam, lParam);
+        case WM_LBUTTONDOWN:
+        {
+            INT x = GET_X_LPARAM(lParam);
+            INT y = GET_Y_LPARAM(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 &&
+                (y >= nHSplitPos - SPLIT_WIDTH/2 && y < nHSplitPos + SPLIT_WIDTH/2 + 1))
+            {
+                bSplit = 2;
+                SetCapture(hWnd);
             }
             break;
-        case WM_DESTROY:
-            FreeRecords();
-            PostQuitMessage(0);
-            break;
+        }
 
-        default:
-            return DefWindowProc(hWnd, message, wParam, lParam);
-    }
+        case WM_LBUTTONUP:
+        case WM_RBUTTONDOWN:
+            if (GetCapture() == hWnd)
+            {
+                /* 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);
+                }
 
-    return 0;
-}
+                /* Reset the splitter state */
+                bSplit = 0;
 
+                ReleaseCapture();
+            }
+            break;
 
-// 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:
+        case WM_MOUSEMOVE:
+            if (GetCapture() == hWnd)
             {
-                return (INT_PTR)TRUE;
+                /* Move the correct splitter */
+                if (bSplit == 1)
+                {
+                    INT x = GET_X_LPARAM(lParam);
+
+                    GetClientRect(hWnd, &rect);
+
+                    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)
+                {
+                    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, Type;
+    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(HKEY_LOCAL_MACHINE, 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,
+                              &Type,
+                              (LPBYTE)&dwMaxSize,
+                              &cbData);
+    if ((Result != ERROR_SUCCESS) || (Type != 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,
+                              &Type,
+                              (LPBYTE)&dwRetention,
+                              &cbData);
+    if ((Result != ERROR_SUCCESS) || (Type != 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)
+    {
+        ExpandEnvironmentStringsW(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:
@@ -1198,37 +3593,354 @@ EventDetails(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
                     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;