1 /*
2  *  ReactOS Win32 Applications
3  *  Copyright (C) 2007 ReactOS Team
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License along
16  *  with this program; if not, write to the Free Software Foundation, Inc.,
17  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 /*
20  * PROJECT:         ReactOS Event Log Viewer
21  * LICENSE:         GPL - See COPYING in the top level directory
22  * FILE:            base/applications/mscutils/eventvwr/eventvwr.c
23  * PURPOSE:         Event Log Viewer main file
24  * PROGRAMMERS:     Marc Piulachs (marc.piulachs at codexchange [dot] net)
25  *                  Eric Kohl
26  *                  Hermes Belusca-Maito
27  */
28 
29 #include "eventvwr.h"
30 #include "evtdetctl.h"
31 
32 #include <sddl.h> // For ConvertSidToStringSidW
33 #include <shellapi.h>
34 #include <shlwapi.h>
35 
36 // #include "resource.h"
37 
38 #define LVM_PROGRESS    (WM_APP + 1)    // Used by the subclassed ListView
39 
40 static const LPCWSTR EVENTVWR_WNDCLASS = L"EVENTVWR"; /* The main window class name */
41 static const LPCWSTR EVENTLOG_BASE_KEY = L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\";
42 static const LPCWSTR EVNTVWR_PARAM_KEY = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Event Viewer";
43 
44 /* The 3 system logs that should always exist in the user's system */
45 static const LPCWSTR SystemLogs[] =
46 {
47     L"Application",
48     L"Security",
49     L"System"
50 };
51 
52 /* MessageFile message buffer size */
53 #define EVENT_MESSAGE_EVENTTEXT_BUFFER  1024*10                             // NOTE: Used by evtdetctl.c
54 #define EVENT_MESSAGE_FILE_BUFFER       1024*10
55 #define EVENT_DLL_SEPARATOR             L";"
56 #define EVENT_CATEGORY_MESSAGE_FILE     L"CategoryMessageFile"
57 #define EVENT_MESSAGE_FILE              L"EventMessageFile"
58 #define EVENT_PARAMETER_MESSAGE_FILE    L"ParameterMessageFile"
59 
60 #define MAX_LOADSTRING 255
61 
62 #define SPLIT_WIDTH 4
63 
64 /* Globals */
65 HINSTANCE hInst;                            /* Current instance */
66 WCHAR szTitle[MAX_LOADSTRING];              /* The title bar text */
67 WCHAR szTitleTemplate[MAX_LOADSTRING];      /* The logged-on title bar text */
68 WCHAR szStatusBarTemplate[MAX_LOADSTRING];  /* The status bar text */
69 WCHAR szLoadingWait[MAX_LOADSTRING];        /* The "Loading, please wait..." text */
70 WCHAR szEmptyList[MAX_LOADSTRING];          /* The "There are no items to show in this view" text */
71 WCHAR szSaveFilter[MAX_LOADSTRING];         /* Filter Mask for the save Dialog */
72 
73 INT  nVSplitPos;                            /* Vertical splitter (1) position */
74 INT  nHSplitPos;                            /* Horizontal splitter (2) position */
75 BYTE bSplit = 0;                            /* Splitter state:
76                                              * 0: No splitting;
77                                              * 1: Vertical splitting;
78                                              * 2: Horizontal splitting.
79                                              */
80 
81 HWND hwndMainWindow = NULL;                 /* Main window */
82 HWND hwndTreeView;                          /* TreeView control */
83 HWND hwndListView;                          /* ListView control */          // NOTE: Used by evtdetctl.c
84 HWND hwndEventDetails;                      /* Event details pane */
85 HWND hwndStatus;                            /* Status bar */
86 HWND hwndStatusProgress;                    /* Progress bar in the status bar */
87 HMENU hMainMenu;                            /* The application's main menu */
88 
89 HTREEITEM htiSystemLogs = NULL, htiAppLogs = NULL, htiUserLogs = NULL;
90 
91 LPWSTR lpComputerName = NULL;       /* NULL: local user computer (default) */
92 LPWSTR lpszzUserLogsToLoad = NULL;  /* The list of user logs to load at startup (multi-string) */
93 SIZE_T cbUserLogsSize = 0;
94 
95 HKEY hkMachine = NULL; // Registry handle to the HKEY_LOCAL_MACHINE key of the remote computer registry.
96 
97 /* Global event records cache for the current active event log filter */
98 DWORD g_TotalRecords = 0;
99 PEVENTLOGRECORD* g_RecordPtrs = NULL;
100 
101 /* Lists of event logs and event log filters */
102 LIST_ENTRY EventLogList;
103 LIST_ENTRY EventLogFilterList;
104 PEVENTLOGFILTER ActiveFilter = NULL;
105 
106 HANDLE hEnumEventsThread = NULL;
107 HANDLE hStopEnumEvent    = NULL;
108 
109 /*
110  * Setting EnumFilter to a valid pointer and raising the hStartEnumEvent event
111  * triggers the event-enumerator thread to perform a new enumeration.
112  */
113 PEVENTLOGFILTER EnumFilter = NULL;
114 HANDLE hStartStopEnumEvent = NULL;  // End-of-application event
115 HANDLE hStartEnumEvent     = NULL;  // Command event
116 
117 /* Default Open/Save-As dialog box */
118 OPENFILENAMEW sfn;
119 
120 
121 /* Event Viewer Application Settings */
122 typedef struct _SETTINGS
123 {
124     BOOL bShowDetailsPane;      /* Show (TRUE) or Hide (FALSE) the events details pane */
125     BOOL bShowGrid;             /* Show (TRUE) or Hide (FALSE) the events view grid */
126     BOOL bSaveSettings;         /* Save (TRUE) or do not save (FALSE) current settings on exit */
127     BOOL bNewestEventsFirst;    /* Sort the displayed events the newest ones first (TRUE) or last (FALSE) */
128     INT  nVSplitPos;            /* Vertical splitter position */
129     INT  nHSplitPos;            /* Horizontal splitter position */
130     WINDOWPLACEMENT wpPos;
131 } SETTINGS, *PSETTINGS;
132 
133 SETTINGS Settings;
134 
135 
136 /* Forward declarations of functions included in this code module */
137 
138 static DWORD WINAPI
139 StartStopEnumEventsThread(IN LPVOID lpParameter);
140 
141 VOID OpenUserEventLogFile(IN LPCWSTR lpszFileName);
142 
143 VOID BuildLogListAndFilterList(IN LPCWSTR lpComputerName);
144 VOID FreeLogList(VOID);
145 VOID FreeLogFilterList(VOID);
146 
147 ATOM MyRegisterClass(HINSTANCE);
148 BOOL InitInstance(HINSTANCE);
149 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
150 INT_PTR EventLogProperties(HINSTANCE, HWND, PEVENTLOGFILTER);
151 INT_PTR CALLBACK EventDetails(HWND, UINT, WPARAM, LPARAM);
152 
153 
154 /* MAIN FUNCTIONS *************************************************************/
155 
156 VOID
157 ShowWin32Error(IN DWORD dwError)
158 {
159     LPWSTR lpMessageBuffer;
160 
161     if (dwError == ERROR_SUCCESS)
162         return;
163 
164     if (!FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
165                         FORMAT_MESSAGE_FROM_SYSTEM |
166                         FORMAT_MESSAGE_IGNORE_INSERTS,
167                         NULL,
168                         dwError,
169                         LANG_USER_DEFAULT,
170                         (LPWSTR)&lpMessageBuffer,
171                         0, NULL))
172     {
173         return;
174     }
175 
176     MessageBoxW(hwndMainWindow, lpMessageBuffer, szTitle, MB_OK | MB_ICONERROR);
177     LocalFree(lpMessageBuffer);
178 }
179 
180 VOID
181 DisplayUsage(VOID)
182 {
183     LPWSTR lpBuffer;
184     LPCWSTR lpUsage;
185     INT iUsageLen = LoadStringW(hInst, IDS_USAGE, (LPWSTR)&lpUsage, 0);
186 
187     if (iUsageLen == 0)
188         return;
189 
190     lpBuffer = HeapAlloc(GetProcessHeap(), 0, (iUsageLen + 1) * sizeof(WCHAR));
191     if (!lpBuffer)
192         return;
193 
194     StringCchCopyNW(lpBuffer, iUsageLen + 1, lpUsage, iUsageLen);
195     MessageBoxW(NULL, lpBuffer, szTitle, MB_OK | MB_ICONINFORMATION);
196 
197     HeapFree(GetProcessHeap(), 0, lpBuffer);
198 }
199 
200 BOOL
201 ProcessCmdLine(IN LPWSTR lpCmdLine)
202 {
203     BOOL Success = FALSE;
204     INT i, argc;
205     LPWSTR* argv;
206 
207     /* Skip any leading whitespace */
208     if (lpCmdLine)
209     {
210         while (iswspace(*lpCmdLine))
211             ++lpCmdLine;
212     }
213 
214     /* No command line means no processing needed */
215     if (!lpCmdLine || !*lpCmdLine)
216         return TRUE;
217 
218     /* Build the arguments vector */
219     argv = CommandLineToArgvW(lpCmdLine, &argc);
220     if (!argv)
221         return FALSE;
222 
223     /* Parse the command line for options (skip the program name) */
224     for (i = 1; i < argc; ++i)
225     {
226         /* Check for new options */
227         if (argv[i][0] == L'-' || argv[i][0] == L'/')
228         {
229             if (argv[i][1] == L'?' && argv[i][2] == 0)
230             {
231                 /* Display help */
232                 DisplayUsage();
233                 goto Quit;
234             }
235             else
236             if (argv[i][2] == L':')
237             {
238                 switch (towupper(argv[i][1]))
239                 {
240                     case L'L':
241                     {
242                         LPWSTR lpNewBuffer;
243                         LPWSTR lpFileName = argv[i] + 3;
244                         SIZE_T cbFileName;
245 
246                         /* Check for a quoted file name */
247                         if (*lpFileName == L'\"')
248                         {
249                             /* Skip this quote, and the last one too if any */
250                             ++lpFileName;
251                             cbFileName = wcslen(lpFileName);
252                             if (cbFileName > 0 && lpFileName[cbFileName - 1] == L'\"')
253                                 lpFileName[cbFileName - 1] = UNICODE_NULL;
254                         }
255 
256                         /* Skip this one if we do not actually have a file name */
257                         if (!*lpFileName)
258                             continue;
259 
260                         cbFileName = (wcslen(lpFileName) + 1) * sizeof(WCHAR);
261 
262                         /* Reallocate the list of user logs to load */
263                         if (lpszzUserLogsToLoad)
264                         {
265                             lpNewBuffer = HeapReAlloc(GetProcessHeap(),
266                                                       HEAP_ZERO_MEMORY,
267                                                       lpszzUserLogsToLoad,
268                                                       /* Count the multi-string NULL-terminator */
269                                                       cbUserLogsSize + cbFileName + sizeof(WCHAR));
270                         }
271                         else
272                         {
273                             cbUserLogsSize = 0;
274                             lpNewBuffer = HeapAlloc(GetProcessHeap(),
275                                                     HEAP_ZERO_MEMORY,
276                                                     /* Count the multi-string NULL-terminator */
277                                                     cbUserLogsSize + cbFileName + sizeof(WCHAR));
278                         }
279 
280                         if (!lpNewBuffer)
281                         {
282                             ShowWin32Error(ERROR_NOT_ENOUGH_MEMORY);
283                             goto Quit;
284                         }
285 
286                         lpszzUserLogsToLoad = lpNewBuffer;
287                         lpNewBuffer = (LPWSTR)((ULONG_PTR)lpNewBuffer + cbUserLogsSize);
288                         cbUserLogsSize += cbFileName;
289 
290                         /* Save the file name */
291                         StringCbCopyW(lpNewBuffer, cbFileName, lpFileName);
292 
293                         continue;
294                     }
295 
296                     default:
297                         break;
298                 }
299             }
300 
301             /* Unknown argument: display help and bail out */
302             DisplayUsage();
303             goto Quit;
304         }
305         else
306         {
307             /*
308              * An argument that does not start with the switch character.
309              * If this is the first argument then this corresponds to the
310              * optional computer name. Otherwise this is a wrong argument.
311              */
312             if (i == 1)
313             {
314                 /* Store the computer name */
315                 LPWSTR lpTemp = argv[i];
316                 SIZE_T cbLength;
317 
318                 /* Strip any leading backslashes */
319                 while (*lpTemp == L'\\')
320                     ++lpTemp;
321 
322                 cbLength = (wcslen(lpTemp) + 1) * sizeof(WCHAR);
323                 lpComputerName = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbLength);
324                 if (lpComputerName)
325                 {
326                     StringCbCopyW(lpComputerName, cbLength, lpTemp);
327                 }
328                 /* else, fall back to local computer */
329             }
330             else
331             {
332                 /* Invalid syntax: display help and bail out */
333                 DisplayUsage();
334                 goto Quit;
335             }
336         }
337     }
338 
339     Success = TRUE;
340 
341 Quit:
342     /* In case of failure, free anything we have allocated */
343     if (!Success)
344     {
345         if (lpszzUserLogsToLoad)
346         {
347             cbUserLogsSize = 0;
348             HeapFree(GetProcessHeap(), 0, lpszzUserLogsToLoad);
349             lpszzUserLogsToLoad = NULL;
350         }
351         if (lpComputerName)
352         {
353             HeapFree(GetProcessHeap(), 0, lpComputerName);
354             lpComputerName = NULL;
355         }
356     }
357 
358     /* Free the arguments vector and exit */
359     LocalFree(argv);
360     return Success;
361 }
362 
363 BOOL
364 LoadSettings(int nDefCmdShow)
365 {
366     HKEY hKeyEventVwr;
367     LONG Result;
368     DWORD dwSize;
369     DWORD dwType;
370     DWORD Value;
371     UNICODE_STRING ValueU;
372     WCHAR buffer[100];
373 
374     /* Load the default values */
375     Settings.bShowDetailsPane = TRUE;
376     Settings.bShowGrid = FALSE;
377     Settings.bSaveSettings = TRUE;
378     Settings.bNewestEventsFirst = TRUE;
379     Settings.nVSplitPos = 250; /* Splitter default positions */
380     Settings.nHSplitPos = 250;
381     ZeroMemory(&Settings.wpPos, sizeof(Settings.wpPos));
382     Settings.wpPos.length = sizeof(Settings.wpPos);
383     Settings.wpPos.rcNormalPosition.left = CW_USEDEFAULT;
384     Settings.wpPos.rcNormalPosition.top  = CW_USEDEFAULT;
385     Settings.wpPos.rcNormalPosition.right  = CW_USEDEFAULT;
386     Settings.wpPos.rcNormalPosition.bottom = CW_USEDEFAULT;
387 
388     /* Try to create/open the Event Viewer user key */
389     if (RegCreateKeyExW(HKEY_CURRENT_USER,
390                         EVNTVWR_PARAM_KEY,
391                         0,
392                         NULL,
393                         REG_OPTION_NON_VOLATILE,
394                         KEY_READ | KEY_WRITE,
395                         NULL,
396                         &hKeyEventVwr,
397                         NULL) != ERROR_SUCCESS)
398     {
399         return FALSE;
400     }
401 
402     // Result = RegQueryValueExW(hKeyEventVwr, L"Filter", NULL, &dwType, (LPBYTE)&szFilter, &dwSize); // REG_SZ
403     // Result = RegQueryValueExW(hKeyEventVwr, L"Find", NULL, &dwType, (LPBYTE)&szFind, &dwSize); // REG_SZ
404     // Result = RegQueryValueExW(hKeyEventVwr, L"Module", NULL, &dwType, (LPBYTE)&szModule, &dwSize); // REG_SZ
405 
406     dwSize = sizeof(Value);
407     Result = RegQueryValueExW(hKeyEventVwr, L"DetailsPane", NULL, &dwType, (LPBYTE)&Value, &dwSize);
408     if ((Result == ERROR_SUCCESS) && (dwType == REG_SZ || dwType == REG_DWORD))
409     {
410         if (dwType == REG_SZ)
411         {
412             ValueU.Buffer = (PWSTR)&Value;
413             ValueU.Length = ValueU.MaximumLength = dwSize;
414             RtlUnicodeStringToInteger(&ValueU, 10, &Value);
415         }
416         Settings.bShowDetailsPane = !!Value;
417     }
418 
419     dwSize = sizeof(Value);
420     Result = RegQueryValueExW(hKeyEventVwr, L"ShowGrid", NULL, &dwType, (LPBYTE)&Value, &dwSize);
421     if ((Result == ERROR_SUCCESS) && (dwType == REG_SZ || dwType == REG_DWORD))
422     {
423         if (dwType == REG_SZ)
424         {
425             ValueU.Buffer = (PWSTR)&Value;
426             ValueU.Length = ValueU.MaximumLength = dwSize;
427             RtlUnicodeStringToInteger(&ValueU, 10, &Value);
428         }
429         Settings.bShowGrid = !!Value;
430     }
431 
432     dwSize = sizeof(Value);
433     Result = RegQueryValueExW(hKeyEventVwr, L"SortOrder", NULL, &dwType, (LPBYTE)&Value, &dwSize);
434     if ((Result == ERROR_SUCCESS) && (dwType == REG_SZ || dwType == REG_DWORD))
435     {
436         if (dwType == REG_SZ)
437         {
438             ValueU.Buffer = (PWSTR)&Value;
439             ValueU.Length = ValueU.MaximumLength = dwSize;
440             RtlUnicodeStringToInteger(&ValueU, 10, &Value);
441         }
442         Settings.bNewestEventsFirst = !!Value;
443     }
444 
445     /* Retrieve the splitter positions */
446     dwSize = sizeof(Value);
447     Result = RegQueryValueExW(hKeyEventVwr, L"VSplitPos", NULL, &dwType, (LPBYTE)&Value, &dwSize);
448     if ((Result == ERROR_SUCCESS) && (dwType == REG_SZ || dwType == REG_DWORD))
449     {
450         if (dwType == REG_SZ)
451         {
452             ValueU.Buffer = (PWSTR)&Value;
453             ValueU.Length = ValueU.MaximumLength = dwSize;
454             RtlUnicodeStringToInteger(&ValueU, 10, &Value);
455         }
456         Settings.nVSplitPos = Value;
457     }
458 
459     dwSize = sizeof(Value);
460     Result = RegQueryValueExW(hKeyEventVwr, L"HSplitPos", NULL, &dwType, (LPBYTE)&Value, &dwSize);
461     if ((Result == ERROR_SUCCESS) && (dwType == REG_SZ || dwType == REG_DWORD))
462     {
463         if (dwType == REG_SZ)
464         {
465             ValueU.Buffer = (PWSTR)&Value;
466             ValueU.Length = ValueU.MaximumLength = dwSize;
467             RtlUnicodeStringToInteger(&ValueU, 10, &Value);
468         }
469         Settings.nHSplitPos = Value;
470     }
471 
472     /* Retrieve the geometry of the main window */
473     dwSize = sizeof(buffer);
474     Result = RegQueryValueExW(hKeyEventVwr, L"Window", NULL, &dwType, (LPBYTE)buffer, &dwSize);
475     if ((Result != ERROR_SUCCESS) || (dwType != REG_SZ))
476         buffer[0] = UNICODE_NULL;
477 
478     if (swscanf(buffer, L"%d %d %d %d %d",
479                 &Settings.wpPos.rcNormalPosition.left,
480                 &Settings.wpPos.rcNormalPosition.top,
481                 &Settings.wpPos.rcNormalPosition.right,
482                 &Settings.wpPos.rcNormalPosition.bottom,
483                 &Settings.wpPos.showCmd) != 5)
484     {
485         /* Parsing failed, use defaults */
486         Settings.wpPos.rcNormalPosition.left = CW_USEDEFAULT;
487         Settings.wpPos.rcNormalPosition.top  = CW_USEDEFAULT;
488         Settings.wpPos.rcNormalPosition.right  = CW_USEDEFAULT;
489         Settings.wpPos.rcNormalPosition.bottom = CW_USEDEFAULT;
490         Settings.wpPos.showCmd = nDefCmdShow; // SW_SHOWNORMAL;
491     }
492 
493     dwSize = sizeof(Value);
494     Result = RegQueryValueExW(hKeyEventVwr, L"SaveSettings", NULL, &dwType, (LPBYTE)&Value, &dwSize);
495     if ((Result == ERROR_SUCCESS) && (dwType == REG_SZ || dwType == REG_DWORD))
496     {
497         if (dwType == REG_SZ)
498         {
499             ValueU.Buffer = (PWSTR)&Value;
500             ValueU.Length = ValueU.MaximumLength = dwSize;
501             RtlUnicodeStringToInteger(&ValueU, 10, &Value);
502         }
503         Settings.bSaveSettings = !!Value;
504     }
505 
506     RegCloseKey(hKeyEventVwr);
507     return TRUE;
508 }
509 
510 BOOL
511 SaveSettings(VOID)
512 {
513     HKEY hKeyEventVwr;
514     DWORD dwSize;
515     WCHAR buffer[100];
516 
517     /* Try to create/open the Event Viewer user key */
518     if (RegCreateKeyExW(HKEY_CURRENT_USER,
519                         EVNTVWR_PARAM_KEY,
520                         0,
521                         NULL,
522                         REG_OPTION_NON_VOLATILE,
523                         KEY_READ | KEY_WRITE,
524                         NULL,
525                         &hKeyEventVwr,
526                         NULL) != ERROR_SUCCESS)
527     {
528         return FALSE;
529     }
530 
531     dwSize = sizeof(Settings.bSaveSettings);
532     RegSetValueExW(hKeyEventVwr, L"SaveSettings", 0, REG_DWORD, (LPBYTE)&Settings.bSaveSettings, dwSize);
533 
534     /* Do not save more settings if we are not asked to do so */
535     if (!Settings.bSaveSettings)
536         goto Quit;
537 
538     dwSize = sizeof(Settings.bShowDetailsPane);
539     RegSetValueExW(hKeyEventVwr, L"DetailsPane", 0, REG_DWORD, (LPBYTE)&Settings.bShowDetailsPane, dwSize);
540 
541     dwSize = sizeof(Settings.bShowGrid);
542     RegSetValueExW(hKeyEventVwr, L"ShowGrid", 0, REG_DWORD, (LPBYTE)&Settings.bShowGrid, dwSize);
543 
544     dwSize = sizeof(Settings.bNewestEventsFirst);
545     RegSetValueExW(hKeyEventVwr, L"SortOrder", 0, REG_DWORD, (LPBYTE)&Settings.bNewestEventsFirst, dwSize);
546 
547     Settings.nVSplitPos = nVSplitPos;
548     dwSize = sizeof(Settings.nVSplitPos);
549     RegSetValueExW(hKeyEventVwr, L"VSplitPos", 0, REG_DWORD, (LPBYTE)&Settings.nVSplitPos, dwSize);
550 
551     Settings.nHSplitPos = nHSplitPos;
552     dwSize = sizeof(Settings.nHSplitPos);
553     RegSetValueExW(hKeyEventVwr, L"HSplitPos", 0, REG_DWORD, (LPBYTE)&Settings.nHSplitPos, dwSize);
554 
555     StringCbPrintfW(buffer, sizeof(buffer),
556                     L"%d %d %d %d %d",
557                     Settings.wpPos.rcNormalPosition.left,
558                     Settings.wpPos.rcNormalPosition.top,
559                     Settings.wpPos.rcNormalPosition.right,
560                     Settings.wpPos.rcNormalPosition.bottom,
561                     Settings.wpPos.showCmd);
562 
563     dwSize = wcslen(buffer) * sizeof(WCHAR);
564     RegSetValueExW(hKeyEventVwr, L"Window", 0, REG_SZ, (LPBYTE)buffer, dwSize);
565 
566 Quit:
567     RegCloseKey(hKeyEventVwr);
568     return TRUE;
569 }
570 
571 int APIENTRY
572 wWinMain(HINSTANCE hInstance,
573          HINSTANCE hPrevInstance,
574          LPWSTR    lpCmdLine,
575          int       nCmdShow)
576 {
577     HANDLE hThread;
578     INITCOMMONCONTROLSEX iccx;
579     HMODULE hRichEdit;
580     HACCEL hAccelTable;
581     MSG msg;
582 
583     UNREFERENCED_PARAMETER(hPrevInstance);
584     UNREFERENCED_PARAMETER(lpCmdLine);
585 
586     /* Whenever any of the common controls are used in your app,
587      * you must call InitCommonControlsEx() to register the classes
588      * for those controls. */
589     iccx.dwSize = sizeof(iccx);
590     iccx.dwICC = ICC_LISTVIEW_CLASSES;
591     InitCommonControlsEx(&iccx);
592 
593     /* Load the RichEdit DLL to add support for RichEdit controls */
594     hRichEdit = LoadLibraryW(L"riched20.dll");
595     if (!hRichEdit)
596         return -1;
597 
598     msg.wParam = (WPARAM)-1;
599 
600     /* Store the instance handle in the global variable */
601     hInst = hInstance;
602 
603     /* Initialize global strings */
604     LoadStringW(hInstance, IDS_APP_TITLE, szTitle, ARRAYSIZE(szTitle));
605     LoadStringW(hInstance, IDS_APP_TITLE_EX, szTitleTemplate, ARRAYSIZE(szTitleTemplate));
606     LoadStringW(hInstance, IDS_STATUS_MSG, szStatusBarTemplate, ARRAYSIZE(szStatusBarTemplate));
607     LoadStringW(hInstance, IDS_LOADING_WAIT, szLoadingWait, ARRAYSIZE(szLoadingWait));
608     LoadStringW(hInstance, IDS_NO_ITEMS, szEmptyList, ARRAYSIZE(szEmptyList));
609 
610     /*
611      * Process the command-line arguments. Note that we need the full
612      * command-line, with the program file included, and not just what
613      * WinMain() provides in its lpCmdLine parameter.
614      */
615     if (!ProcessCmdLine(GetCommandLineW()))
616         goto Quit;
617 
618     if (!MyRegisterClass(hInstance))
619         goto Quit;
620 
621     /* Load the settings */
622     LoadSettings(nCmdShow);
623 
624     /* Perform application initialization */
625     if (!InitInstance(hInstance))
626         goto Quit;
627 
628     hAccelTable = LoadAcceleratorsW(hInstance, MAKEINTRESOURCEW(IDA_EVENTVWR));
629 
630     /* Create the Start/Stop enumerator thread */
631     // Manual-reset event
632     hStartStopEnumEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
633     if (!hStartStopEnumEvent)
634         goto Cleanup;
635 
636     // Auto-reset event
637     hStartEnumEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
638     if (!hStartEnumEvent)
639         goto Cleanup;
640 
641     hThread = CreateThread(NULL, 0,
642                            StartStopEnumEventsThread,
643                            NULL, 0, NULL);
644     if (!hThread)
645         goto Cleanup;
646 
647     /* Retrieve the available event logs on this computer and create filters for them */
648     InitializeListHead(&EventLogList);
649     InitializeListHead(&EventLogFilterList);
650     BuildLogListAndFilterList(lpComputerName);
651 
652     /* Open the user-specified logs if any are present on the command-line */
653     if (lpszzUserLogsToLoad)
654     {
655         LPWSTR lpUserLog;
656         for (lpUserLog = lpszzUserLogsToLoad; *lpUserLog; lpUserLog += wcslen(lpUserLog) + 1)
657         {
658             OpenUserEventLogFile(lpUserLog);
659         }
660 
661         /* Now cleanup the list of user logs */
662         cbUserLogsSize = 0;
663         HeapFree(GetProcessHeap(), 0, lpszzUserLogsToLoad);
664         lpszzUserLogsToLoad = NULL;
665     }
666 
667     /* Main message loop */
668     while (GetMessageW(&msg, NULL, 0, 0))
669     {
670         if (!TranslateAcceleratorW(hwndMainWindow, hAccelTable, &msg))
671         {
672             TranslateMessage(&msg);
673             DispatchMessageW(&msg);
674         }
675     }
676 
677     /* Save the settings */
678     SaveSettings();
679 
680     /* Disconnect from computer */
681     if (hkMachine && hkMachine != HKEY_LOCAL_MACHINE)
682     {
683         /* We are connected to some other computer, close the old connection */
684         RegCloseKey(hkMachine);
685         hkMachine = NULL;
686     }
687 
688     /* Stop the enumerator thread */
689     SetEvent(hStartStopEnumEvent);
690     WaitForSingleObject(hThread, INFINITE);
691     CloseHandle(hThread);
692 
693     /* Free the filters list and the event logs list */
694     FreeLogFilterList();
695     FreeLogList();
696 
697 Cleanup:
698     /* Handle cleanup */
699     if (hStartEnumEvent)
700         CloseHandle(hStartEnumEvent);
701     if (hStartStopEnumEvent)
702         CloseHandle(hStartStopEnumEvent);
703 
704 Quit:
705     /* Final cleanup */
706     if (lpszzUserLogsToLoad)
707     {
708         cbUserLogsSize = 0;
709         HeapFree(GetProcessHeap(), 0, lpszzUserLogsToLoad);
710         lpszzUserLogsToLoad = NULL;
711     }
712     if (lpComputerName)
713     {
714         HeapFree(GetProcessHeap(), 0, lpComputerName);
715         lpComputerName = NULL;
716     }
717     FreeLibrary(hRichEdit);
718 
719     return (int)msg.wParam;
720 }
721 
722 
723 /* GENERIC HELPER FUNCTIONS ***************************************************/
724 
725 VOID
726 EventTimeToSystemTime(IN DWORD EventTime,
727                       OUT PSYSTEMTIME pSystemTime)
728 {
729     SYSTEMTIME st1970 = { 1970, 1, 0, 1, 0, 0, 0, 0 };
730     FILETIME ftLocal;
731     union
732     {
733         FILETIME ft;
734         ULONGLONG ll;
735     } u1970, uUCT;
736 
737     uUCT.ft.dwHighDateTime = 0;
738     uUCT.ft.dwLowDateTime = EventTime;
739     SystemTimeToFileTime(&st1970, &u1970.ft);
740     uUCT.ll = uUCT.ll * 10000000 + u1970.ll;
741     FileTimeToLocalFileTime(&uUCT.ft, &ftLocal);
742     FileTimeToSystemTime(&ftLocal, pSystemTime);
743 }
744 
745 /*
746  * This function takes in entry a path to a single DLL, in which
747  * the message string of ID dwMessageId has to be searched.
748  * The other parameters are similar to those of the FormatMessageW API.
749  */
750 LPWSTR
751 GetMessageStringFromDll(
752     IN LPCWSTR  lpMessageDll,
753     IN DWORD    dwFlags, // If we always use the same flags, just remove this param...
754     IN DWORD    dwMessageId,
755     IN DWORD    nSize,
756     IN va_list* Arguments OPTIONAL)
757 {
758     HMODULE hLibrary;
759     DWORD dwLength;
760     LPWSTR lpMsgBuf = NULL;
761 
762     hLibrary = LoadLibraryExW(lpMessageDll, NULL,
763                               /* LOAD_LIBRARY_AS_IMAGE_RESOURCE | */ LOAD_LIBRARY_AS_DATAFILE);
764     if (hLibrary == NULL)
765         return NULL;
766 
767     /* Sanitize dwFlags */
768     dwFlags &= ~FORMAT_MESSAGE_FROM_STRING;
769     dwFlags |= FORMAT_MESSAGE_FROM_HMODULE;
770 
771     _SEH2_TRY
772     {
773         /*
774          * Retrieve the message string without appending extra newlines.
775          * Wrap in SEH to protect from invalid string parameters.
776          */
777         _SEH2_TRY
778         {
779             dwLength = FormatMessageW(dwFlags,
780                                    /* FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
781                                       FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, */
782                                       hLibrary,
783                                       dwMessageId,
784                                       LANG_USER_DEFAULT,
785                                       (LPWSTR)&lpMsgBuf,
786                                       nSize,
787                                       Arguments);
788         }
789         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
790         {
791             dwLength = 0;
792 
793             /*
794              * An exception occurred while calling FormatMessage, this is usually
795              * the sign that a parameter was invalid, either 'lpMsgBuf' was NULL
796              * but we did not pass the flag FORMAT_MESSAGE_ALLOCATE_BUFFER, or the
797              * array pointer 'Arguments' was NULL or did not contain enough elements,
798              * and we did not pass the flag FORMAT_MESSAGE_IGNORE_INSERTS, and the
799              * message string expected too many inserts.
800              * In this last case only, we can call again FormatMessage but ignore
801              * explicitly the inserts. The string that we will return to the user
802              * will not be pre-formatted.
803              */
804             if (((dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) || lpMsgBuf) &&
805                 !(dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS))
806             {
807                 /* Remove any possible harmful flags and always ignore inserts */
808                 dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY;
809                 dwFlags |=  FORMAT_MESSAGE_IGNORE_INSERTS;
810 
811                 /* If this call also throws an exception, we are really dead */
812                 dwLength = FormatMessageW(dwFlags,
813                                           hLibrary,
814                                           dwMessageId,
815                                           LANG_USER_DEFAULT,
816                                           (LPWSTR)&lpMsgBuf,
817                                           nSize,
818                                           NULL /* Arguments */);
819             }
820         }
821         _SEH2_END;
822     }
823     _SEH2_FINALLY
824     {
825         FreeLibrary(hLibrary);
826     }
827     _SEH2_END;
828 
829     if (dwLength == 0)
830     {
831         ASSERT(lpMsgBuf == NULL);
832         lpMsgBuf = NULL;
833     }
834     else
835     {
836         LPWSTR ptr;
837 
838         ASSERT(lpMsgBuf);
839 
840         /* Trim any trailing whitespace */
841         ptr = lpMsgBuf + dwLength - 1;
842         while (iswspace(*ptr))
843             *ptr-- = UNICODE_NULL;
844     }
845 
846     return lpMsgBuf;
847 }
848 
849 /*
850  * This function takes in entry a comma-separated list of DLLs, in which
851  * the message string of ID dwMessageId has to be searched.
852  * The other parameters are similar to those of the FormatMessageW API.
853  */
854 LPWSTR
855 GetMessageStringFromDllList(
856     IN LPCWSTR  lpMessageDllList,
857     IN DWORD    dwFlags, // If we always use the same flags, just remove this param...
858     IN DWORD    dwMessageId,
859     IN DWORD    nSize,
860     IN va_list* Arguments OPTIONAL)
861 {
862     BOOL Success = FALSE;
863     SIZE_T cbLength;
864     LPWSTR szMessageDllList;
865     LPWSTR szDll;
866     LPWSTR lpMsgBuf = NULL;
867 
868     /* Allocate a local buffer for the DLL list that can be tokenized */
869     // TODO: Optimize that!! Maybe we can cleverly use lpMessageDllList in read/write mode
870     // and cleverly temporarily replace the ';' by UNICODE_NULL, do our job, then reverse the change.
871     cbLength = (wcslen(lpMessageDllList) + 1) * sizeof(WCHAR);
872     szMessageDllList = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbLength);
873     if (!szMessageDllList)
874         return NULL;
875     RtlCopyMemory(szMessageDllList, lpMessageDllList, cbLength);
876 
877     /* Loop through the list of message DLLs */
878     szDll = wcstok(szMessageDllList, EVENT_DLL_SEPARATOR);
879     while ((szDll != NULL) && !Success)
880     {
881         // Uses LANG_USER_DEFAULT
882         lpMsgBuf = GetMessageStringFromDll(szDll,
883                                            dwFlags,
884                                            dwMessageId,
885                                            nSize,
886                                            Arguments);
887         if (lpMsgBuf)
888         {
889             /* The ID was found and the message was formatted */
890             Success = TRUE;
891             break;
892         }
893 
894         /*
895          * The DLL could not be loaded, or the message could not be found,
896          * try the next DLL, if any.
897          */
898         szDll = wcstok(NULL, EVENT_DLL_SEPARATOR);
899     }
900 
901     HeapFree(GetProcessHeap(), 0, szMessageDllList);
902 
903     return lpMsgBuf;
904 }
905 
906 
907 typedef struct
908 {
909     LPWSTR pStartingAddress;    // Pointer to the beginning of a parameter string in pMessage
910     LPWSTR pEndingAddress;      // Pointer to the end of a parameter string in pMessage
911     DWORD  pParameterID;        // Parameter identifier found in pMessage
912     LPWSTR pParameter;          // Actual parameter string
913 } param_strings_format_data;
914 
915 DWORD
916 ApplyParameterStringsToMessage(
917     IN LPCWSTR  lpMessageDllList,
918     IN BOOL     bMessagePreFormatted,
919     IN CONST LPCWSTR pMessage,
920     OUT LPWSTR* pFinalMessage)
921 {
922     /*
923      * This code is heavily adapted from the MSDN example:
924      * https://msdn.microsoft.com/en-us/library/windows/desktop/bb427356.aspx
925      * with bugs removed.
926      */
927 
928     DWORD Status = ERROR_SUCCESS;
929     DWORD dwParamCount = 0; // Number of insertion strings found in pMessage
930     size_t cchBuffer = 0;   // Size of the buffer in characters
931     size_t cchParams = 0;   // Number of characters in all the parameter strings
932     size_t cch = 0;
933     DWORD i = 0;
934     param_strings_format_data* pParamData = NULL; // Array of pointers holding information about each parameter string in pMessage
935     LPWSTR pTempMessage = (LPWSTR)pMessage;
936     LPWSTR pTempFinalMessage = NULL;
937 
938     *pFinalMessage = NULL;
939 
940     /* Determine the number of parameter insertion strings in pMessage */
941     if (bMessagePreFormatted)
942     {
943         while ((pTempMessage = wcschr(pTempMessage, L'%')))
944         {
945             pTempMessage++;
946             if (iswdigit(*pTempMessage))
947             {
948                 dwParamCount++;
949                 while (iswdigit(*++pTempMessage)) ;
950             }
951         }
952     }
953     else
954     {
955         while ((pTempMessage = wcsstr(pTempMessage, L"%%")))
956         {
957             pTempMessage += 2;
958             if (iswdigit(*pTempMessage))
959             {
960                 dwParamCount++;
961                 while (iswdigit(*++pTempMessage)) ;
962             }
963         }
964     }
965 
966     /* If there are no parameter insertion strings in pMessage, just return */
967     if (dwParamCount == 0)
968     {
969         // *pFinalMessage = NULL;
970         goto Cleanup;
971     }
972 
973     /* Allocate the array of parameter string format data */
974     pParamData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwParamCount * sizeof(param_strings_format_data));
975     if (!pParamData)
976     {
977         Status = ERROR_OUTOFMEMORY;
978         goto Cleanup;
979     }
980 
981     /*
982      * Retrieve each parameter in pMessage and the beginning and end of the
983      * insertion string, as well as the message identifier of the parameter.
984      */
985     pTempMessage = (LPWSTR)pMessage;
986     if (bMessagePreFormatted)
987     {
988         while ((pTempMessage = wcschr(pTempMessage, L'%')) && (i < dwParamCount))
989         {
990             pTempMessage++;
991             if (iswdigit(*pTempMessage))
992             {
993                 pParamData[i].pStartingAddress = pTempMessage-1;
994                 pParamData[i].pParameterID = (DWORD)_wtol(pTempMessage);
995 
996                 while (iswdigit(*++pTempMessage)) ;
997 
998                 pParamData[i].pEndingAddress = pTempMessage;
999                 i++;
1000             }
1001         }
1002     }
1003     else
1004     {
1005         while ((pTempMessage = wcsstr(pTempMessage, L"%%")) && (i < dwParamCount))
1006         {
1007             pTempMessage += 2;
1008             if (iswdigit(*pTempMessage))
1009             {
1010                 pParamData[i].pStartingAddress = pTempMessage-2;
1011                 pParamData[i].pParameterID = (DWORD)_wtol(pTempMessage);
1012 
1013                 while (iswdigit(*++pTempMessage)) ;
1014 
1015                 pParamData[i].pEndingAddress = pTempMessage;
1016                 i++;
1017             }
1018         }
1019     }
1020 
1021     /* Retrieve each parameter string */
1022     for (i = 0; i < dwParamCount; i++)
1023     {
1024         // pParamData[i].pParameter = GetMessageString(pParamData[i].pParameterID, 0, NULL);
1025         pParamData[i].pParameter =
1026             GetMessageStringFromDllList(lpMessageDllList,
1027                                         FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
1028                                         FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
1029                                         pParamData[i].pParameterID,
1030                                         0, NULL);
1031         if (!pParamData[i].pParameter)
1032         {
1033             /* Skip the insertion string */
1034             continue;
1035         }
1036 
1037         cchParams += wcslen(pParamData[i].pParameter);
1038     }
1039 
1040     /*
1041      * Allocate the final message buffer, the size of which is based on the
1042      * length of the original message and the length of each parameter string.
1043      */
1044     cchBuffer = wcslen(pMessage) + cchParams + 1;
1045     *pFinalMessage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cchBuffer * sizeof(WCHAR));
1046     if (!*pFinalMessage)
1047     {
1048         Status = ERROR_OUTOFMEMORY;
1049         goto Cleanup;
1050     }
1051 
1052     pTempFinalMessage = *pFinalMessage;
1053 
1054     /* Build the final message string */
1055     pTempMessage = (LPWSTR)pMessage;
1056     for (i = 0; i < dwParamCount; i++)
1057     {
1058         /* Append the segment from pMessage */
1059         cch = pParamData[i].pStartingAddress - pTempMessage;
1060         StringCchCopyNW(pTempFinalMessage, cchBuffer, pTempMessage, cch);
1061         pTempMessage = pParamData[i].pEndingAddress;
1062         cchBuffer -= cch;
1063         pTempFinalMessage += cch;
1064 
1065         /* Append the parameter string */
1066         if (pParamData[i].pParameter)
1067         {
1068             StringCchCopyW(pTempFinalMessage, cchBuffer, pParamData[i].pParameter);
1069             cch = wcslen(pParamData[i].pParameter); // pTempFinalMessage
1070         }
1071         else
1072         {
1073             /*
1074              * We failed to retrieve the parameter string before, so just
1075              * place back the original string placeholder.
1076              */
1077             cch = pParamData[i].pEndingAddress /* == pTempMessage */ - pParamData[i].pStartingAddress;
1078             StringCchCopyNW(pTempFinalMessage, cchBuffer, pParamData[i].pStartingAddress, cch);
1079             // cch = wcslen(pTempFinalMessage);
1080         }
1081         cchBuffer -= cch;
1082         pTempFinalMessage += cch;
1083     }
1084 
1085     /* Append the last segment from pMessage */
1086     StringCchCopyW(pTempFinalMessage, cchBuffer, pTempMessage);
1087 
1088 Cleanup:
1089 
1090     // if (Status != ERROR_SUCCESS)
1091         // *pFinalMessage = NULL;
1092 
1093     if (pParamData)
1094     {
1095         for (i = 0; i < dwParamCount; i++)
1096         {
1097             if (pParamData[i].pParameter)
1098                 LocalFree(pParamData[i].pParameter);
1099         }
1100 
1101         HeapFree(GetProcessHeap(), 0, pParamData);
1102     }
1103 
1104     return Status;
1105 }
1106 
1107 
1108 /*
1109  * The following functions were adapted from
1110  * shell32!dialogs/filedefext.cpp:``SH_...'' functions.
1111  */
1112 
1113 UINT
1114 FormatInteger(LONGLONG Num, LPWSTR pwszResult, UINT cchResultMax)
1115 {
1116     WCHAR wszNumber[24];
1117     WCHAR wszDecimalSep[8], wszThousandSep[8];
1118     NUMBERFMTW nf;
1119     WCHAR wszGrouping[12];
1120     INT cchGrouping;
1121     INT cchResult;
1122     INT i;
1123 
1124     // Print the number in uniform mode
1125     swprintf(wszNumber, L"%I64u", Num);
1126 
1127     // Get system strings for decimal and thousand separators.
1128     GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, wszDecimalSep, _countof(wszDecimalSep));
1129     GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, wszThousandSep, _countof(wszThousandSep));
1130 
1131     // Initialize format for printing the number in bytes
1132     ZeroMemory(&nf, sizeof(nf));
1133     nf.lpDecimalSep = wszDecimalSep;
1134     nf.lpThousandSep = wszThousandSep;
1135 
1136     // Get system string for groups separator
1137     cchGrouping = GetLocaleInfoW(LOCALE_USER_DEFAULT,
1138                                  LOCALE_SGROUPING,
1139                                  wszGrouping,
1140                                  _countof(wszGrouping));
1141 
1142     // Convert grouping specs from string to integer
1143     for (i = 0; i < cchGrouping; i++)
1144     {
1145         WCHAR wch = wszGrouping[i];
1146 
1147         if (wch >= L'0' && wch <= L'9')
1148             nf.Grouping = nf.Grouping * 10 + (wch - L'0');
1149         else if (wch != L';')
1150             break;
1151     }
1152 
1153     if ((nf.Grouping % 10) == 0)
1154         nf.Grouping /= 10;
1155     else
1156         nf.Grouping *= 10;
1157 
1158     // Format the number
1159     cchResult = GetNumberFormatW(LOCALE_USER_DEFAULT,
1160                                  0,
1161                                  wszNumber,
1162                                  &nf,
1163                                  pwszResult,
1164                                  cchResultMax);
1165 
1166     if (!cchResult)
1167         return 0;
1168 
1169     // GetNumberFormatW returns number of characters including UNICODE_NULL
1170     return cchResult - 1;
1171 }
1172 
1173 UINT
1174 FormatByteSize(LONGLONG cbSize, LPWSTR pwszResult, UINT cchResultMax)
1175 {
1176     INT cchWritten;
1177     LPWSTR pwszEnd;
1178     size_t cchRemaining;
1179 
1180     /* Write formated bytes count */
1181     cchWritten = FormatInteger(cbSize, pwszResult, cchResultMax);
1182     if (!cchWritten)
1183         return 0;
1184 
1185     /* Copy " bytes" to buffer */
1186     pwszEnd = pwszResult + cchWritten;
1187     cchRemaining = cchResultMax - cchWritten;
1188     StringCchCopyExW(pwszEnd, cchRemaining, L" ", &pwszEnd, &cchRemaining, 0);
1189     cchWritten = LoadStringW(hInst, IDS_BYTES_FORMAT, pwszEnd, cchRemaining);
1190     cchRemaining -= cchWritten;
1191 
1192     return cchResultMax - cchRemaining;
1193 }
1194 
1195 LPWSTR
1196 FormatFileSizeWithBytes(const PULARGE_INTEGER lpQwSize, LPWSTR pwszResult, UINT cchResultMax)
1197 {
1198     UINT cchWritten;
1199     LPWSTR pwszEnd;
1200     size_t cchRemaining;
1201 
1202     /* Format bytes in KBs, MBs etc */
1203     if (StrFormatByteSizeW(lpQwSize->QuadPart, pwszResult, cchResultMax) == NULL)
1204         return NULL;
1205 
1206     /* If there is less bytes than 1KB, we have nothing to do */
1207     if (lpQwSize->QuadPart < 1024)
1208         return pwszResult;
1209 
1210     /* Concatenate " (" */
1211     cchWritten = wcslen(pwszResult);
1212     pwszEnd = pwszResult + cchWritten;
1213     cchRemaining = cchResultMax - cchWritten;
1214     StringCchCopyExW(pwszEnd, cchRemaining, L" (", &pwszEnd, &cchRemaining, 0);
1215 
1216     /* Write formated bytes count */
1217     cchWritten = FormatByteSize(lpQwSize->QuadPart, pwszEnd, cchRemaining);
1218     pwszEnd += cchWritten;
1219     cchRemaining -= cchWritten;
1220 
1221     /* Copy ")" to the buffer */
1222     StringCchCopyW(pwszEnd, cchRemaining, L")");
1223 
1224     return pwszResult;
1225 }
1226 
1227 /* Adapted from shell32!dialogs/filedefext.cpp:``CFileDefExt::GetFileTimeString'' */
1228 BOOL
1229 GetFileTimeString(LPFILETIME lpFileTime, LPWSTR pwszResult, UINT cchResult)
1230 {
1231     FILETIME ft;
1232     SYSTEMTIME st;
1233     int cchWritten;
1234     size_t cchRemaining = cchResult;
1235     LPWSTR pwszEnd = pwszResult;
1236 
1237     if (!FileTimeToLocalFileTime(lpFileTime, &ft) || !FileTimeToSystemTime(&ft, &st))
1238         return FALSE;
1239 
1240     cchWritten = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, pwszEnd, cchRemaining);
1241     if (cchWritten)
1242         --cchWritten; // GetDateFormatW returns count with terminating zero
1243     // else
1244         // ERR("GetDateFormatW failed\n");
1245 
1246     cchRemaining -= cchWritten;
1247     pwszEnd += cchWritten;
1248 
1249     StringCchCopyExW(pwszEnd, cchRemaining, L", ", &pwszEnd, &cchRemaining, 0);
1250 
1251     cchWritten = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, pwszEnd, cchRemaining);
1252     if (cchWritten)
1253         --cchWritten; // GetTimeFormatW returns count with terminating zero
1254     // else
1255         // ERR("GetTimeFormatW failed\n");
1256 
1257     return TRUE;
1258 }
1259 
1260 
1261 HTREEITEM
1262 TreeViewAddItem(IN HWND hTreeView,
1263                 IN HTREEITEM hParent,
1264                 IN LPWSTR lpText,
1265                 IN INT Image,
1266                 IN INT SelectedImage,
1267                 IN LPARAM lParam)
1268 {
1269     TV_INSERTSTRUCTW Insert;
1270 
1271     ZeroMemory(&Insert, sizeof(Insert));
1272 
1273     Insert.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
1274     Insert.hInsertAfter = TVI_LAST;
1275     Insert.hParent = hParent;
1276     Insert.item.pszText = lpText;
1277     Insert.item.iImage = Image;
1278     Insert.item.iSelectedImage = SelectedImage;
1279     Insert.item.lParam = lParam;
1280 
1281     Insert.item.mask |= TVIF_STATE;
1282     Insert.item.stateMask = TVIS_OVERLAYMASK;
1283     Insert.item.state = INDEXTOOVERLAYMASK(1);
1284 
1285     return TreeView_InsertItem(hTreeView, &Insert);
1286 }
1287 
1288 
1289 /* LOG HELPER FUNCTIONS *******************************************************/
1290 
1291 PEVENTLOG
1292 AllocEventLog(IN PCWSTR ComputerName OPTIONAL,
1293               IN PCWSTR LogName,
1294               IN BOOL Permanent)
1295 {
1296     PEVENTLOG EventLog;
1297     UINT cchName;
1298 
1299     /* Allocate a new event log entry */
1300     EventLog = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*EventLog));
1301     if (!EventLog)
1302         return NULL;
1303 
1304     /* Allocate the computer name string (optional) and copy it */
1305     if (ComputerName)
1306     {
1307         cchName = wcslen(ComputerName) + 1;
1308         EventLog->ComputerName = HeapAlloc(GetProcessHeap(), 0, cchName * sizeof(WCHAR));
1309         if (EventLog->ComputerName)
1310             StringCchCopyW(EventLog->ComputerName, cchName, ComputerName);
1311     }
1312 
1313     /* Allocate the event log name string and copy it */
1314     cchName = wcslen(LogName) + 1;
1315     EventLog->LogName = HeapAlloc(GetProcessHeap(), 0, cchName * sizeof(WCHAR));
1316     if (!EventLog->LogName)
1317     {
1318         if (EventLog->ComputerName)
1319             HeapFree(GetProcessHeap(), 0, EventLog->ComputerName);
1320         HeapFree(GetProcessHeap(), 0, EventLog);
1321         return NULL;
1322     }
1323     StringCchCopyW(EventLog->LogName, cchName, LogName);
1324 
1325     EventLog->Permanent = Permanent;
1326 
1327     return EventLog;
1328 }
1329 
1330 VOID
1331 EventLog_Free(IN PEVENTLOG EventLog)
1332 {
1333     if (EventLog->LogName)
1334         HeapFree(GetProcessHeap(), 0, EventLog->LogName);
1335 
1336     if (EventLog->ComputerName)
1337         HeapFree(GetProcessHeap(), 0, EventLog->ComputerName);
1338 
1339     if (EventLog->FileName)
1340         HeapFree(GetProcessHeap(), 0, EventLog->FileName);
1341 
1342     HeapFree(GetProcessHeap(), 0, EventLog);
1343 }
1344 
1345 
1346 PWSTR
1347 AllocAndCopyMultiStr(IN PCWSTR MultiStr OPTIONAL)
1348 {
1349     PWSTR pStr;
1350     ULONG Length;
1351 
1352     if (!MultiStr)
1353         return NULL;
1354 
1355     pStr = (PWSTR)MultiStr;
1356     while (*pStr) pStr += (wcslen(pStr) + 1);
1357     Length = MultiStr - pStr + 2;
1358 
1359     pStr = HeapAlloc(GetProcessHeap(), 0, Length * sizeof(WCHAR));
1360     // NOTE: If we failed allocating the string, then fall back into no filter!
1361     if (pStr)
1362         RtlCopyMemory(pStr, MultiStr, Length * sizeof(WCHAR));
1363 
1364     return pStr;
1365 }
1366 
1367 PEVENTLOGFILTER
1368 AllocEventLogFilter(// IN PCWSTR FilterName,
1369                     IN BOOL Information,
1370                     IN BOOL Warning,
1371                     IN BOOL Error,
1372                     IN BOOL AuditSuccess,
1373                     IN BOOL AuditFailure,
1374                     IN PCWSTR Sources OPTIONAL,
1375                     IN PCWSTR Users OPTIONAL,
1376                     IN PCWSTR ComputerNames OPTIONAL,
1377                     IN ULONG NumOfEventLogs,
1378                     IN PEVENTLOG* EventLogs)
1379 {
1380     PEVENTLOGFILTER EventLogFilter;
1381 
1382     /* Allocate a new event log filter entry, big enough to accommodate the list of logs */
1383     EventLogFilter = HeapAlloc(GetProcessHeap(),
1384                                HEAP_ZERO_MEMORY,
1385                                FIELD_OFFSET(EVENTLOGFILTER, EventLogs[NumOfEventLogs]));
1386     if (!EventLogFilter)
1387         return NULL;
1388 
1389     EventLogFilter->Information  = Information;
1390     EventLogFilter->Warning      = Warning;
1391     EventLogFilter->Error        = Error;
1392     EventLogFilter->AuditSuccess = AuditSuccess;
1393     EventLogFilter->AuditFailure = AuditFailure;
1394 
1395     /* Allocate and copy the sources, users, and computers multi-strings */
1396     EventLogFilter->Sources = AllocAndCopyMultiStr(Sources);
1397     EventLogFilter->Users   = AllocAndCopyMultiStr(Users);
1398     EventLogFilter->ComputerNames = AllocAndCopyMultiStr(ComputerNames);
1399 
1400     /* Copy the list of event logs */
1401     EventLogFilter->NumOfEventLogs = NumOfEventLogs;
1402     RtlCopyMemory(EventLogFilter->EventLogs, EventLogs, NumOfEventLogs * sizeof(PEVENTLOG));
1403 
1404     /* Initialize the filter reference count */
1405     EventLogFilter->ReferenceCount = 1;
1406 
1407     return EventLogFilter;
1408 }
1409 
1410 VOID
1411 EventLogFilter_Free(IN PEVENTLOGFILTER EventLogFilter)
1412 {
1413     if (EventLogFilter->Sources)
1414         HeapFree(GetProcessHeap(), 0, EventLogFilter->Sources);
1415 
1416     if (EventLogFilter->Users)
1417         HeapFree(GetProcessHeap(), 0, EventLogFilter->Users);
1418 
1419     if (EventLogFilter->ComputerNames)
1420         HeapFree(GetProcessHeap(), 0, EventLogFilter->ComputerNames);
1421 
1422     HeapFree(GetProcessHeap(), 0, EventLogFilter);
1423 }
1424 
1425 LONG EventLogFilter_AddRef(IN PEVENTLOGFILTER EventLogFilter)
1426 {
1427     ASSERT(EventLogFilter);
1428     return InterlockedIncrement(&EventLogFilter->ReferenceCount);
1429 }
1430 
1431 LONG EventLogFilter_Release(IN PEVENTLOGFILTER EventLogFilter)
1432 {
1433     LONG RefCount;
1434 
1435     ASSERT(EventLogFilter);
1436 
1437     /* When the reference count reaches zero, delete the filter */
1438     RefCount = InterlockedDecrement(&EventLogFilter->ReferenceCount);
1439     if (RefCount <= 0)
1440     {
1441         /* Remove the filter from the list */
1442         /** RemoveEntryList(&EventLogFilter->ListEntry); **/
1443         EventLogFilter_Free(EventLogFilter);
1444     }
1445 
1446     return RefCount;
1447 }
1448 
1449 void
1450 TrimNulls(LPWSTR s)
1451 {
1452     WCHAR *c;
1453 
1454     if (s != NULL)
1455     {
1456         c = s + wcslen(s) - 1;
1457         while (c >= s && iswspace(*c))
1458             --c;
1459         *++c = L'\0';
1460     }
1461 }
1462 
1463 DWORD
1464 GetExpandedFilePathName(
1465     IN LPCWSTR ComputerName OPTIONAL,
1466     IN LPCWSTR lpFileName,
1467     OUT LPWSTR lpFullFileName OPTIONAL,
1468     IN DWORD nSize)
1469 {
1470     DWORD dwLength;
1471 
1472     /* Determine the needed size after expansion of any environment strings */
1473     dwLength = ExpandEnvironmentStringsW(lpFileName, NULL, 0);
1474     if (dwLength == 0)
1475     {
1476         /* We failed, bail out */
1477         return 0;
1478     }
1479 
1480     /* If the file path is on a remote computer, estimate its length */
1481     // FIXME: Use WNetGetUniversalName instead?
1482     if (ComputerName && *ComputerName)
1483     {
1484         /* Skip any leading backslashes */
1485         while (*ComputerName == L'\\')
1486             ++ComputerName;
1487 
1488         if (*ComputerName)
1489         {
1490             /* Count 2 backslashes plus the computer name and one backslash separator */
1491             dwLength += 2 + wcslen(ComputerName) + 1;
1492         }
1493     }
1494 
1495     /* Check whether we have enough space */
1496     if (dwLength > nSize)
1497     {
1498         /* No, return the needed size in characters (includes NULL-terminator) */
1499         return dwLength;
1500     }
1501 
1502 
1503     /* Now expand the file path */
1504     ASSERT(dwLength <= nSize);
1505 
1506     /* Expand any existing environment strings */
1507     if (ExpandEnvironmentStringsW(lpFileName, lpFullFileName, dwLength) == 0)
1508     {
1509         /* We failed, bail out */
1510         return 0;
1511     }
1512 
1513     /* If the file path is on a remote computer, retrieve the network share form of the file name */
1514     // FIXME: Use WNetGetUniversalName instead?
1515     if (ComputerName && *ComputerName)
1516     {
1517         /* Note that we previously skipped any potential leading backslashes */
1518 
1519         /* Replace ':' by '$' in the drive letter */
1520         if (*lpFullFileName && lpFullFileName[1] == L':')
1521             lpFullFileName[1] = L'$';
1522 
1523         /* Prepend the computer name */
1524         RtlMoveMemory(lpFullFileName + 2 + wcslen(ComputerName) + 1,
1525                       lpFullFileName, dwLength * sizeof(WCHAR) - (2 + wcslen(ComputerName) + 1) * sizeof(WCHAR));
1526         lpFullFileName[0] = L'\\';
1527         lpFullFileName[1] = L'\\';
1528         wcsncpy(lpFullFileName + 2, ComputerName, wcslen(ComputerName));
1529         lpFullFileName[2 + wcslen(ComputerName)] = L'\\';
1530     }
1531 
1532     /* Return the number of stored characters (includes NULL-terminator) */
1533     return dwLength;
1534 }
1535 
1536 BOOL
1537 GetEventMessageFileDLL(IN LPCWSTR lpLogName,
1538                        IN LPCWSTR SourceName,
1539                        IN LPCWSTR EntryName,
1540                        OUT PWCHAR lpModuleName) // TODO: Add IN DWORD BufLen
1541 {
1542     BOOL Success = FALSE;
1543     LONG Result;
1544     DWORD dwType, dwSize;
1545     WCHAR szModuleName[MAX_PATH];
1546     WCHAR szKeyName[MAX_PATH];
1547     HKEY hLogKey = NULL;
1548     HKEY hSourceKey = NULL;
1549 
1550     StringCbCopyW(szKeyName, sizeof(szKeyName), EVENTLOG_BASE_KEY);
1551     StringCbCatW(szKeyName, sizeof(szKeyName), lpLogName);
1552 
1553     Result = RegOpenKeyExW(hkMachine,
1554                            szKeyName,
1555                            0,
1556                            KEY_READ,
1557                            &hLogKey);
1558     if (Result != ERROR_SUCCESS)
1559         return FALSE;
1560 
1561     Result = RegOpenKeyExW(hLogKey,
1562                            SourceName,
1563                            0,
1564                            KEY_QUERY_VALUE,
1565                            &hSourceKey);
1566     if (Result != ERROR_SUCCESS)
1567     {
1568         RegCloseKey(hLogKey);
1569         return FALSE;
1570     }
1571 
1572     dwSize = sizeof(szModuleName);
1573     Result = RegQueryValueExW(hSourceKey,
1574                               EntryName,
1575                               NULL,
1576                               &dwType,
1577                               (LPBYTE)szModuleName,
1578                               &dwSize);
1579     if ((Result != ERROR_SUCCESS) || (dwType != REG_EXPAND_SZ && dwType != REG_SZ))
1580     {
1581         szModuleName[0] = UNICODE_NULL;
1582     }
1583     else
1584     {
1585         /* NULL-terminate the string and expand it */
1586         szModuleName[dwSize / sizeof(WCHAR) - 1] = UNICODE_NULL;
1587         GetExpandedFilePathName(lpComputerName, szModuleName, lpModuleName, ARRAYSIZE(szModuleName));
1588         Success = TRUE;
1589     }
1590 
1591     RegCloseKey(hSourceKey);
1592     RegCloseKey(hLogKey);
1593 
1594     return Success;
1595 }
1596 
1597 BOOL
1598 GetEventCategory(IN LPCWSTR KeyName,
1599                  IN LPCWSTR SourceName,
1600                  IN PEVENTLOGRECORD pevlr,
1601                  OUT PWCHAR CategoryName) // TODO: Add IN DWORD BufLen
1602 {
1603     BOOL Success = FALSE;
1604     WCHAR szMessageDLL[MAX_PATH];
1605     LPWSTR lpMsgBuf = NULL;
1606 
1607     if (!GetEventMessageFileDLL(KeyName, SourceName, EVENT_CATEGORY_MESSAGE_FILE, szMessageDLL))
1608         goto Quit;
1609 
1610     /* Retrieve the message string without appending extra newlines */
1611     lpMsgBuf =
1612     GetMessageStringFromDllList(szMessageDLL,
1613                                 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
1614                                 FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
1615                                 pevlr->EventCategory,
1616                                 EVENT_MESSAGE_FILE_BUFFER,
1617                                 NULL);
1618     if (lpMsgBuf)
1619     {
1620         /* Trim the string */
1621         TrimNulls(lpMsgBuf);
1622 
1623         /* Copy the category name */
1624         StringCchCopyW(CategoryName, MAX_PATH, lpMsgBuf);
1625 
1626         /* Free the buffer allocated by FormatMessage */
1627         LocalFree(lpMsgBuf);
1628 
1629         /* The ID was found and the message was formatted */
1630         Success = TRUE;
1631     }
1632 
1633 Quit:
1634     if (!Success)
1635     {
1636         if (pevlr->EventCategory != 0)
1637         {
1638             StringCchPrintfW(CategoryName, MAX_PATH, L"(%lu)", pevlr->EventCategory);
1639             Success = TRUE;
1640         }
1641     }
1642 
1643     return Success;
1644 }
1645 
1646 
1647 BOOL                                                                        // NOTE: Used by evtdetctl.c
1648 GetEventMessage(IN LPCWSTR KeyName,
1649                 IN LPCWSTR SourceName,
1650                 IN PEVENTLOGRECORD pevlr,
1651                 OUT PWCHAR EventText) // TODO: Add IN DWORD BufLen
1652 {
1653     BOOL Success = FALSE;
1654     DWORD i;
1655     size_t cch;
1656     WCHAR SourceModuleName[1024];
1657     WCHAR ParameterModuleName[1024];
1658     BOOL IsParamModNameCached = FALSE;
1659     LPWSTR lpMsgBuf = NULL;
1660     LPWSTR szStringArray, szMessage;
1661     LPWSTR *szArguments;
1662 
1663     /* Get the event string array */
1664     szStringArray = (LPWSTR)((LPBYTE)pevlr + pevlr->StringOffset);
1665 
1666     /* NOTE: GetEventMessageFileDLL can return a comma-separated list of DLLs */
1667     if (!GetEventMessageFileDLL(KeyName, SourceName, EVENT_MESSAGE_FILE, SourceModuleName))
1668         goto Quit;
1669 
1670     /* Allocate space for insertion strings */
1671     szArguments = HeapAlloc(GetProcessHeap(), 0, pevlr->NumStrings * sizeof(LPVOID));
1672     if (!szArguments)
1673         goto Quit;
1674 
1675     if (!IsParamModNameCached)
1676     {
1677         /* Now that the parameter file list is loaded, no need to reload it at the next run! */
1678         IsParamModNameCached = GetEventMessageFileDLL(KeyName, SourceName, EVENT_PARAMETER_MESSAGE_FILE, ParameterModuleName);
1679         // FIXME: If the string loading failed the first time, no need to retry it just after???
1680     }
1681 
1682     if (IsParamModNameCached)
1683     {
1684         /* Not yet support for reading messages from parameter message DLL */
1685     }
1686 
1687     szMessage = szStringArray;
1688     /*
1689      * HACK:
1690      * We do some hackish preformatting of the cached event strings...
1691      * That's because after we pass the string to FormatMessage
1692      * (via GetMessageStringFromDllList) with the FORMAT_MESSAGE_ARGUMENT_ARRAY
1693      * flag, instead of ignoring the insertion parameters and do the formatting
1694      * by ourselves. Therefore, the resulting string should have the parameter
1695      * string placeholders starting with a single '%' instead of a mix of one
1696      * and two '%'.
1697      */
1698     /* HACK part 1: Compute the full length of the string array */
1699     cch = 0;
1700     for (i = 0; i < pevlr->NumStrings; i++)
1701     {
1702         szMessage += wcslen(szMessage) + 1;
1703     }
1704     cch = szMessage - szStringArray;
1705 
1706     /* HACK part 2: Now do the HACK proper! */
1707     szMessage = szStringArray;
1708     for (i = 0; i < pevlr->NumStrings; i++)
1709     {
1710         lpMsgBuf = szMessage;
1711         while ((lpMsgBuf = wcsstr(lpMsgBuf, L"%%")))
1712         {
1713             if (iswdigit(lpMsgBuf[2]))
1714             {
1715                 RtlMoveMemory(lpMsgBuf, lpMsgBuf+1, ((szStringArray + cch) - lpMsgBuf - 1) * sizeof(WCHAR));
1716             }
1717         }
1718 
1719         szArguments[i] = szMessage;
1720         szMessage += wcslen(szMessage) + 1;
1721     }
1722 
1723     /* Retrieve the message string without appending extra newlines */
1724     lpMsgBuf =
1725     GetMessageStringFromDllList(SourceModuleName,
1726                                 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
1727                                 FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_MAX_WIDTH_MASK,
1728                                 pevlr->EventID,
1729                                 0,
1730                                 (va_list*)szArguments);
1731     if (lpMsgBuf)
1732     {
1733         /* Trim the string */
1734         TrimNulls(lpMsgBuf);
1735 
1736         szMessage = NULL;
1737         Success = (ApplyParameterStringsToMessage(ParameterModuleName,
1738                                                   TRUE,
1739                                                   lpMsgBuf,
1740                                                   &szMessage) == ERROR_SUCCESS);
1741         if (Success && szMessage)
1742         {
1743             /* Free the buffer allocated by FormatMessage */
1744             LocalFree(lpMsgBuf);
1745             lpMsgBuf = szMessage;
1746         }
1747 
1748         /* Copy the event text */
1749         StringCchCopyW(EventText, EVENT_MESSAGE_EVENTTEXT_BUFFER, lpMsgBuf);
1750 
1751         /* Free the buffer allocated by FormatMessage */
1752         LocalFree(lpMsgBuf);
1753     }
1754 
1755     HeapFree(GetProcessHeap(), 0, szArguments);
1756 
1757 Quit:
1758     if (!Success)
1759     {
1760         /* Get a read-only pointer to the "event-not-found" string */
1761         lpMsgBuf = HeapAlloc(GetProcessHeap(), 0, EVENT_MESSAGE_EVENTTEXT_BUFFER * sizeof(WCHAR));
1762         LoadStringW(hInst, IDS_EVENTSTRINGIDNOTFOUND, lpMsgBuf, EVENT_MESSAGE_EVENTTEXT_BUFFER);
1763         StringCchPrintfW(EventText, EVENT_MESSAGE_EVENTTEXT_BUFFER, lpMsgBuf, (pevlr->EventID & 0xFFFF), SourceName);
1764 
1765         /* Append the strings */
1766         szMessage = szStringArray;
1767         for (i = 0; i < pevlr->NumStrings; i++)
1768         {
1769             StringCchCatW(EventText, EVENT_MESSAGE_EVENTTEXT_BUFFER, szMessage);
1770             StringCchCatW(EventText, EVENT_MESSAGE_EVENTTEXT_BUFFER, L"\n");
1771             szMessage += wcslen(szMessage) + 1;
1772         }
1773     }
1774 
1775     return Success;
1776 }
1777 
1778 VOID
1779 GetEventType(IN WORD dwEventType,
1780              OUT PWCHAR eventTypeText) // TODO: Add IN DWORD BufLen
1781 {
1782     switch (dwEventType)
1783     {
1784         case EVENTLOG_ERROR_TYPE:
1785             LoadStringW(hInst, IDS_EVENTLOG_ERROR_TYPE, eventTypeText, MAX_LOADSTRING);
1786             break;
1787         case EVENTLOG_WARNING_TYPE:
1788             LoadStringW(hInst, IDS_EVENTLOG_WARNING_TYPE, eventTypeText, MAX_LOADSTRING);
1789             break;
1790         case EVENTLOG_INFORMATION_TYPE:
1791             LoadStringW(hInst, IDS_EVENTLOG_INFORMATION_TYPE, eventTypeText, MAX_LOADSTRING);
1792             break;
1793         case EVENTLOG_SUCCESS:
1794             LoadStringW(hInst, IDS_EVENTLOG_SUCCESS, eventTypeText, MAX_LOADSTRING);
1795             break;
1796         case EVENTLOG_AUDIT_SUCCESS:
1797             LoadStringW(hInst, IDS_EVENTLOG_AUDIT_SUCCESS, eventTypeText, MAX_LOADSTRING);
1798             break;
1799         case EVENTLOG_AUDIT_FAILURE:
1800             LoadStringW(hInst, IDS_EVENTLOG_AUDIT_FAILURE, eventTypeText, MAX_LOADSTRING);
1801             break;
1802         default:
1803             LoadStringW(hInst, IDS_EVENTLOG_UNKNOWN_TYPE, eventTypeText, MAX_LOADSTRING);
1804             break;
1805     }
1806 }
1807 
1808 BOOL
1809 GetEventUserName(IN PEVENTLOGRECORD pelr,
1810                  IN OUT PSID *pLastSid,
1811                  OUT PWCHAR pszUser) // TODO: Add IN DWORD BufLen
1812 {
1813     PSID pCurrentSid;
1814     PWSTR StringSid;
1815     WCHAR szName[1024];
1816     WCHAR szDomain[1024];
1817     SID_NAME_USE peUse;
1818     DWORD cchName = ARRAYSIZE(szName);
1819     DWORD cchDomain = ARRAYSIZE(szDomain);
1820     BOOL Success = FALSE;
1821 
1822     /* Point to the SID */
1823     pCurrentSid = (PSID)((LPBYTE)pelr + pelr->UserSidOffset);
1824 
1825     if (!IsValidSid(pCurrentSid))
1826     {
1827         *pLastSid = NULL;
1828         return FALSE;
1829     }
1830     else if (*pLastSid && EqualSid(*pLastSid, pCurrentSid))
1831     {
1832         return TRUE;
1833     }
1834 
1835     /* User SID */
1836     if (pelr->UserSidLength > 0)
1837     {
1838         /*
1839          * Try to retrieve the user account name and domain name corresponding
1840          * to the SID. If it cannot be retrieved, try to convert the SID to a
1841          * string-form. It should not be bigger than the user-provided buffer
1842          * 'pszUser', otherwise we return an error.
1843          */
1844         if (LookupAccountSidW(lpComputerName,
1845                               pCurrentSid,
1846                               szName,
1847                               &cchName,
1848                               szDomain,
1849                               &cchDomain,
1850                               &peUse))
1851         {
1852             StringCchCopyW(pszUser, MAX_PATH, szName);
1853             Success = TRUE;
1854         }
1855         else if (ConvertSidToStringSidW(pCurrentSid, &StringSid))
1856         {
1857             /* Copy the string only if the user-provided buffer is big enough */
1858             if (wcslen(StringSid) + 1 <= MAX_PATH) // + 1 for NULL-terminator
1859             {
1860                 StringCchCopyW(pszUser, MAX_PATH, StringSid);
1861                 Success = TRUE;
1862             }
1863             else
1864             {
1865                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1866                 Success = FALSE;
1867             }
1868 
1869             /* Free the allocated buffer */
1870             LocalFree(StringSid);
1871         }
1872     }
1873 
1874     *pLastSid = Success ? pCurrentSid : NULL;
1875 
1876     return Success;
1877 }
1878 
1879 
1880 static VOID FreeRecords(VOID)
1881 {
1882     DWORD iIndex;
1883 
1884     if (!g_RecordPtrs)
1885         return;
1886 
1887     for (iIndex = 0; iIndex < g_TotalRecords; iIndex++)
1888     {
1889         if (g_RecordPtrs[iIndex])
1890             HeapFree(GetProcessHeap(), 0, g_RecordPtrs[iIndex]);
1891     }
1892     HeapFree(GetProcessHeap(), 0, g_RecordPtrs);
1893     g_RecordPtrs = NULL;
1894     g_TotalRecords = 0;
1895 }
1896 
1897 BOOL
1898 FilterByType(IN PEVENTLOGFILTER EventLogFilter,
1899              IN PEVENTLOGRECORD pevlr)
1900 {
1901     if ((pevlr->EventType == EVENTLOG_SUCCESS          && !EventLogFilter->Information ) ||
1902         (pevlr->EventType == EVENTLOG_INFORMATION_TYPE && !EventLogFilter->Information ) ||
1903         (pevlr->EventType == EVENTLOG_WARNING_TYPE     && !EventLogFilter->Warning     ) ||
1904         (pevlr->EventType == EVENTLOG_ERROR_TYPE       && !EventLogFilter->Error       ) ||
1905         (pevlr->EventType == EVENTLOG_AUDIT_SUCCESS    && !EventLogFilter->AuditSuccess) ||
1906         (pevlr->EventType == EVENTLOG_AUDIT_FAILURE    && !EventLogFilter->AuditFailure))
1907     {
1908         return FALSE;
1909     }
1910     return TRUE;
1911 }
1912 
1913 BOOL
1914 FilterByString(IN PCWSTR FilterString, // This is a multi-string
1915                IN PWSTR  String)
1916 {
1917     PCWSTR pStr;
1918 
1919     /* The filter string is NULL so it does not filter anything */
1920     if (!FilterString)
1921         return TRUE;
1922 
1923     /*
1924      * If the filter string filters for an empty string AND the source string
1925      * is an empty string, we have a match (particular case of the last one).
1926      */
1927     if (!*FilterString && !*String)
1928         return TRUE;
1929 
1930     // if (*FilterString || *String)
1931 
1932     /*
1933      * If the filter string is empty BUT the source string is not empty,
1934      * OR vice-versa, we cannot have a match.
1935      */
1936     if ( (!*FilterString && *String) || (*FilterString && !*String) )
1937         return FALSE;
1938 
1939     /*
1940      * If the filter string filters for at least a non-empty string,
1941      * browse it and search for a string that matches the source string.
1942      */
1943     // else if (*FilterString && *String)
1944     {
1945         pStr = FilterString;
1946         while (*pStr)
1947         {
1948             if (wcsicmp(pStr, String) == 0)
1949             {
1950                 /* We have a match, break the loop */
1951                 break;
1952             }
1953 
1954             pStr += (wcslen(pStr) + 1);
1955         }
1956         if (!*pStr) // && *String
1957         {
1958             /* We do not have a match */
1959             return FALSE;
1960         }
1961     }
1962 
1963     /* We have a match */
1964     return TRUE;
1965 }
1966 
1967 /*
1968  * The events enumerator thread.
1969  */
1970 static DWORD WINAPI
1971 EnumEventsThread(IN LPVOID lpParameter)
1972 {
1973     PEVENTLOGFILTER EventLogFilter = (PEVENTLOGFILTER)lpParameter;
1974     PEVENTLOG EventLog;
1975 
1976     ULONG LogIndex;
1977     HANDLE hEventLog;
1978     PEVENTLOGRECORD pEvlr = NULL;
1979     PBYTE pEvlrEnd;
1980     PBYTE pEvlrBuffer;
1981     DWORD dwWanted, dwRead, dwNeeded, dwStatus = ERROR_SUCCESS;
1982     DWORD dwTotalRecords = 0, dwCurrentRecord = 0;
1983     DWORD dwFlags, dwMaxLength;
1984     size_t cchRemaining;
1985     LPWSTR lpszSourceName;
1986     LPWSTR lpszComputerName;
1987     BOOL bResult = TRUE; /* Read succeeded */
1988     HANDLE hProcessHeap = GetProcessHeap();
1989     PSID pLastSid = NULL;
1990 
1991     UINT uStep = 0, uStepAt = 0, uPos = 0;
1992 
1993     WCHAR szWindowTitle[MAX_PATH];
1994     WCHAR szStatusText[MAX_PATH];
1995     WCHAR szLocalDate[MAX_PATH];
1996     WCHAR szLocalTime[MAX_PATH];
1997     WCHAR szEventID[MAX_PATH];
1998     WCHAR szEventTypeText[MAX_LOADSTRING];
1999     WCHAR szCategoryID[MAX_PATH];
2000     WCHAR szUsername[MAX_PATH];
2001     WCHAR szNoUsername[MAX_PATH];
2002     WCHAR szCategory[MAX_PATH];
2003     WCHAR szNoCategory[MAX_PATH];
2004     PWCHAR lpTitleTemplateEnd;
2005 
2006     SYSTEMTIME time;
2007     LVITEMW lviEventItem;
2008 
2009     /* Save the current event log filter globally */
2010     EventLogFilter_AddRef(EventLogFilter);
2011     ActiveFilter = EventLogFilter;
2012 
2013 
2014     /** HACK!! **/
2015     EventLog = EventLogFilter->EventLogs[0];
2016 
2017     // FIXME: Use something else instead of EventLog->LogName !!
2018 
2019     /*
2020      * Use a different formatting, whether the event log filter holds
2021      * only one log, or many logs (the latter case is WIP TODO!)
2022      */
2023     if (EventLogFilter->NumOfEventLogs <= 1)
2024     {
2025         StringCchPrintfExW(szWindowTitle,
2026                            ARRAYSIZE(szWindowTitle),
2027                            &lpTitleTemplateEnd,
2028                            &cchRemaining,
2029                            0,
2030                            szTitleTemplate, szTitle, EventLog->LogName); /* i = number of characters written */
2031         dwMaxLength = (DWORD)cchRemaining;
2032         if (!EventLog->ComputerName)
2033             GetComputerNameW(lpTitleTemplateEnd, &dwMaxLength);
2034         else
2035             StringCchCopyW(lpTitleTemplateEnd, dwMaxLength, EventLog->ComputerName);
2036 
2037         StringCbPrintfW(szStatusText,
2038                         sizeof(szStatusText),
2039                         szStatusBarTemplate,
2040                         EventLog->LogName,
2041                         0,
2042                         0);
2043     }
2044     else
2045     {
2046         // TODO: Use a different title & implement filtering for multi-log filters !!
2047         // (EventLogFilter->NumOfEventLogs > 1)
2048         MessageBoxW(hwndMainWindow,
2049                     L"Many-logs filtering is not implemented yet!!",
2050                     L"Event Log",
2051                     MB_OK | MB_ICONINFORMATION);
2052     }
2053 
2054     /* Set the window title */
2055     SetWindowTextW(hwndMainWindow, szWindowTitle);
2056 
2057     /* Update the status bar */
2058     StatusBar_SetText(hwndStatus, 0, szStatusText);
2059 
2060 
2061     /* Disable list view redraw */
2062     SendMessageW(hwndListView, WM_SETREDRAW, FALSE, 0);
2063 
2064     /* Clear the list view and free the cached records */
2065     ListView_DeleteAllItems(hwndListView);
2066     FreeRecords();
2067 
2068     SendMessageW(hwndListView, LVM_PROGRESS, 0, TRUE);
2069     ProgressBar_SetRange(hwndStatusProgress, 0);
2070     StatusBar_SetText(hwndStatus, 0, NULL);
2071     ShowWindow(hwndStatusProgress, SW_SHOW);
2072 
2073     /* Do a loop over the logs enumerated in the filter */
2074     // FIXME: For now we only support 1 event log per filter!
2075     LogIndex = 0;
2076     // for (LogIndex = 0; LogIndex < EventLogFilter->NumOfEventLogs; ++LogIndex)
2077     {
2078 
2079     EventLog = EventLogFilter->EventLogs[LogIndex];
2080 
2081     /* Open the event log */
2082     if (EventLog->Permanent)
2083         hEventLog = OpenEventLogW(EventLog->ComputerName, EventLog->LogName);
2084     else
2085         hEventLog = OpenBackupEventLogW(EventLog->ComputerName, EventLog->LogName); // FileName
2086 
2087     if (hEventLog == NULL)
2088     {
2089         ShowWin32Error(GetLastError());
2090         goto Cleanup;
2091     }
2092 
2093     // GetOldestEventLogRecord(hEventLog, &dwThisRecord);
2094 
2095     /* Get the total number of event log records */
2096     GetNumberOfEventLogRecords(hEventLog, &dwTotalRecords);
2097 
2098     if (dwTotalRecords > 0)
2099     {
2100         EnableMenuItem(hMainMenu, IDM_CLEAR_EVENTS, MF_BYCOMMAND | MF_ENABLED);
2101         EnableMenuItem(hMainMenu, IDM_SAVE_EVENTLOG, MF_BYCOMMAND | MF_ENABLED);
2102     }
2103     else
2104     {
2105         EnableMenuItem(hMainMenu, IDM_CLEAR_EVENTS, MF_BYCOMMAND | MF_GRAYED);
2106         EnableMenuItem(hMainMenu, IDM_SAVE_EVENTLOG, MF_BYCOMMAND | MF_GRAYED);
2107     }
2108 
2109     /* Set up the event records cache */
2110     g_RecordPtrs = HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, dwTotalRecords * sizeof(*g_RecordPtrs));
2111     if (!g_RecordPtrs)
2112     {
2113         // ShowWin32Error(GetLastError());
2114         goto Quit;
2115     }
2116     g_TotalRecords = dwTotalRecords;
2117 
2118     if (WaitForSingleObject(hStopEnumEvent, 0) == WAIT_OBJECT_0)
2119         goto Quit;
2120 
2121     LoadStringW(hInst, IDS_NOT_AVAILABLE, szNoUsername, ARRAYSIZE(szNoUsername));
2122     LoadStringW(hInst, IDS_NONE, szNoCategory, ARRAYSIZE(szNoCategory));
2123 
2124     ProgressBar_SetRange(hwndStatusProgress, MAKELPARAM(0, 100));
2125     uStepAt = (dwTotalRecords / 100) + 1;
2126 
2127     dwFlags = EVENTLOG_SEQUENTIAL_READ | (Settings.bNewestEventsFirst ? EVENTLOG_FORWARDS_READ : EVENTLOG_BACKWARDS_READ);
2128 
2129     /* 0x7ffff is the maximum buffer size ReadEventLog will accept */
2130     dwWanted = 0x7ffff;
2131     pEvlr = HeapAlloc(hProcessHeap, 0, dwWanted);
2132 
2133     if (!pEvlr)
2134         goto Quit;
2135 
2136     while (dwStatus == ERROR_SUCCESS)
2137     {
2138         bResult =  ReadEventLogW(hEventLog, dwFlags, 0, pEvlr, dwWanted, &dwRead, &dwNeeded);
2139         dwStatus = GetLastError();
2140 
2141         if (!bResult && dwStatus == ERROR_INSUFFICIENT_BUFFER)
2142         {
2143             pEvlr = HeapReAlloc(hProcessHeap, 0, pEvlr, dwNeeded);
2144             dwWanted = dwNeeded;
2145 
2146             if (!pEvlr)
2147                 break;
2148 
2149             bResult =  ReadEventLogW(hEventLog, dwFlags, 0, pEvlr, dwNeeded, &dwRead, &dwNeeded);
2150 
2151             if (!bResult)
2152                 break;
2153         }
2154         else if (!bResult)
2155         {
2156             /* Exit on other errors (ERROR_HANDLE_EOF) */
2157             break;
2158         }
2159 
2160         pEvlrBuffer = (LPBYTE)pEvlr;
2161         pEvlrEnd = pEvlrBuffer + dwRead;
2162 
2163         while (pEvlrBuffer < pEvlrEnd)
2164         {
2165             PEVENTLOGRECORD pEvlrTmp = (PEVENTLOGRECORD)pEvlrBuffer;
2166             PWSTR lpszUsername, lpszCategoryName;
2167             g_RecordPtrs[dwCurrentRecord] = NULL;
2168 
2169             // ProgressBar_StepIt(hwndStatusProgress);
2170             uStep++;
2171             if (uStep % uStepAt == 0)
2172             {
2173                 ++uPos;
2174                 ProgressBar_SetPos(hwndStatusProgress, uPos);
2175             }
2176 
2177            if (WaitForSingleObject(hStopEnumEvent, 0) == WAIT_OBJECT_0)
2178                 goto Quit;
2179 
2180             /* Filter by event type */
2181             if (!FilterByType(EventLogFilter, pEvlrTmp))
2182                 goto SkipEvent;
2183 
2184             /* Get the event source name and filter it */
2185             lpszSourceName = (LPWSTR)(pEvlrBuffer + sizeof(EVENTLOGRECORD));
2186             if (!FilterByString(EventLogFilter->Sources, lpszSourceName))
2187                 goto SkipEvent;
2188 
2189             /* Get the computer name and filter it */
2190             lpszComputerName = (LPWSTR)(pEvlrBuffer + sizeof(EVENTLOGRECORD) + (wcslen(lpszSourceName) + 1) * sizeof(WCHAR));
2191             if (!FilterByString(EventLogFilter->ComputerNames, lpszComputerName))
2192                 goto SkipEvent;
2193 
2194             /* Compute the event time */
2195             EventTimeToSystemTime(pEvlrTmp->TimeWritten, &time);
2196             GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time, NULL, szLocalDate, ARRAYSIZE(szLocalDate));
2197             GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &time, NULL, szLocalTime, ARRAYSIZE(szLocalTime));
2198 
2199             /* Get the username that generated the event, and filter it */
2200             lpszUsername = GetEventUserName(pEvlrTmp, &pLastSid, szUsername) ? szUsername : szNoUsername;
2201 
2202             if (!FilterByString(EventLogFilter->Users, lpszUsername))
2203                 goto SkipEvent;
2204 
2205             // TODO: Filter by event ID and category
2206             GetEventType(pEvlrTmp->EventType, szEventTypeText);
2207 
2208             lpszCategoryName = GetEventCategory(EventLog->LogName, lpszSourceName, pEvlrTmp, szCategory) ? szCategory : szNoCategory;
2209 
2210             StringCbPrintfW(szEventID, sizeof(szEventID), L"%u", (pEvlrTmp->EventID & 0xFFFF));
2211             StringCbPrintfW(szCategoryID, sizeof(szCategoryID), L"%u", pEvlrTmp->EventCategory);
2212 
2213             g_RecordPtrs[dwCurrentRecord] = HeapAlloc(hProcessHeap, 0, pEvlrTmp->Length);
2214             RtlCopyMemory(g_RecordPtrs[dwCurrentRecord], pEvlrTmp, pEvlrTmp->Length);
2215 
2216             lviEventItem.mask = LVIF_IMAGE | LVIF_TEXT | LVIF_PARAM;
2217             lviEventItem.iItem = 0;
2218             lviEventItem.iSubItem = 0;
2219             lviEventItem.lParam = (LPARAM)g_RecordPtrs[dwCurrentRecord];
2220             lviEventItem.pszText = szEventTypeText;
2221 
2222             switch (pEvlrTmp->EventType)
2223             {
2224                 case EVENTLOG_SUCCESS:
2225                 case EVENTLOG_INFORMATION_TYPE:
2226                     lviEventItem.iImage = 0;
2227                     break;
2228 
2229                 case EVENTLOG_WARNING_TYPE:
2230                     lviEventItem.iImage = 1;
2231                     break;
2232 
2233                 case EVENTLOG_ERROR_TYPE:
2234                     lviEventItem.iImage = 2;
2235                     break;
2236 
2237                 case EVENTLOG_AUDIT_SUCCESS:
2238                     lviEventItem.iImage = 3;
2239                     break;
2240 
2241                 case EVENTLOG_AUDIT_FAILURE:
2242                     lviEventItem.iImage = 4;
2243                     break;
2244             }
2245 
2246             lviEventItem.iItem = ListView_InsertItem(hwndListView, &lviEventItem);
2247 
2248             ListView_SetItemText(hwndListView, lviEventItem.iItem, 1, szLocalDate);
2249             ListView_SetItemText(hwndListView, lviEventItem.iItem, 2, szLocalTime);
2250             ListView_SetItemText(hwndListView, lviEventItem.iItem, 3, lpszSourceName);
2251             ListView_SetItemText(hwndListView, lviEventItem.iItem, 4, lpszCategoryName);
2252             ListView_SetItemText(hwndListView, lviEventItem.iItem, 5, szEventID);
2253             ListView_SetItemText(hwndListView, lviEventItem.iItem, 6, lpszUsername);
2254             ListView_SetItemText(hwndListView, lviEventItem.iItem, 7, lpszComputerName);
2255 
2256 SkipEvent:
2257             pEvlrBuffer += pEvlrTmp->Length;
2258             dwCurrentRecord++;
2259         }
2260     }
2261 
2262 Quit:
2263 
2264     if (pEvlr)
2265         HeapFree(hProcessHeap, 0, pEvlr);
2266 
2267     /* Close the event log */
2268     CloseEventLog(hEventLog);
2269 
2270     } // end-for (LogIndex)
2271 
2272     /* All events loaded */
2273 
2274 Cleanup:
2275 
2276     ShowWindow(hwndStatusProgress, SW_HIDE);
2277     SendMessageW(hwndListView, LVM_PROGRESS, 0, FALSE);
2278 
2279     // FIXME: Use something else instead of EventLog->LogName !!
2280 
2281     /*
2282      * Use a different formatting, whether the event log filter holds
2283      * only one log, or many logs (the latter case is WIP TODO!)
2284      */
2285     if (EventLogFilter->NumOfEventLogs <= 1)
2286     {
2287         StringCbPrintfW(szStatusText,
2288                         sizeof(szStatusText),
2289                         szStatusBarTemplate,
2290                         EventLog->LogName,
2291                         dwTotalRecords,
2292                         ListView_GetItemCount(hwndListView));
2293     }
2294     else
2295     {
2296         // TODO: Use a different title & implement filtering for multi-log filters !!
2297         // (EventLogFilter->NumOfEventLogs > 1)
2298     }
2299 
2300     /* Update the status bar */
2301     StatusBar_SetText(hwndStatus, 0, szStatusText);
2302 
2303     /* Resume list view redraw */
2304     SendMessageW(hwndListView, WM_SETREDRAW, TRUE, 0);
2305 
2306     EventLogFilter_Release(EventLogFilter);
2307 
2308     CloseHandle(hStopEnumEvent);
2309     InterlockedExchangePointer((PVOID*)&hStopEnumEvent, NULL);
2310 
2311     return 0;
2312 }
2313 
2314 /*
2315  * The purpose of this thread is to serialize the creation of the events
2316  * enumeration thread, since the Event Log Viewer currently only supports
2317  * one view, one event list, one enumeration.
2318  */
2319 static DWORD WINAPI
2320 StartStopEnumEventsThread(IN LPVOID lpParameter)
2321 {
2322     HANDLE WaitHandles[2];
2323     DWORD  WaitResult;
2324 
2325     WaitHandles[0] = hStartStopEnumEvent; // End-of-application event
2326     WaitHandles[1] = hStartEnumEvent;     // Command event
2327 
2328     while (TRUE)
2329     {
2330         WaitResult = WaitForMultipleObjects(ARRAYSIZE(WaitHandles),
2331                                             WaitHandles,
2332                                             FALSE, // WaitAny
2333                                             INFINITE);
2334         switch (WaitResult)
2335         {
2336             case WAIT_OBJECT_0 + 0:
2337             {
2338                 /* End-of-application event signaled, quit this thread */
2339 
2340                 /* Stop the previous enumeration */
2341                 if (hEnumEventsThread)
2342                 {
2343                     if (hStopEnumEvent)
2344                     {
2345                         SetEvent(hStopEnumEvent);
2346                         WaitForSingleObject(hEnumEventsThread, INFINITE);
2347                         // NOTE: The following is done by the enumeration thread just before terminating.
2348                         // hStopEnumEvent = NULL;
2349                     }
2350 
2351                     CloseHandle(hEnumEventsThread);
2352                     hEnumEventsThread = NULL;
2353                 }
2354 
2355                 /* Clear the list view and free the cached records */
2356                 ListView_DeleteAllItems(hwndListView);
2357                 FreeRecords();
2358 
2359                 /* Reset the active filter */
2360                 ActiveFilter = NULL;
2361 
2362                 return 0;
2363             }
2364 
2365             case WAIT_OBJECT_0 + 1:
2366             {
2367                 /* Restart a new enumeration if needed */
2368                 PEVENTLOGFILTER EventLogFilter;
2369 
2370                 /* Stop the previous enumeration */
2371                 if (hEnumEventsThread)
2372                 {
2373                     if (hStopEnumEvent)
2374                     {
2375                         SetEvent(hStopEnumEvent);
2376                         WaitForSingleObject(hEnumEventsThread, INFINITE);
2377                         // NOTE: The following is done by the enumeration thread just before terminating.
2378                         // hStopEnumEvent = NULL;
2379                     }
2380 
2381                     CloseHandle(hEnumEventsThread);
2382                     hEnumEventsThread = NULL;
2383                 }
2384 
2385                 /* Clear the list view and free the cached records */
2386                 ListView_DeleteAllItems(hwndListView);
2387                 FreeRecords();
2388 
2389                 /* Reset the active filter */
2390                 ActiveFilter = NULL;
2391 
2392                 EventLogFilter = InterlockedExchangePointer((PVOID*)&EnumFilter, NULL);
2393                 if (!EventLogFilter)
2394                     break;
2395 
2396                 // Manual-reset event
2397                 hStopEnumEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
2398                 if (!hStopEnumEvent)
2399                     break;
2400 
2401                 hEnumEventsThread = CreateThread(NULL,
2402                                                  0,
2403                                                  EnumEventsThread,
2404                                                  (LPVOID)EventLogFilter,
2405                                                  CREATE_SUSPENDED,
2406                                                  NULL);
2407                 if (!hEnumEventsThread)
2408                 {
2409                     CloseHandle(hStopEnumEvent);
2410                     hStopEnumEvent = NULL;
2411                     break;
2412                 }
2413                 // CloseHandle(hEnumEventsThread);
2414                 ResumeThread(hEnumEventsThread);
2415 
2416                 break;
2417             }
2418 
2419             default:
2420             {
2421                 /* Unknown command, must never go there! */
2422                 return GetLastError();
2423             }
2424         }
2425     }
2426 
2427     return 0;
2428 }
2429 
2430 VOID
2431 EnumEvents(IN PEVENTLOGFILTER EventLogFilter)
2432 {
2433     /* Signal the enumerator thread we want to enumerate events */
2434     InterlockedExchangePointer((PVOID*)&EnumFilter, EventLogFilter);
2435     SetEvent(hStartEnumEvent);
2436     return;
2437 }
2438 
2439 
2440 PEVENTLOGFILTER
2441 GetSelectedFilter(OUT HTREEITEM* phti OPTIONAL)
2442 {
2443     TVITEMEXW tvItemEx;
2444     HTREEITEM hti;
2445 
2446     if (phti)
2447         *phti = NULL;
2448 
2449     /* Get index of selected item */
2450     hti = TreeView_GetSelection(hwndTreeView);
2451     if (hti == NULL)
2452         return NULL; // No filter
2453 
2454     tvItemEx.mask = TVIF_PARAM;
2455     tvItemEx.hItem = hti;
2456 
2457     TreeView_GetItem(hwndTreeView, &tvItemEx);
2458 
2459     if (phti)
2460         *phti = tvItemEx.hItem;
2461 
2462     return (PEVENTLOGFILTER)tvItemEx.lParam;
2463 }
2464 
2465 
2466 VOID
2467 OpenUserEventLogFile(IN LPCWSTR lpszFileName)
2468 {
2469     WIN32_FIND_DATAW FindData;
2470     HANDLE hFind;
2471     PEVENTLOG EventLog;
2472     PEVENTLOGFILTER EventLogFilter;
2473     SIZE_T cchFileName;
2474     HTREEITEM hItem = NULL;
2475 
2476     /* Check whether the file actually exists */
2477     hFind = FindFirstFileW(lpszFileName, &FindData);
2478     if (hFind == INVALID_HANDLE_VALUE)
2479     {
2480         ShowWin32Error(GetLastError());
2481         return;
2482     }
2483     FindClose(hFind);
2484 
2485     /* Allocate a new event log entry */
2486     EventLog = AllocEventLog(NULL, lpszFileName, FALSE);
2487     if (EventLog == NULL)
2488     {
2489         ShowWin32Error(ERROR_NOT_ENOUGH_MEMORY);
2490         return;
2491     }
2492 
2493     /* Allocate a new event log filter entry for this event log */
2494     EventLogFilter = AllocEventLogFilter(// LogName,
2495                                          TRUE, TRUE, TRUE, TRUE, TRUE,
2496                                          NULL, NULL, NULL,
2497                                          1, &EventLog);
2498     if (EventLogFilter == NULL)
2499     {
2500         ShowWin32Error(ERROR_NOT_ENOUGH_MEMORY);
2501         EventLog_Free(EventLog);
2502         return;
2503     }
2504 
2505     /* Add the event log and the filter into their lists */
2506     InsertTailList(&EventLogList, &EventLog->ListEntry);
2507     InsertTailList(&EventLogFilterList, &EventLogFilter->ListEntry);
2508 
2509     /* Retrieve and cache the event log file */
2510     cchFileName = wcslen(lpszFileName) + 1;
2511     EventLog->FileName = HeapAlloc(GetProcessHeap(), 0, cchFileName * sizeof(WCHAR));
2512     if (EventLog->FileName)
2513         StringCchCopyW(EventLog->FileName, cchFileName, lpszFileName);
2514 
2515     hItem = TreeViewAddItem(hwndTreeView, htiUserLogs,
2516                             (LPWSTR)lpszFileName,
2517                             2, 3, (LPARAM)EventLogFilter);
2518 
2519     /* Select the event log */
2520     if (hItem)
2521     {
2522         // TreeView_Expand(hwndTreeView, htiUserLogs, TVE_EXPAND);
2523         TreeView_SelectItem(hwndTreeView, hItem);
2524         TreeView_EnsureVisible(hwndTreeView, hItem);
2525     }
2526     InvalidateRect(hwndTreeView, NULL, FALSE);
2527     SetFocus(hwndTreeView);
2528 }
2529 
2530 VOID
2531 OpenUserEventLog(VOID)
2532 {
2533     WCHAR szFileName[MAX_PATH];
2534 
2535     ZeroMemory(szFileName, sizeof(szFileName));
2536 
2537     sfn.lpstrFile = szFileName;
2538     sfn.nMaxFile  = ARRAYSIZE(szFileName);
2539 
2540     if (!GetOpenFileNameW(&sfn))
2541         return;
2542     sfn.lpstrFile[sfn.nMaxFile-1] = UNICODE_NULL;
2543 
2544     OpenUserEventLogFile(sfn.lpstrFile);
2545 }
2546 
2547 VOID
2548 SaveEventLog(IN PEVENTLOGFILTER EventLogFilter)
2549 {
2550     PEVENTLOG EventLog;
2551     HANDLE hEventLog;
2552     WCHAR szFileName[MAX_PATH];
2553 
2554     /* Bail out if there is no available filter */
2555     if (!EventLogFilter)
2556         return;
2557 
2558     ZeroMemory(szFileName, sizeof(szFileName));
2559 
2560     sfn.lpstrFile = szFileName;
2561     sfn.nMaxFile  = ARRAYSIZE(szFileName);
2562 
2563     if (!GetSaveFileNameW(&sfn))
2564         return;
2565 
2566     EventLogFilter_AddRef(EventLogFilter);
2567 
2568     EventLog = EventLogFilter->EventLogs[0];
2569     hEventLog = OpenEventLogW(EventLog->ComputerName, EventLog->LogName);
2570 
2571     EventLogFilter_Release(EventLogFilter);
2572 
2573     if (!hEventLog)
2574     {
2575         ShowWin32Error(GetLastError());
2576         return;
2577     }
2578 
2579     if (!BackupEventLogW(hEventLog, szFileName))
2580         ShowWin32Error(GetLastError());
2581 
2582     CloseEventLog(hEventLog);
2583 }
2584 
2585 VOID
2586 CloseUserEventLog(IN PEVENTLOGFILTER EventLogFilter, IN HTREEITEM hti)
2587 {
2588     /* Bail out if there is no available filter */
2589     if (!EventLogFilter)
2590         return;
2591 
2592     if (InterlockedCompareExchangePointer((PVOID*)&ActiveFilter, NULL, NULL) == EventLogFilter)
2593     {
2594         /* Signal the enumerator thread we want to stop enumerating events */
2595         // EnumEvents(NULL);
2596         InterlockedExchangePointer((PVOID*)&EnumFilter, NULL);
2597         SetEvent(hStartEnumEvent);
2598     }
2599 
2600     /*
2601      * The deletion of the item automatically triggers a TVN_SELCHANGED
2602      * notification, that will reset the ActiveFilter (in case the item
2603      * selected is a filter). Otherwise we reset it there.
2604      */
2605     TreeView_DeleteItem(hwndTreeView, hti);
2606 
2607     /* Remove the filter from the list */
2608     RemoveEntryList(&EventLogFilter->ListEntry);
2609     EventLogFilter_Release(EventLogFilter);
2610 
2611     // /* Select the default event log */
2612     // // TreeView_Expand(hwndTreeView, htiUserLogs, TVE_EXPAND);
2613     // TreeView_SelectItem(hwndTreeView, hItem);
2614     // TreeView_EnsureVisible(hwndTreeView, hItem);
2615     InvalidateRect(hwndTreeView, NULL, FALSE);
2616     SetFocus(hwndTreeView);
2617 }
2618 
2619 
2620 BOOL
2621 ClearEvents(IN PEVENTLOGFILTER EventLogFilter)
2622 {
2623     BOOL Success;
2624     PEVENTLOG EventLog;
2625     HANDLE hEventLog;
2626     WCHAR szFileName[MAX_PATH];
2627     WCHAR szMessage[MAX_LOADSTRING];
2628 
2629     /* Bail out if there is no available filter */
2630     if (!EventLogFilter)
2631         return FALSE;
2632 
2633     ZeroMemory(szFileName, sizeof(szFileName));
2634     ZeroMemory(szMessage, sizeof(szMessage));
2635 
2636     LoadStringW(hInst, IDS_CLEAREVENTS_MSG, szMessage, ARRAYSIZE(szMessage));
2637 
2638     sfn.lpstrFile = szFileName;
2639     sfn.nMaxFile  = ARRAYSIZE(szFileName);
2640 
2641     switch (MessageBoxW(hwndMainWindow, szMessage, szTitle, MB_YESNOCANCEL | MB_ICONINFORMATION))
2642     {
2643         case IDCANCEL:
2644             return FALSE;
2645 
2646         case IDNO:
2647             sfn.lpstrFile = NULL;
2648             break;
2649 
2650         case IDYES:
2651             if (!GetSaveFileNameW(&sfn))
2652                 return FALSE;
2653             break;
2654     }
2655 
2656     EventLogFilter_AddRef(EventLogFilter);
2657 
2658     EventLog = EventLogFilter->EventLogs[0];
2659     hEventLog = OpenEventLogW(EventLog->ComputerName, EventLog->LogName);
2660 
2661     EventLogFilter_Release(EventLogFilter);
2662 
2663     if (!hEventLog)
2664     {
2665         ShowWin32Error(GetLastError());
2666         return FALSE;
2667     }
2668 
2669     Success = ClearEventLogW(hEventLog, sfn.lpstrFile);
2670     if (!Success)
2671         ShowWin32Error(GetLastError());
2672 
2673     CloseEventLog(hEventLog);
2674     return Success;
2675 }
2676 
2677 
2678 VOID
2679 Refresh(IN PEVENTLOGFILTER EventLogFilter)
2680 {
2681     /* Bail out if there is no available filter */
2682     if (!EventLogFilter)
2683         return;
2684 
2685     /* Reenumerate the events through the filter */
2686     EnumEvents(EventLogFilter);
2687 }
2688 
2689 
2690 ATOM
2691 MyRegisterClass(HINSTANCE hInstance)
2692 {
2693     WNDCLASSEXW wcex;
2694 
2695     wcex.cbSize = sizeof(wcex);
2696     wcex.style = 0;
2697     wcex.lpfnWndProc = WndProc;
2698     wcex.cbClsExtra = 0;
2699     wcex.cbWndExtra = 0;
2700     wcex.hInstance = hInstance;
2701     wcex.hIcon = LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_EVENTVWR));
2702     wcex.hCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_ARROW));
2703     wcex.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); // COLOR_WINDOW + 1
2704     wcex.lpszMenuName = MAKEINTRESOURCEW(IDM_EVENTVWR);
2705     wcex.lpszClassName = EVENTVWR_WNDCLASS;
2706     wcex.hIconSm = (HICON)LoadImageW(hInstance,
2707                                      MAKEINTRESOURCEW(IDI_EVENTVWR),
2708                                      IMAGE_ICON,
2709                                      16,
2710                                      16,
2711                                      LR_SHARED);
2712 
2713     return RegisterClassExW(&wcex);
2714 }
2715 
2716 
2717 BOOL
2718 GetDisplayNameFileAndID(IN LPCWSTR lpLogName,
2719                         OUT PWCHAR lpModuleName, // TODO: Add IN DWORD BufLen
2720                         OUT PDWORD pdwMessageID)
2721 {
2722     BOOL Success = FALSE;
2723     LONG Result;
2724     HKEY hLogKey;
2725     WCHAR *KeyPath;
2726     SIZE_T cbKeyPath;
2727     DWORD dwType, cbData;
2728     DWORD dwMessageID = 0;
2729     WCHAR szModuleName[MAX_PATH];
2730 
2731     /* Use a default value for the message ID */
2732     *pdwMessageID = 0;
2733 
2734     cbKeyPath = (wcslen(EVENTLOG_BASE_KEY) + wcslen(lpLogName) + 1) * sizeof(WCHAR);
2735     KeyPath = HeapAlloc(GetProcessHeap(), 0, cbKeyPath);
2736     if (!KeyPath)
2737         return FALSE;
2738 
2739     StringCbCopyW(KeyPath, cbKeyPath, EVENTLOG_BASE_KEY);
2740     StringCbCatW(KeyPath, cbKeyPath, lpLogName);
2741 
2742     Result = RegOpenKeyExW(hkMachine, KeyPath, 0, KEY_QUERY_VALUE, &hLogKey);
2743     HeapFree(GetProcessHeap(), 0, KeyPath);
2744     if (Result != ERROR_SUCCESS)
2745         return FALSE;
2746 
2747     cbData = sizeof(szModuleName);
2748     Result = RegQueryValueExW(hLogKey,
2749                               L"DisplayNameFile",
2750                               NULL,
2751                               &dwType,
2752                               (LPBYTE)szModuleName,
2753                               &cbData);
2754     if ((Result != ERROR_SUCCESS) || (dwType != REG_EXPAND_SZ && dwType != REG_SZ))
2755     {
2756         szModuleName[0] = UNICODE_NULL;
2757     }
2758     else
2759     {
2760         /* NULL-terminate the string and expand it */
2761         szModuleName[cbData / sizeof(WCHAR) - 1] = UNICODE_NULL;
2762         GetExpandedFilePathName(lpComputerName, szModuleName, lpModuleName, ARRAYSIZE(szModuleName));
2763         Success = TRUE;
2764     }
2765 
2766     /*
2767      * If we have a 'DisplayNameFile', query for 'DisplayNameID';
2768      * otherwise it's not really useful. 'DisplayNameID' is optional.
2769      */
2770     if (Success)
2771     {
2772         cbData = sizeof(dwMessageID);
2773         Result = RegQueryValueExW(hLogKey,
2774                                   L"DisplayNameID",
2775                                   NULL,
2776                                   &dwType,
2777                                   (LPBYTE)&dwMessageID,
2778                                   &cbData);
2779         if ((Result != ERROR_SUCCESS) || (dwType != REG_DWORD))
2780             dwMessageID = 0;
2781 
2782         *pdwMessageID = dwMessageID;
2783     }
2784 
2785     RegCloseKey(hLogKey);
2786 
2787     return Success;
2788 }
2789 
2790 
2791 VOID
2792 BuildLogListAndFilterList(IN LPCWSTR lpComputerName)
2793 {
2794     LONG Result;
2795     HKEY hEventLogKey, hLogKey;
2796     DWORD dwNumLogs = 0;
2797     DWORD dwIndex, dwMaxKeyLength;
2798     DWORD dwType;
2799     PEVENTLOG EventLog;
2800     PEVENTLOGFILTER EventLogFilter;
2801     LPWSTR LogName = NULL;
2802     WCHAR szModuleName[MAX_PATH];
2803     DWORD lpcName;
2804     DWORD dwMessageID;
2805     LPWSTR lpDisplayName;
2806     HTREEITEM hRootNode = NULL, hItem = NULL, hItemDefault = NULL;
2807 
2808     if (hkMachine && hkMachine != HKEY_LOCAL_MACHINE)
2809     {
2810         /* We are connected to some other computer, close the old connection */
2811         RegCloseKey(hkMachine);
2812         hkMachine = NULL;
2813     }
2814     if (!lpComputerName || !*lpComputerName)
2815     {
2816         /* Use the local computer registry */
2817         hkMachine = HKEY_LOCAL_MACHINE;
2818     }
2819     else
2820     {
2821         /* Connect to the remote computer registry */
2822         Result = RegConnectRegistry(lpComputerName, HKEY_LOCAL_MACHINE, &hkMachine);
2823         if (Result != ERROR_SUCCESS)
2824         {
2825             /* Connection failed, display a message and bail out */
2826             hkMachine = NULL;
2827             ShowWin32Error(GetLastError());
2828             return;
2829         }
2830     }
2831 
2832     /* Open the EventLog key */
2833     Result = RegOpenKeyExW(hkMachine, EVENTLOG_BASE_KEY, 0, KEY_READ, &hEventLogKey);
2834     if (Result != ERROR_SUCCESS)
2835     {
2836         return;
2837     }
2838 
2839     /* Retrieve the number of event logs enumerated as registry keys */
2840     Result = RegQueryInfoKeyW(hEventLogKey, NULL, NULL, NULL, &dwNumLogs, &dwMaxKeyLength,
2841                               NULL, NULL, NULL, NULL, NULL, NULL);
2842     if (Result != ERROR_SUCCESS)
2843     {
2844         goto Quit;
2845     }
2846     if (!dwNumLogs)
2847         goto Quit;
2848 
2849     /* Take the NULL terminator into account */
2850     ++dwMaxKeyLength;
2851 
2852     /* Allocate the temporary buffer */
2853     LogName = HeapAlloc(GetProcessHeap(), 0, dwMaxKeyLength * sizeof(WCHAR));
2854     if (!LogName)
2855         goto Quit;
2856 
2857     /* Enumerate and retrieve each event log name */
2858     for (dwIndex = 0; dwIndex < dwNumLogs; dwIndex++)
2859     {
2860         lpcName = dwMaxKeyLength;
2861         Result = RegEnumKeyExW(hEventLogKey, dwIndex, LogName, &lpcName, NULL, NULL, NULL, NULL);
2862         if (Result != ERROR_SUCCESS)
2863             continue;
2864 
2865         /* Take the NULL terminator into account */
2866         ++lpcName;
2867 
2868         /* Allocate a new event log entry */
2869         EventLog = AllocEventLog(lpComputerName, LogName, TRUE);
2870         if (EventLog == NULL)
2871             continue;
2872 
2873         /* Allocate a new event log filter entry for this event log */
2874         EventLogFilter = AllocEventLogFilter(// LogName,
2875                                              TRUE, TRUE, TRUE, TRUE, TRUE,
2876                                              NULL, NULL, NULL,
2877                                              1, &EventLog);
2878         if (EventLogFilter == NULL)
2879         {
2880             EventLog_Free(EventLog);
2881             continue;
2882         }
2883 
2884         /* Add the event log and the filter into their lists */
2885         InsertTailList(&EventLogList, &EventLog->ListEntry);
2886         InsertTailList(&EventLogFilterList, &EventLogFilter->ListEntry);
2887 
2888         EventLog->FileName = NULL;
2889 
2890         /* Retrieve and cache the event log file */
2891         Result = RegOpenKeyExW(hEventLogKey,
2892                                LogName,
2893                                0,
2894                                KEY_QUERY_VALUE,
2895                                &hLogKey);
2896         if (Result == ERROR_SUCCESS)
2897         {
2898             lpcName = 0;
2899             Result = RegQueryValueExW(hLogKey,
2900                                       L"File",
2901                                       NULL,
2902                                       &dwType,
2903                                       NULL,
2904                                       &lpcName);
2905             if ((Result != ERROR_SUCCESS) || (dwType != REG_EXPAND_SZ && dwType != REG_SZ))
2906             {
2907                 // Windows' EventLog uses some kind of default value, we do not.
2908                 EventLog->FileName = NULL;
2909             }
2910             else
2911             {
2912                 lpcName = ROUND_DOWN(lpcName, sizeof(WCHAR));
2913                 EventLog->FileName = HeapAlloc(GetProcessHeap(), 0, lpcName);
2914                 if (EventLog->FileName)
2915                 {
2916                     Result = RegQueryValueExW(hLogKey,
2917                                               L"File",
2918                                               NULL,
2919                                               &dwType,
2920                                               (LPBYTE)EventLog->FileName,
2921                                               &lpcName);
2922                     if (Result != ERROR_SUCCESS)
2923                     {
2924                         HeapFree(GetProcessHeap(), 0, EventLog->FileName);
2925                         EventLog->FileName = NULL;
2926                     }
2927                     else
2928                     {
2929                         EventLog->FileName[lpcName / sizeof(WCHAR) - 1] = UNICODE_NULL;
2930                     }
2931                 }
2932             }
2933 
2934             RegCloseKey(hLogKey);
2935         }
2936 
2937         /* Get the display name for the event log */
2938         lpDisplayName = NULL;
2939 
2940         ZeroMemory(szModuleName, sizeof(szModuleName));
2941         if (GetDisplayNameFileAndID(LogName, szModuleName, &dwMessageID))
2942         {
2943             /* Retrieve the message string without appending extra newlines */
2944             lpDisplayName =
2945             GetMessageStringFromDll(szModuleName,
2946                                     FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
2947                                     FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
2948                                     dwMessageID,
2949                                     0,
2950                                     NULL);
2951         }
2952 
2953         /*
2954          * Select the correct tree root node, whether the log is a System
2955          * or an Application log. Default to Application log otherwise.
2956          */
2957         hRootNode = htiAppLogs;
2958         for (lpcName = 0; lpcName < ARRAYSIZE(SystemLogs); ++lpcName)
2959         {
2960             /* Check whether the log name is part of the system logs */
2961             if (wcsicmp(LogName, SystemLogs[lpcName]) == 0)
2962             {
2963                 hRootNode = htiSystemLogs;
2964                 break;
2965             }
2966         }
2967 
2968         hItem = TreeViewAddItem(hwndTreeView, hRootNode,
2969                                 (lpDisplayName ? lpDisplayName : LogName),
2970                                 2, 3, (LPARAM)EventLogFilter);
2971 
2972         /* Try to get the default event log: "Application" */
2973         if ((hItemDefault == NULL) && (wcsicmp(LogName, SystemLogs[0]) == 0))
2974         {
2975             hItemDefault = hItem;
2976         }
2977 
2978         /* Free the buffer allocated by FormatMessage */
2979         if (lpDisplayName)
2980             LocalFree(lpDisplayName);
2981     }
2982 
2983     HeapFree(GetProcessHeap(), 0, LogName);
2984 
2985 Quit:
2986     RegCloseKey(hEventLogKey);
2987 
2988     /* Select the default event log */
2989     if (hItemDefault)
2990     {
2991         // TreeView_Expand(hwndTreeView, hRootNode, TVE_EXPAND);
2992         TreeView_SelectItem(hwndTreeView, hItemDefault);
2993         TreeView_EnsureVisible(hwndTreeView, hItemDefault);
2994     }
2995     InvalidateRect(hwndTreeView, NULL, FALSE);
2996     SetFocus(hwndTreeView);
2997 
2998     return;
2999 }
3000 
3001 VOID
3002 FreeLogList(VOID)
3003 {
3004     PLIST_ENTRY Entry;
3005     PEVENTLOG EventLog;
3006 
3007     while (!IsListEmpty(&EventLogList))
3008     {
3009         Entry = RemoveHeadList(&EventLogList);
3010         EventLog = (PEVENTLOG)CONTAINING_RECORD(Entry, EVENTLOG, ListEntry);
3011         EventLog_Free(EventLog);
3012     }
3013 
3014     return;
3015 }
3016 
3017 VOID
3018 FreeLogFilterList(VOID)
3019 {
3020     PLIST_ENTRY Entry;
3021     PEVENTLOGFILTER EventLogFilter;
3022 
3023     while (!IsListEmpty(&EventLogFilterList))
3024     {
3025         Entry = RemoveHeadList(&EventLogFilterList);
3026         EventLogFilter = (PEVENTLOGFILTER)CONTAINING_RECORD(Entry, EVENTLOGFILTER, ListEntry);
3027         EventLogFilter_Free(EventLogFilter);
3028     }
3029 
3030     ActiveFilter = NULL;
3031 
3032     return;
3033 }
3034 
3035 BOOL
3036 InitInstance(HINSTANCE hInstance)
3037 {
3038     RECT rcClient, rs;
3039     LONG StatusHeight;
3040     HIMAGELIST hSmall;
3041     LVCOLUMNW lvc = {0};
3042     WCHAR szTemp[256];
3043 
3044     /* Create the main window */
3045     rs = Settings.wpPos.rcNormalPosition;
3046     hwndMainWindow = CreateWindowW(EVENTVWR_WNDCLASS,
3047                                    szTitle,
3048                                    WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
3049                                    rs.left, rs.top,
3050                                    (rs.right  != CW_USEDEFAULT && rs.left != CW_USEDEFAULT) ? rs.right - rs.left : CW_USEDEFAULT,
3051                                    (rs.bottom != CW_USEDEFAULT && rs.top  != CW_USEDEFAULT) ? rs.bottom - rs.top : CW_USEDEFAULT,
3052                                    NULL,
3053                                    NULL,
3054                                    hInstance,
3055                                    NULL);
3056     if (!hwndMainWindow)
3057         return FALSE;
3058 
3059     /* Create the status bar */
3060     hwndStatus = CreateWindowExW(0,                                  // no extended styles
3061                                  STATUSCLASSNAMEW,                   // status bar
3062                                  L"",                                // no text
3063                                  WS_CHILD | WS_VISIBLE | CCS_BOTTOM | SBARS_SIZEGRIP, // styles
3064                                  0, 0, 0, 0,                         // x, y, cx, cy
3065                                  hwndMainWindow,                     // parent window
3066                                  (HMENU)100,                         // window ID
3067                                  hInstance,                          // instance
3068                                  NULL);                              // window data
3069 
3070     GetClientRect(hwndMainWindow, &rcClient);
3071     GetWindowRect(hwndStatus, &rs);
3072     StatusHeight = rs.bottom - rs.top;
3073 
3074     /* Create a progress bar in the status bar (hidden by default) */
3075     StatusBar_GetItemRect(hwndStatus, 0, &rs);
3076     hwndStatusProgress = CreateWindowExW(0,                          // no extended styles
3077                                          PROGRESS_CLASSW,            // status bar
3078                                          NULL,                       // no text
3079                                          WS_CHILD | PBS_SMOOTH,      // styles
3080                                          rs.left, rs.top,            // x, y
3081                                          rs.right - rs.left, rs.bottom - rs.top, // cx, cy
3082                                          hwndStatus,                 // parent window
3083                                          NULL,                       // window ID
3084                                          hInstance,                  // instance
3085                                          NULL);                      // window data
3086     /* Remove its static edge */
3087     SetWindowLongPtrW(hwndStatusProgress, GWL_EXSTYLE,
3088                       GetWindowLongPtrW(hwndStatusProgress, GWL_EXSTYLE) & ~WS_EX_STATICEDGE);
3089     ProgressBar_SetStep(hwndStatusProgress, 1);
3090 
3091     /* Initialize the splitter default positions */
3092     nVSplitPos = Settings.nVSplitPos;
3093     nHSplitPos = Settings.nHSplitPos;
3094 
3095     /* Create the TreeView */
3096     hwndTreeView = CreateWindowExW(WS_EX_CLIENTEDGE,
3097                                    WC_TREEVIEWW,
3098                                    NULL,
3099                                    // WS_CHILD | WS_VISIBLE | TVS_HASLINES | TVS_SHOWSELALWAYS,
3100                                    WS_CHILD | WS_VISIBLE | /* WS_TABSTOP | */ TVS_HASLINES | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_EDITLABELS | TVS_SHOWSELALWAYS,
3101                                    0, 0,
3102                                    nVSplitPos - SPLIT_WIDTH/2,
3103                                    (rcClient.bottom - rcClient.top) - StatusHeight,
3104                                    hwndMainWindow,
3105                                    NULL,
3106                                    hInstance,
3107                                    NULL);
3108 
3109     /* Create the ImageList */
3110     hSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
3111                               GetSystemMetrics(SM_CYSMICON),
3112                               ILC_COLOR32 | ILC_MASK, // ILC_COLOR24
3113                               1, 1);
3114 
3115     /* Add event type icons to the ImageList: closed/opened folder, event log (normal/viewed) */
3116     ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_CLOSED_CATEGORY)));
3117     ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_OPENED_CATEGORY)));
3118     ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_EVENTLOG)));
3119     ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_EVENTVWR)));
3120 
3121     /* Assign the ImageList to the Tree View */
3122     TreeView_SetImageList(hwndTreeView, hSmall, TVSIL_NORMAL);
3123 
3124     /* Add the event logs nodes */
3125     // "System Logs"
3126     LoadStringW(hInstance, IDS_EVENTLOG_SYSTEM, szTemp, ARRAYSIZE(szTemp));
3127     htiSystemLogs = TreeViewAddItem(hwndTreeView, NULL, szTemp, 0, 1, (LPARAM)NULL);
3128     // "Application Logs"
3129     LoadStringW(hInstance, IDS_EVENTLOG_APP, szTemp, ARRAYSIZE(szTemp));
3130     htiAppLogs = TreeViewAddItem(hwndTreeView, NULL, szTemp, 0, 1, (LPARAM)NULL);
3131     // "User Logs"
3132     LoadStringW(hInstance, IDS_EVENTLOG_USER, szTemp, ARRAYSIZE(szTemp));
3133     htiUserLogs = TreeViewAddItem(hwndTreeView, NULL, szTemp, 0, 1, (LPARAM)NULL);
3134 
3135     /* Create the Event details pane (optional) */
3136     hwndEventDetails = CreateEventDetailsCtrl(hInst, hwndMainWindow, (LPARAM)NULL);
3137     if (hwndEventDetails)
3138     {
3139     SetWindowLongPtrW(hwndEventDetails, GWL_EXSTYLE,
3140                       GetWindowLongPtrW(hwndEventDetails, GWL_EXSTYLE) | WS_EX_CLIENTEDGE);
3141     SetWindowPos(hwndEventDetails, NULL,
3142                  nVSplitPos + SPLIT_WIDTH/2,
3143                  nHSplitPos + SPLIT_WIDTH/2,
3144                  (rcClient.right - rcClient.left) - nVSplitPos - SPLIT_WIDTH/2,
3145                  (rcClient.bottom - rcClient.top) - nHSplitPos - SPLIT_WIDTH/2 - StatusHeight,
3146                  SWP_NOZORDER | SWP_NOACTIVATE | (Settings.bShowDetailsPane ? SWP_SHOWWINDOW : SWP_HIDEWINDOW));
3147     }
3148 
3149     /* Create the ListView */
3150     hwndListView = CreateWindowExW(WS_EX_CLIENTEDGE,
3151                                    WC_LISTVIEWW,
3152                                    NULL,
3153                                    WS_CHILD | WS_VISIBLE | LVS_SHOWSELALWAYS | LVS_REPORT,
3154                                    nVSplitPos + SPLIT_WIDTH/2,
3155                                    0,
3156                                    (rcClient.right - rcClient.left) - nVSplitPos - SPLIT_WIDTH/2,
3157                                    hwndEventDetails && Settings.bShowDetailsPane
3158                                        ? nHSplitPos - SPLIT_WIDTH/2
3159                                        : (rcClient.bottom - rcClient.top) - StatusHeight,
3160                                    hwndMainWindow,
3161                                    NULL,
3162                                    hInstance,
3163                                    NULL);
3164 
3165     /* Add the extended ListView styles */
3166     ListView_SetExtendedListViewStyle(hwndListView, LVS_EX_HEADERDRAGDROP | LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP | (Settings.bShowGrid ? LVS_EX_GRIDLINES : 0));
3167 
3168     /* Create the ImageList */
3169     hSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
3170                               GetSystemMetrics(SM_CYSMICON),
3171                               ILC_COLOR32 | ILC_MASK, // ILC_COLOR24
3172                               1, 1);
3173 
3174     /* Add event type icons to the ImageList */
3175     ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_INFORMATIONICON)));
3176     ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_WARNINGICON)));
3177     ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_ERRORICON)));
3178     ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_AUDITSUCCESSICON)));
3179     ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_AUDITFAILUREICON)));
3180 
3181     /* Assign the ImageList to the List View */
3182     ListView_SetImageList(hwndListView, hSmall, LVSIL_SMALL);
3183 
3184     /* Now set up the listview with its columns */
3185     lvc.mask = LVCF_TEXT | LVCF_WIDTH;
3186     lvc.cx = 90;
3187     LoadStringW(hInstance,
3188                 IDS_COLUMNTYPE,
3189                 szTemp,
3190                 ARRAYSIZE(szTemp));
3191     lvc.pszText = szTemp;
3192     ListView_InsertColumn(hwndListView, 0, &lvc);
3193 
3194     lvc.cx = 70;
3195     LoadStringW(hInstance,
3196                 IDS_COLUMNDATE,
3197                 szTemp,
3198                 ARRAYSIZE(szTemp));
3199     lvc.pszText = szTemp;
3200     ListView_InsertColumn(hwndListView, 1, &lvc);
3201 
3202     lvc.cx = 70;
3203     LoadStringW(hInstance,
3204                 IDS_COLUMNTIME,
3205                 szTemp,
3206                 ARRAYSIZE(szTemp));
3207     lvc.pszText = szTemp;
3208     ListView_InsertColumn(hwndListView, 2, &lvc);
3209 
3210     lvc.cx = 150;
3211     LoadStringW(hInstance,
3212                 IDS_COLUMNSOURCE,
3213                 szTemp,
3214                 ARRAYSIZE(szTemp));
3215     lvc.pszText = szTemp;
3216     ListView_InsertColumn(hwndListView, 3, &lvc);
3217 
3218     lvc.cx = 100;
3219     LoadStringW(hInstance,
3220                 IDS_COLUMNCATEGORY,
3221                 szTemp,
3222                 ARRAYSIZE(szTemp));
3223     lvc.pszText = szTemp;
3224     ListView_InsertColumn(hwndListView, 4, &lvc);
3225 
3226     lvc.cx = 60;
3227     LoadStringW(hInstance,
3228                 IDS_COLUMNEVENT,
3229                 szTemp,
3230                 ARRAYSIZE(szTemp));
3231     lvc.pszText = szTemp;
3232     ListView_InsertColumn(hwndListView, 5, &lvc);
3233 
3234     lvc.cx = 120;
3235     LoadStringW(hInstance,
3236                 IDS_COLUMNUSER,
3237                 szTemp,
3238                 ARRAYSIZE(szTemp));
3239     lvc.pszText = szTemp;
3240     ListView_InsertColumn(hwndListView, 6, &lvc);
3241 
3242     lvc.cx = 100;
3243     LoadStringW(hInstance,
3244                 IDS_COLUMNCOMPUTER,
3245                 szTemp,
3246                 ARRAYSIZE(szTemp));
3247     lvc.pszText = szTemp;
3248     ListView_InsertColumn(hwndListView, 7, &lvc);
3249 
3250     /* Initialize the save Dialog */
3251     ZeroMemory(&sfn, sizeof(sfn));
3252     ZeroMemory(szSaveFilter, sizeof(szSaveFilter));
3253 
3254     LoadStringW(hInst, IDS_SAVE_FILTER, szSaveFilter, ARRAYSIZE(szSaveFilter));
3255 
3256     sfn.lStructSize     = sizeof(sfn);
3257     sfn.hwndOwner       = hwndMainWindow;
3258     sfn.hInstance       = hInstance;
3259     sfn.lpstrFilter     = szSaveFilter;
3260     sfn.lpstrInitialDir = NULL;
3261     sfn.Flags           = OFN_HIDEREADONLY | OFN_SHAREAWARE;
3262     sfn.lpstrDefExt     = NULL;
3263 
3264     ShowWindow(hwndMainWindow, Settings.wpPos.showCmd);
3265     UpdateWindow(hwndMainWindow);
3266 
3267     return TRUE;
3268 }
3269 
3270 VOID ResizeWnd(INT cx, INT cy)
3271 {
3272     RECT rs;
3273     LONG StatusHeight;
3274     LONG_PTR dwExStyle;
3275     HDWP hdwp;
3276 
3277     /* Resize the status bar -- now done in WM_SIZE */
3278     // SendMessageW(hwndStatus, WM_SIZE, 0, 0);
3279     GetWindowRect(hwndStatus, &rs);
3280     StatusHeight = rs.bottom - rs.top;
3281 
3282     /*
3283      * Move the progress bar -- Take into account for extra size due to the static edge
3284      * (AdjustWindowRectEx() does not seem to work for the progress bar).
3285      */
3286     StatusBar_GetItemRect(hwndStatus, 0, &rs);
3287     dwExStyle = GetWindowLongPtrW(hwndStatusProgress, GWL_EXSTYLE);
3288     SetWindowLongPtrW(hwndStatusProgress, GWL_EXSTYLE, dwExStyle | WS_EX_STATICEDGE);
3289     MoveWindow(hwndStatusProgress,
3290                rs.left, rs.top, rs.right - rs.left, rs.bottom - rs.top,
3291                IsWindowVisible(hwndStatusProgress) ? TRUE : FALSE);
3292     SetWindowLongPtrW(hwndStatusProgress, GWL_EXSTYLE, dwExStyle);
3293 
3294     /*
3295      * TODO: Adjust the splitter positions:
3296      * - Vertical splitter (1)  : fixed position from the left window side.
3297      * - Horizontal splitter (2): fixed position from the bottom window side.
3298      */
3299     nVSplitPos = min(max(nVSplitPos, SPLIT_WIDTH/2), cx - SPLIT_WIDTH/2); // OK
3300     nHSplitPos = min(max(nHSplitPos, SPLIT_WIDTH/2), cy - SPLIT_WIDTH/2 - StatusHeight); // FIXME!
3301 
3302     hdwp = BeginDeferWindowPos(3);
3303 
3304     if (hdwp)
3305         hdwp = DeferWindowPos(hdwp,
3306                               hwndTreeView,
3307                               HWND_TOP,
3308                               0, 0,
3309                               nVSplitPos - SPLIT_WIDTH/2,
3310                               cy - StatusHeight,
3311                               SWP_NOZORDER | SWP_NOACTIVATE);
3312 
3313     if (hdwp)
3314         hdwp = DeferWindowPos(hdwp,
3315                               hwndListView,
3316                               HWND_TOP,
3317                               nVSplitPos + SPLIT_WIDTH/2, 0,
3318                               cx - nVSplitPos - SPLIT_WIDTH/2,
3319                               hwndEventDetails && Settings.bShowDetailsPane
3320                                   ? nHSplitPos - SPLIT_WIDTH/2
3321                                   : cy - StatusHeight,
3322                               SWP_NOZORDER | SWP_NOACTIVATE);
3323 
3324     if (hwndEventDetails && Settings.bShowDetailsPane && hdwp)
3325         hdwp = DeferWindowPos(hdwp,
3326                               hwndEventDetails,
3327                               HWND_TOP,
3328                               nVSplitPos + SPLIT_WIDTH/2,
3329                               nHSplitPos + SPLIT_WIDTH/2,
3330                               cx - nVSplitPos - SPLIT_WIDTH/2,
3331                               cy - nHSplitPos - SPLIT_WIDTH/2 - StatusHeight,
3332                               SWP_NOZORDER | SWP_NOACTIVATE);
3333 
3334     if (hdwp)
3335         EndDeferWindowPos(hdwp);
3336 }
3337 
3338 
3339 LRESULT CALLBACK
3340 WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3341 {
3342     RECT rect;
3343 
3344     switch (uMsg)
3345     {
3346         case WM_CREATE:
3347             hMainMenu = GetMenu(hWnd);
3348             break;
3349 
3350         case WM_DESTROY:
3351         {
3352             GetWindowPlacement(hwndMainWindow, &Settings.wpPos);
3353             PostQuitMessage(0);
3354             break;
3355         }
3356 
3357         case WM_NOTIFY:
3358         {
3359             LPNMHDR hdr = (LPNMHDR)lParam;
3360 
3361             if (hdr->hwndFrom == hwndListView)
3362             {
3363                 switch (hdr->code)
3364                 {
3365                     case LVN_ITEMCHANGED:
3366                     {
3367                         LPNMLISTVIEW pnmv = (LPNMLISTVIEW)lParam;
3368 
3369                         if ( (pnmv->uChanged  & LVIF_STATE) && /* The state has changed */
3370                              (pnmv->uNewState & LVIS_SELECTED) /* The item has been (de)selected */ )
3371                         {
3372                             if (hwndEventDetails)
3373                                 SendMessageW(hwndEventDetails, EVT_DISPLAY, 0, 0);
3374                         }
3375                         break;
3376                     }
3377 
3378                     case NM_DBLCLK:
3379                     case NM_RETURN:
3380                         SendMessageW(hWnd, WM_COMMAND, IDM_EVENT_DETAILS, 0);
3381                         break;
3382                 }
3383             }
3384             else if (hdr->hwndFrom == hwndTreeView)
3385             {
3386                 switch (hdr->code)
3387                 {
3388                     case TVN_BEGINLABELEDIT:
3389                     {
3390                         HTREEITEM hItem = ((LPNMTVDISPINFO)lParam)->item.hItem;
3391 
3392                         /* Disable label editing for root nodes */
3393                         return ((hItem == htiSystemLogs) ||
3394                                 (hItem == htiAppLogs)    ||
3395                                 (hItem == htiUserLogs));
3396                     }
3397 
3398                     case TVN_ENDLABELEDIT:
3399                     {
3400                         TVITEMW item = ((LPNMTVDISPINFO)lParam)->item;
3401                         HTREEITEM hItem = item.hItem;
3402 
3403                         /* Disable label editing for root nodes */
3404                         if ((hItem == htiSystemLogs) ||
3405                             (hItem == htiAppLogs)    ||
3406                             (hItem == htiUserLogs))
3407                         {
3408                             return FALSE;
3409                         }
3410 
3411                         if (item.pszText)
3412                         {
3413                             LPWSTR pszText = item.pszText;
3414 
3415                             /* Trim leading whitespace */
3416                             while (*pszText && iswspace(*pszText))
3417                                 ++pszText;
3418 
3419                             if (!*pszText)
3420                                 return FALSE;
3421 
3422                             return TRUE;
3423                         }
3424                         else
3425                         {
3426                             return FALSE;
3427                         }
3428                     }
3429 
3430                     case TVN_SELCHANGED:
3431                     {
3432                         PEVENTLOGFILTER EventLogFilter =
3433                             (PEVENTLOGFILTER)((LPNMTREEVIEW)lParam)->itemNew.lParam;
3434 
3435                         // FIXME: It might be nice to reference here the filter,
3436                         // so that we don't have to reference/dereference it many times
3437                         // in the other functions???
3438 
3439                         // FIXME: This is a hack!!
3440                         if (hwndEventDetails && EventLogFilter)
3441                         {
3442                             SendMessageW(hwndEventDetails, EVT_SETFILTER, 0, (LPARAM)EventLogFilter);
3443                         }
3444 
3445                         if (EventLogFilter)
3446                         {
3447                             /*
3448                              * If we have selected a filter, enable the menu commands;
3449                              * they will possibly be updated after events enumeration.
3450                              */
3451                             EnableMenuItem(hMainMenu, IDM_SAVE_EVENTLOG, MF_BYCOMMAND | MF_ENABLED);
3452                             EnableMenuItem(hMainMenu, IDM_CLOSE_EVENTLOG, MF_BYCOMMAND | MF_ENABLED);
3453                             EnableMenuItem(hMainMenu, IDM_CLEAR_EVENTS, MF_BYCOMMAND | MF_ENABLED);
3454                             EnableMenuItem(hMainMenu, IDM_RENAME_EVENTLOG, MF_BYCOMMAND | MF_ENABLED);
3455                             EnableMenuItem(hMainMenu, IDM_EVENTLOG_SETTINGS, MF_BYCOMMAND | MF_ENABLED);
3456                         }
3457                         else
3458                         {
3459                             EnableMenuItem(hMainMenu, IDM_SAVE_EVENTLOG, MF_BYCOMMAND | MF_GRAYED);
3460                             EnableMenuItem(hMainMenu, IDM_CLOSE_EVENTLOG, MF_BYCOMMAND | MF_GRAYED);
3461                             EnableMenuItem(hMainMenu, IDM_CLEAR_EVENTS, MF_BYCOMMAND | MF_GRAYED);
3462                             EnableMenuItem(hMainMenu, IDM_RENAME_EVENTLOG, MF_BYCOMMAND | MF_GRAYED);
3463                             EnableMenuItem(hMainMenu, IDM_EVENTLOG_SETTINGS, MF_BYCOMMAND | MF_GRAYED);
3464                         }
3465 
3466                         /*
3467                          * The enumeration thread that is triggered by EnumEvents
3468                          * will set a new value for the 'ActiveFilter'.
3469                          */
3470                         if (EventLogFilter)
3471                             EnumEvents(EventLogFilter);
3472 
3473                         break;
3474                     }
3475                 }
3476             }
3477             break;
3478         }
3479 
3480         case WM_COMMAND:
3481         {
3482             /* Parse the menu selections */
3483             switch (LOWORD(wParam))
3484             {
3485                 case IDM_OPEN_EVENTLOG:
3486                     OpenUserEventLog();
3487                     break;
3488 
3489                 case IDM_SAVE_EVENTLOG:
3490                     SaveEventLog(GetSelectedFilter(NULL));
3491                     break;
3492 
3493                 case IDM_CLOSE_EVENTLOG:
3494                 {
3495                     HTREEITEM hti;
3496                     PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(&hti);
3497                     CloseUserEventLog(EventLogFilter, hti);
3498                     break;
3499                 }
3500 
3501                 case IDM_CLEAR_EVENTS:
3502                 {
3503                     PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(NULL);
3504                     if (EventLogFilter && ClearEvents(EventLogFilter))
3505                         Refresh(EventLogFilter);
3506                     break;
3507                 }
3508 
3509                 case IDM_RENAME_EVENTLOG:
3510                     if (GetFocus() == hwndTreeView)
3511                         TreeView_EditLabel(hwndTreeView, TreeView_GetSelection(hwndTreeView));
3512                     break;
3513 
3514                 case IDM_EVENTLOG_SETTINGS:
3515                 {
3516                     PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(NULL);
3517                     // TODO: Check the returned value?
3518                     if (EventLogFilter)
3519                         EventLogProperties(hInst, hWnd, EventLogFilter);
3520                     break;
3521                 }
3522 
3523                 case IDM_LIST_NEWEST:
3524                 {
3525                     CheckMenuRadioItem(hMainMenu, IDM_LIST_NEWEST, IDM_LIST_OLDEST, IDM_LIST_NEWEST, MF_BYCOMMAND);
3526                     if (!Settings.bNewestEventsFirst)
3527                     {
3528                         Settings.bNewestEventsFirst = TRUE;
3529                         Refresh(GetSelectedFilter(NULL));
3530                     }
3531                     break;
3532                 }
3533 
3534                 case IDM_LIST_OLDEST:
3535                 {
3536                     CheckMenuRadioItem(hMainMenu, IDM_LIST_NEWEST, IDM_LIST_OLDEST, IDM_LIST_OLDEST, MF_BYCOMMAND);
3537                     if (Settings.bNewestEventsFirst)
3538                     {
3539                         Settings.bNewestEventsFirst = FALSE;
3540                         Refresh(GetSelectedFilter(NULL));
3541                     }
3542                     break;
3543                 }
3544 
3545                 case IDM_EVENT_DETAILS:
3546                 {
3547                     // LPNMITEMACTIVATE lpnmitem = (LPNMITEMACTIVATE)lParam;
3548                     PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(NULL);
3549                     if (/*lpnmitem->iItem != -1 &&*/ EventLogFilter)
3550                     {
3551                         EventLogFilter_AddRef(EventLogFilter);
3552                         DialogBoxParamW(hInst,
3553                                         MAKEINTRESOURCEW(IDD_EVENTDETAILS_DLG),
3554                                         hWnd,
3555                                         EventDetails,
3556                                         (LPARAM)EventLogFilter);
3557                         EventLogFilter_Release(EventLogFilter);
3558                     }
3559                     break;
3560                 }
3561 
3562                 case IDM_REFRESH:
3563                     Refresh(GetSelectedFilter(NULL));
3564                     break;
3565 
3566                 case IDM_EVENT_DETAILS_VIEW:
3567                 {
3568                     Settings.bShowDetailsPane = !Settings.bShowDetailsPane;
3569                     CheckMenuItem(hMainMenu, IDM_EVENT_DETAILS_VIEW,
3570                                   MF_BYCOMMAND | (Settings.bShowDetailsPane ? MF_CHECKED : MF_UNCHECKED));
3571 
3572                     GetClientRect(hWnd, &rect);
3573                     if (Settings.bShowDetailsPane)
3574                     {
3575                         ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
3576                         ShowWindow(hwndEventDetails, SW_SHOW);
3577                     }
3578                     else
3579                     {
3580                         ShowWindow(hwndEventDetails, SW_HIDE);
3581                         ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
3582                     }
3583 
3584                     break;
3585                 }
3586 
3587                 case IDM_LIST_GRID_LINES:
3588                 {
3589                     Settings.bShowGrid = !Settings.bShowGrid;
3590                     CheckMenuItem(hMainMenu, IDM_LIST_GRID_LINES,
3591                                   MF_BYCOMMAND | (Settings.bShowGrid ? MF_CHECKED : MF_UNCHECKED));
3592 
3593                     ListView_SetExtendedListViewStyleEx(hwndListView, LVS_EX_GRIDLINES, (Settings.bShowGrid ? LVS_EX_GRIDLINES : 0));
3594                     break;
3595                 }
3596 
3597                 case IDM_SAVE_SETTINGS:
3598                 {
3599                     Settings.bSaveSettings = !Settings.bSaveSettings;
3600                     CheckMenuItem(hMainMenu, IDM_SAVE_SETTINGS,
3601                                   MF_BYCOMMAND | (Settings.bSaveSettings ? MF_CHECKED : MF_UNCHECKED));
3602                     break;
3603                 }
3604 
3605                 case IDM_ABOUT:
3606                 {
3607                     HICON hIcon;
3608                     WCHAR szCopyright[MAX_LOADSTRING];
3609 
3610                     hIcon = LoadIconW(hInst, MAKEINTRESOURCEW(IDI_EVENTVWR));
3611                     LoadStringW(hInst, IDS_COPYRIGHT, szCopyright, ARRAYSIZE(szCopyright));
3612                     ShellAboutW(hWnd, szTitle, szCopyright, hIcon);
3613                     DeleteObject(hIcon);
3614                     break;
3615                 }
3616 
3617                 case IDM_HELP:
3618                     MessageBoxW(hwndMainWindow,
3619                                 L"Help not implemented yet!",
3620                                 L"Event Log",
3621                                 MB_OK | MB_ICONINFORMATION);
3622                                 break;
3623 
3624                 case IDM_EXIT:
3625                     DestroyWindow(hWnd);
3626                     break;
3627 
3628                 default:
3629                     return DefWindowProcW(hWnd, uMsg, wParam, lParam);
3630             }
3631             break;
3632         }
3633 
3634         case WM_INITMENU:
3635         {
3636             if ((HMENU)wParam != hMainMenu)
3637                 break;
3638 
3639             CheckMenuRadioItem(hMainMenu, IDM_LIST_NEWEST, IDM_LIST_OLDEST,
3640                                Settings.bNewestEventsFirst ? IDM_LIST_NEWEST : IDM_LIST_OLDEST,
3641                                MF_BYCOMMAND);
3642 
3643             if (!hwndEventDetails)
3644             {
3645                 EnableMenuItem(hMainMenu, IDM_EVENT_DETAILS_VIEW,
3646                                MF_BYCOMMAND | MF_GRAYED);
3647             }
3648             CheckMenuItem(hMainMenu, IDM_EVENT_DETAILS_VIEW,
3649                           MF_BYCOMMAND | (Settings.bShowDetailsPane ? MF_CHECKED : MF_UNCHECKED));
3650 
3651             CheckMenuItem(hMainMenu, IDM_LIST_GRID_LINES,
3652                           MF_BYCOMMAND | (Settings.bShowGrid ? MF_CHECKED : MF_UNCHECKED));
3653 
3654             CheckMenuItem(hMainMenu, IDM_SAVE_SETTINGS,
3655                           MF_BYCOMMAND | (Settings.bSaveSettings ? MF_CHECKED : MF_UNCHECKED));
3656 
3657             break;
3658         }
3659 
3660 #if 0
3661         case WM_INITMENUPOPUP:
3662             lParam = lParam;
3663             break;
3664 
3665         case WM_CONTEXTMENU:
3666             lParam = lParam;
3667             break;
3668 #endif
3669 
3670         case WM_SETCURSOR:
3671         {
3672             POINT pt;
3673 
3674             if (LOWORD(lParam) != HTCLIENT)
3675                 goto Default;
3676 
3677             GetCursorPos(&pt);
3678             ScreenToClient(hWnd, &pt);
3679 
3680             /* Set the cursor for the vertical splitter */
3681             if (pt.x >= nVSplitPos - SPLIT_WIDTH/2 && pt.x < nVSplitPos + SPLIT_WIDTH/2 + 1)
3682             {
3683                 RECT rs;
3684                 GetClientRect(hWnd, &rect);
3685                 GetWindowRect(hwndStatus, &rs);
3686                 if (pt.y >= rect.top && pt.y < rect.bottom - (rs.bottom - rs.top))
3687                 {
3688                     SetCursor(LoadCursorW(NULL, IDC_SIZEWE));
3689                     return TRUE;
3690                 }
3691             }
3692             else
3693             /* Set the cursor for the horizontal splitter, if the Event details pane is displayed */
3694             if (hwndEventDetails && Settings.bShowDetailsPane &&
3695                 (pt.y >= nHSplitPos - SPLIT_WIDTH/2 && pt.y < nHSplitPos + SPLIT_WIDTH/2 + 1))
3696             {
3697                 // RECT rs;
3698                 GetClientRect(hWnd, &rect);
3699                 // GetWindowRect(hwndStatus, &rs);
3700                 if (pt.x >= nVSplitPos + SPLIT_WIDTH/2 + 1 /* rect.left + (rs.bottom - rs.top) */ &&
3701                     pt.x < rect.right)
3702                 {
3703                     SetCursor(LoadCursorW(NULL, IDC_SIZENS));
3704                     return TRUE;
3705                 }
3706             }
3707             goto Default;
3708         }
3709 
3710         case WM_LBUTTONDOWN:
3711         {
3712             INT x = GET_X_LPARAM(lParam);
3713             INT y = GET_Y_LPARAM(lParam);
3714 
3715             /* Reset the splitter state */
3716             bSplit = 0;
3717 
3718             /* Capture the cursor for the vertical splitter */
3719             if (x >= nVSplitPos - SPLIT_WIDTH/2 && x < nVSplitPos + SPLIT_WIDTH/2 + 1)
3720             {
3721                 bSplit = 1;
3722                 SetCapture(hWnd);
3723             }
3724             else
3725             /* Capture the cursor for the horizontal splitter, if the Event details pane is displayed */
3726             if (hwndEventDetails && Settings.bShowDetailsPane &&
3727                 (y >= nHSplitPos - SPLIT_WIDTH/2 && y < nHSplitPos + SPLIT_WIDTH/2 + 1))
3728             {
3729                 bSplit = 2;
3730                 SetCapture(hWnd);
3731             }
3732             break;
3733         }
3734 
3735         case WM_LBUTTONUP:
3736         case WM_RBUTTONDOWN:
3737         {
3738             if (GetCapture() != hWnd)
3739                 break;
3740 
3741             /* Adjust the correct splitter position */
3742             if (bSplit == 1)
3743                 nVSplitPos = GET_X_LPARAM(lParam);
3744             else if (bSplit == 2)
3745                 nHSplitPos = GET_Y_LPARAM(lParam);
3746 
3747             /* If we are splitting, resize the windows */
3748             if (bSplit != 0)
3749             {
3750                 GetClientRect(hWnd, &rect);
3751                 ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
3752             }
3753 
3754             /* Reset the splitter state */
3755             bSplit = 0;
3756 
3757             ReleaseCapture();
3758             break;
3759         }
3760 
3761         case WM_MOUSEMOVE:
3762         {
3763             if (GetCapture() != hWnd)
3764                 break;
3765 
3766             /* Move the correct splitter */
3767             if (bSplit == 1)
3768             {
3769                 INT x = GET_X_LPARAM(lParam);
3770 
3771                 GetClientRect(hWnd, &rect);
3772 
3773                 x = min(max(x, SPLIT_WIDTH/2), rect.right - rect.left - SPLIT_WIDTH/2);
3774                 if (nVSplitPos != x)
3775                 {
3776                     nVSplitPos = x;
3777                     ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
3778                 }
3779             }
3780             else if (bSplit == 2)
3781             {
3782                 RECT rs;
3783                 INT y = GET_Y_LPARAM(lParam);
3784 
3785                 GetClientRect(hWnd, &rect);
3786                 GetWindowRect(hwndStatus, &rs);
3787 
3788                 y = min(max(y, SPLIT_WIDTH/2), rect.bottom - rect.top - SPLIT_WIDTH/2 - (rs.bottom - rs.top));
3789                 if (nHSplitPos != y)
3790                 {
3791                     nHSplitPos = y;
3792                     ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
3793                 }
3794             }
3795             break;
3796         }
3797 
3798         case WM_SIZE:
3799         {
3800             if (wParam != SIZE_MINIMIZED)
3801             {
3802                 SendMessageW(hwndStatus, WM_SIZE, 0, 0);
3803                 ResizeWnd(LOWORD(lParam), HIWORD(lParam));
3804                 break;
3805             }
3806             /* Fall through the default case */
3807         }
3808 
3809         default: Default:
3810             return DefWindowProcW(hWnd, uMsg, wParam, lParam);
3811     }
3812 
3813     return 0;
3814 }
3815 
3816 
3817 static
3818 VOID
3819 InitPropertiesDlg(HWND hDlg, PEVENTLOG EventLog)
3820 {
3821     LPWSTR lpLogName = EventLog->LogName;
3822 
3823     DWORD Result, dwType;
3824     DWORD dwMaxSize = 0, dwRetention = 0;
3825     BOOL Success;
3826     WIN32_FIND_DATAW FileInfo; // WIN32_FILE_ATTRIBUTE_DATA
3827     ULARGE_INTEGER FileSize;
3828     WCHAR wszBuf[MAX_PATH];
3829     WCHAR szTemp[MAX_LOADSTRING];
3830     LPWSTR FileName;
3831 
3832     HKEY hLogKey;
3833     WCHAR *KeyPath;
3834     DWORD cbData;
3835     SIZE_T cbKeyPath;
3836 
3837     if (EventLog->Permanent)
3838     {
3839 
3840     cbKeyPath = (wcslen(EVENTLOG_BASE_KEY) + wcslen(lpLogName) + 1) * sizeof(WCHAR);
3841     KeyPath = HeapAlloc(GetProcessHeap(), 0, cbKeyPath);
3842     if (!KeyPath)
3843     {
3844         goto Quit;
3845     }
3846 
3847     StringCbCopyW(KeyPath, cbKeyPath, EVENTLOG_BASE_KEY);
3848     StringCbCatW(KeyPath, cbKeyPath, lpLogName);
3849 
3850     if (RegOpenKeyExW(hkMachine, KeyPath, 0, KEY_QUERY_VALUE, &hLogKey) != ERROR_SUCCESS)
3851     {
3852         HeapFree(GetProcessHeap(), 0, KeyPath);
3853         goto Quit;
3854     }
3855     HeapFree(GetProcessHeap(), 0, KeyPath);
3856 
3857 
3858     cbData = sizeof(dwMaxSize);
3859     Result = RegQueryValueExW(hLogKey,
3860                               L"MaxSize",
3861                               NULL,
3862                               &dwType,
3863                               (LPBYTE)&dwMaxSize,
3864                               &cbData);
3865     if ((Result != ERROR_SUCCESS) || (dwType != REG_DWORD))
3866     {
3867         // dwMaxSize = 512 * 1024; /* 512 kBytes */
3868         dwMaxSize = 0;
3869     }
3870     /* Convert in KB */
3871     dwMaxSize /= 1024;
3872 
3873     cbData = sizeof(dwRetention);
3874     Result = RegQueryValueExW(hLogKey,
3875                               L"Retention",
3876                               NULL,
3877                               &dwType,
3878                               (LPBYTE)&dwRetention,
3879                               &cbData);
3880     if ((Result != ERROR_SUCCESS) || (dwType != REG_DWORD))
3881     {
3882         /* On Windows 2003 it is 604800 (secs) == 7 days */
3883         dwRetention = 0;
3884     }
3885     /* Convert in days, rounded up */ // ROUND_UP
3886     // dwRetention = ROUND_UP(dwRetention, 24*3600) / (24*3600);
3887     dwRetention = (dwRetention + 24*3600 - 1) / (24*3600);
3888 
3889 
3890     RegCloseKey(hLogKey);
3891 
3892     }
3893 
3894 
3895 Quit:
3896 
3897     SetDlgItemTextW(hDlg, IDC_DISPLAYNAME, lpLogName); // FIXME!
3898     SetDlgItemTextW(hDlg, IDC_LOGNAME, lpLogName);
3899 
3900     FileName = EventLog->FileName;
3901     if (FileName && *FileName)
3902     {
3903         /* Expand the file name. If the log file is on a remote computer, retrieve the network share form of the file name. */
3904         GetExpandedFilePathName(EventLog->ComputerName, FileName, wszBuf, ARRAYSIZE(wszBuf));
3905         FileName = wszBuf;
3906     }
3907     else
3908     {
3909         FileName = L"";
3910     }
3911     SetDlgItemTextW(hDlg, IDC_LOGFILE, FileName);
3912 
3913     if (FileName && *FileName)
3914     {
3915         /*
3916          * The general problem here (and in the shell as well) is that
3917          * GetFileAttributesEx fails for files that are opened without
3918          * shared access. To retrieve file information for those we need
3919          * to use something else: FindFirstFile, on the full file name.
3920          */
3921         Success = GetFileAttributesExW(FileName,
3922                                        GetFileExInfoStandard,
3923                                        (LPWIN32_FILE_ATTRIBUTE_DATA)&FileInfo);
3924         if (!Success)
3925         {
3926             HANDLE hFind = FindFirstFileW(FileName, &FileInfo);
3927             Success = (hFind != INVALID_HANDLE_VALUE);
3928             if (Success)
3929                 FindClose(hFind);
3930         }
3931     }
3932     else
3933     {
3934         Success = FALSE;
3935     }
3936 
3937     /* Starting there, FileName becomes invalid because we are reusing wszBuf */
3938 
3939     if (Success)
3940     {
3941         FileSize.u.LowPart = FileInfo.nFileSizeLow;
3942         FileSize.u.HighPart = FileInfo.nFileSizeHigh;
3943         if (FormatFileSizeWithBytes(&FileSize, wszBuf, ARRAYSIZE(wszBuf)))
3944             SetDlgItemTextW(hDlg, IDC_SIZE_LABEL, wszBuf);
3945 
3946         LoadStringW(hInst, IDS_NOT_AVAILABLE, szTemp, ARRAYSIZE(szTemp));
3947 
3948         if (GetFileTimeString(&FileInfo.ftCreationTime, wszBuf, ARRAYSIZE(wszBuf)))
3949             SetDlgItemTextW(hDlg, IDC_CREATED_LABEL, wszBuf);
3950         else
3951             SetDlgItemTextW(hDlg, IDC_CREATED_LABEL, szTemp);
3952 
3953         if (GetFileTimeString(&FileInfo.ftLastWriteTime, wszBuf, ARRAYSIZE(wszBuf)))
3954             SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, wszBuf);
3955         else
3956             SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, szTemp);
3957 
3958         if (GetFileTimeString(&FileInfo.ftLastAccessTime, wszBuf, ARRAYSIZE(wszBuf)))
3959             SetDlgItemTextW(hDlg, IDC_ACCESSED_LABEL, wszBuf);
3960         else
3961             SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, szTemp);
3962     }
3963     else
3964     {
3965         LoadStringW(hInst, IDS_NOT_AVAILABLE, szTemp, ARRAYSIZE(szTemp));
3966 
3967         SetDlgItemTextW(hDlg, IDC_SIZE_LABEL, szTemp);
3968         SetDlgItemTextW(hDlg, IDC_CREATED_LABEL, szTemp);
3969         SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, szTemp);
3970         SetDlgItemTextW(hDlg, IDC_ACCESSED_LABEL, szTemp);
3971     }
3972 
3973     if (EventLog->Permanent)
3974     {
3975         SendDlgItemMessageW(hDlg, IDC_UPDOWN_MAXLOGSIZE, UDM_SETRANGE32, (WPARAM)1, (LPARAM)0x3FFFC0);
3976         SendDlgItemMessageW(hDlg, IDC_UPDOWN_EVENTS_AGE, UDM_SETRANGE, 0, (LPARAM)MAKELONG(365, 1));
3977 
3978         SetDlgItemInt(hDlg, IDC_EDIT_MAXLOGSIZE, dwMaxSize, FALSE);
3979         SetDlgItemInt(hDlg, IDC_EDIT_EVENTS_AGE, dwRetention, FALSE);
3980 
3981         if (dwRetention == 0)
3982         {
3983             CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_OVERWRITE_AS_NEEDED);
3984             EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, FALSE);
3985             EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, FALSE);
3986         }
3987         else if (dwRetention == INFINITE)
3988         {
3989             CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_NO_OVERWRITE);
3990             EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, FALSE);
3991             EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, FALSE);
3992         }
3993         else
3994         {
3995             CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_OVERWRITE_OLDER_THAN);
3996             EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, TRUE);
3997             EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, TRUE);
3998         }
3999     }
4000     else
4001     {
4002         // TODO: Hide the unused controls! Or, just use another type of property sheet!
4003     }
4004 }
4005 
4006 /* Message handler for EventLog Properties dialog */
4007 INT_PTR CALLBACK
4008 EventLogPropProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
4009 {
4010     PEVENTLOG EventLog;
4011 
4012     EventLog = (PEVENTLOG)GetWindowLongPtrW(hDlg, DWLP_USER);
4013 
4014     switch (uMsg)
4015     {
4016         case WM_INITDIALOG:
4017         {
4018             EventLog = (PEVENTLOG)((LPPROPSHEETPAGE)lParam)->lParam;
4019             SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)EventLog);
4020 
4021             InitPropertiesDlg(hDlg, EventLog);
4022 
4023             PropSheet_UnChanged(GetParent(hDlg), hDlg);
4024             return (INT_PTR)TRUE;
4025         }
4026 
4027         case WM_DESTROY:
4028             return (INT_PTR)TRUE;
4029 
4030         case WM_COMMAND:
4031             switch (LOWORD(wParam))
4032             {
4033                 case IDOK:
4034                 case IDCANCEL:
4035                     EndDialog(hDlg, LOWORD(wParam));
4036                     return (INT_PTR)TRUE;
4037 
4038                 case IDC_OVERWRITE_AS_NEEDED:
4039                 {
4040                     CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_OVERWRITE_AS_NEEDED);
4041                     EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, FALSE);
4042                     EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, FALSE);
4043                     break;
4044                 }
4045 
4046                 case IDC_OVERWRITE_OLDER_THAN:
4047                 {
4048                     CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_OVERWRITE_OLDER_THAN);
4049                     EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, TRUE);
4050                     EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, TRUE);
4051                     break;
4052                 }
4053 
4054                 case IDC_NO_OVERWRITE:
4055                 {
4056                     CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_NO_OVERWRITE);
4057                     EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, FALSE);
4058                     EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, FALSE);
4059                     break;
4060                 }
4061 
4062                 case IDHELP:
4063                     MessageBoxW(hDlg,
4064                                 L"Help not implemented yet!",
4065                                 L"Event Log",
4066                                 MB_OK | MB_ICONINFORMATION);
4067                     return (INT_PTR)TRUE;
4068 
4069                 default:
4070                     break;
4071             }
4072             break;
4073     }
4074 
4075     return (INT_PTR)FALSE;
4076 }
4077 
4078 INT_PTR
4079 EventLogProperties(HINSTANCE hInstance, HWND hWndParent, PEVENTLOGFILTER EventLogFilter)
4080 {
4081     INT_PTR ret = 0;
4082     PROPSHEETHEADERW psh;
4083     PROPSHEETPAGEW   psp[1]; // 2
4084 
4085     /*
4086      * Bail out if there is no available filter, or if the filter
4087      * contains more than one log.
4088      */
4089     if (!EventLogFilter)
4090         return 0;
4091 
4092     EventLogFilter_AddRef(EventLogFilter);
4093 
4094     if (EventLogFilter->NumOfEventLogs > 1 ||
4095         EventLogFilter->EventLogs[0] == NULL)
4096     {
4097         goto Quit;
4098     }
4099 
4100     /* Header */
4101     psh.dwSize      = sizeof(psh);
4102     psh.dwFlags     = PSH_PROPSHEETPAGE /*| PSH_USEICONID */ | PSH_PROPTITLE | PSH_HASHELP /*| PSH_NOCONTEXTHELP */ /*| PSH_USECALLBACK */;
4103     psh.hInstance   = hInstance;
4104     psh.hwndParent  = hWndParent;
4105     // psh.pszIcon     = MAKEINTRESOURCEW(IDI_APPICON); // Disabled because it only sets the small icon; the big icon is a stretched version of the small one.
4106     psh.pszCaption  = EventLogFilter->EventLogs[0]->LogName;
4107     psh.nStartPage  = 0;
4108     psh.ppsp        = psp;
4109     psh.nPages      = ARRAYSIZE(psp);
4110     // psh.pfnCallback = PropSheetCallback;
4111 
4112     /* Log properties page */
4113     psp[0].dwSize      = sizeof(psp[0]);
4114     psp[0].dwFlags     = PSP_HASHELP;
4115     psp[0].hInstance   = hInstance;
4116     psp[0].pszTemplate = MAKEINTRESOURCEW(IDD_LOGPROPERTIES_GENERAL);
4117     psp[0].pfnDlgProc  = EventLogPropProc;
4118     psp[0].lParam      = (LPARAM)EventLogFilter->EventLogs[0];
4119 
4120 #if 0
4121     /* TODO: Log sources page */
4122     psp[1].dwSize      = sizeof(psp[1]);
4123     psp[1].dwFlags     = PSP_HASHELP;
4124     psp[1].hInstance   = hInstance;
4125     psp[1].pszTemplate = MAKEINTRESOURCEW(IDD_GENERAL_PAGE);
4126     psp[1].pfnDlgProc  = GeneralPageWndProc;
4127     psp[0].lParam      = (LPARAM)EventLogFilter->EventLogs[0];
4128 #endif
4129 
4130     /* Create the property sheet */
4131     ret = PropertySheetW(&psh);
4132 
4133 Quit:
4134     EventLogFilter_Release(EventLogFilter);
4135     return ret;
4136 }
4137 
4138 /* Message handler for Event Details dialog */
4139 static HWND hWndDetailsCtrl = NULL; // May go into the DWLP_USER
4140 static HWND hWndGrip = NULL;
4141 static INT cxMin, cyMin;    // In window coordinates
4142 static INT cxOld, cyOld;    // In client coordinates
4143 
4144 INT_PTR CALLBACK
4145 EventDetails(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
4146 {
4147     switch (uMsg)
4148     {
4149         case WM_INITDIALOG:
4150         {
4151             LONG_PTR dwStyle;
4152             INT sbVXSize, sbHYSize;
4153             RECT rcWnd, rect;
4154 
4155             hWndDetailsCtrl = CreateEventDetailsCtrl(hInst, hDlg, lParam);
4156             if (!hWndDetailsCtrl)
4157             {
4158                 EndDialog(hDlg, 0);
4159                 return (INT_PTR)TRUE;
4160             }
4161 
4162             /* Create a size grip if the dialog has a sizing border */
4163             GetClientRect(hDlg, &rcWnd);
4164             dwStyle  = GetWindowLongPtrW(hDlg, GWL_STYLE);
4165             sbVXSize = GetSystemMetrics(SM_CXVSCROLL);
4166             sbHYSize = GetSystemMetrics(SM_CYHSCROLL);
4167             if (dwStyle & WS_THICKFRAME /* == WS_SIZEBOX */)
4168             {
4169                 hWndGrip = CreateWindowW(WC_SCROLLBARW,
4170                                          NULL,
4171                                          WS_CHILD | WS_VISIBLE | /**/ WS_CLIPSIBLINGS | /**/ SBS_SIZEGRIP | SBS_SIZEBOXBOTTOMRIGHTALIGN,
4172                                          rcWnd.right - sbVXSize,
4173                                          rcWnd.bottom - sbHYSize,
4174                                          sbVXSize, sbHYSize,
4175                                          hDlg,
4176                                          NULL,
4177                                          hInst,
4178                                          NULL);
4179             }
4180 
4181             // SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)hWndDetailsCtrl);
4182 
4183             /*
4184              * Compute the minimum window size (in window coordinates) by
4185              * adding the widths/heights of the "Help" and "Close" buttons,
4186              * together with the margins, and add some minimal spacing
4187              * between the buttons.
4188              */
4189             GetWindowRect(hDlg, &rcWnd);
4190             cxMin = cyMin = 0;
4191 
4192             GetWindowRect(GetDlgItem(hDlg, IDHELP), &rect);
4193             cxMin += (rect.right - rect.left) + (rect.left - rcWnd.left); // == (rect.right - rcWnd.left);
4194             cyMin += (rect.bottom - rect.top) + (rcWnd.bottom - rect.bottom); // == (rcWnd.bottom - rect.top);
4195 
4196             GetWindowRect(GetDlgItem(hDlg, IDOK), &rect);
4197             cxMin += (rect.right - rect.left) + (rcWnd.right - rect.right); // == (rcWnd.right - rect.left);
4198             cyMin += (rect.bottom - rect.top) + (rcWnd.bottom - rect.bottom); // == (rcWnd.bottom - rect.top);
4199 
4200             /*
4201              * Convert the window rect from window to client coordinates
4202              * in order to retrieve the sizes of the left and top margins,
4203              * and add some extra space.
4204              */
4205             MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rcWnd, sizeof(RECT)/sizeof(POINT));
4206 
4207             cxMin += -2*rcWnd.left;   // Minimal spacing between the buttons == 2 * left margin
4208             cyMin += -rcWnd.top + 12; // Add some space on top
4209 
4210             GetClientRect(hDlg, &rcWnd);
4211             cxOld = rcWnd.right - rcWnd.left;
4212             cyOld = rcWnd.bottom - rcWnd.top;
4213 
4214             /* Show event info on dialog control */
4215             SendMessageW(hWndDetailsCtrl, EVT_DISPLAY, 0, 0);
4216 
4217             // SetWindowPos(hWndDetailsCtrl, NULL,
4218                          // 0, 0,
4219                          // (rcWnd.right - rcWnd.left),
4220                          // (rcWnd.bottom - rcWnd.top),
4221                          // SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
4222 
4223             /*
4224              * Hide the placeholder static control and show the event details
4225              * control instead. Note that the placeholder is here so far just
4226              * to get the dimensions right in the dialog resource editor.
4227              * I plan to remove it and use a custom control with a suitable
4228              * window class for it, that would create the event details control
4229              * instead.
4230              */
4231             ShowWindow(GetDlgItem(hDlg, IDC_STATIC), SW_HIDE);
4232             ShowWindow(hWndDetailsCtrl, SW_SHOW);
4233             return (INT_PTR)TRUE;
4234         }
4235 
4236         case WM_DESTROY:
4237             if (IsWindow(hWndDetailsCtrl))
4238                 DestroyWindow(hWndDetailsCtrl);
4239             hWndDetailsCtrl = NULL;
4240             return (INT_PTR)TRUE;
4241 
4242         case WM_COMMAND:
4243             switch (LOWORD(wParam))
4244             {
4245                 case IDOK:
4246                 case IDCANCEL:
4247                     EndDialog(hDlg, LOWORD(wParam));
4248                     return (INT_PTR)TRUE;
4249 
4250                 case IDHELP:
4251                     MessageBoxW(hDlg,
4252                                 L"Help not implemented yet!",
4253                                 L"Event Log",
4254                                 MB_OK | MB_ICONINFORMATION);
4255                     return (INT_PTR)TRUE;
4256 
4257                 default:
4258                     break;
4259             }
4260             break;
4261 
4262         case WM_SETCURSOR:
4263             if (((HWND)wParam == hWndGrip) && (LOWORD(lParam) == HTCLIENT))
4264             {
4265                 SetCursor(LoadCursorW(NULL, IDC_SIZENWSE));
4266                 SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, TRUE);
4267                 return (INT_PTR)TRUE;
4268             }
4269             break;
4270 
4271         case WM_SIZING:
4272         {
4273             /* Forbid resizing the dialog smaller than its minimal size */
4274             PRECT dragRect = (PRECT)lParam;
4275 
4276             if ((wParam == WMSZ_LEFT) || (wParam == WMSZ_TOPLEFT) || (wParam == WMSZ_BOTTOMLEFT))
4277             {
4278                 if (dragRect->right - dragRect->left < cxMin)
4279                     dragRect->left = dragRect->right - cxMin;
4280             }
4281 
4282             if ((wParam == WMSZ_RIGHT) || (wParam == WMSZ_TOPRIGHT) || (wParam == WMSZ_BOTTOMRIGHT))
4283             {
4284                 if (dragRect->right - dragRect->left < cxMin)
4285                     dragRect->right = dragRect->left + cxMin;
4286             }
4287 
4288             if ((wParam == WMSZ_TOP) || (wParam == WMSZ_TOPLEFT) || (wParam == WMSZ_TOPRIGHT))
4289             {
4290                 if (dragRect->bottom - dragRect->top < cyMin)
4291                     dragRect->top = dragRect->bottom - cyMin;
4292             }
4293 
4294             if ((wParam == WMSZ_BOTTOM) || (wParam == WMSZ_BOTTOMLEFT) || (wParam == WMSZ_BOTTOMRIGHT))
4295             {
4296                 if (dragRect->bottom - dragRect->top < cyMin)
4297                     dragRect->bottom = dragRect->top + cyMin;
4298             }
4299 
4300             SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, TRUE);
4301             return (INT_PTR)TRUE;
4302         }
4303 
4304         case WM_SIZE:
4305         {
4306             INT cx = LOWORD(lParam);
4307             INT cy = HIWORD(lParam);
4308 
4309             HDWP hdwp;
4310             HWND hItemWnd;
4311             RECT rect;
4312 
4313             hdwp = BeginDeferWindowPos(4);
4314 
4315             /* Resize the event details control window */
4316 
4317             hItemWnd = hWndDetailsCtrl;
4318             GetWindowRect(hItemWnd, &rect);
4319             MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
4320 
4321             if (hdwp)
4322                 hdwp = DeferWindowPos(hdwp,
4323                                       hItemWnd,
4324                                       HWND_TOP,
4325                                       0, 0,
4326                                       (rect.right - rect.left) + (cx - cxOld),
4327                                       (rect.bottom - rect.top) + (cy - cyOld),
4328                                       SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
4329 
4330             /* Move the buttons */
4331 
4332             hItemWnd = GetDlgItem(hDlg, IDHELP);
4333             GetWindowRect(hItemWnd, &rect);
4334             MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
4335 
4336             if (hdwp)
4337                 hdwp = DeferWindowPos(hdwp,
4338                                       hItemWnd,
4339                                       HWND_TOP,
4340                                       rect.left,
4341                                       rect.top + (cy - cyOld),
4342                                       0, 0,
4343                                       SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
4344 
4345             hItemWnd = GetDlgItem(hDlg, IDOK);
4346             GetWindowRect(hItemWnd, &rect);
4347             MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
4348 
4349             if (hdwp)
4350                 hdwp = DeferWindowPos(hdwp,
4351                                       hItemWnd,
4352                                       HWND_TOP,
4353                                       rect.left + (cx - cxOld),
4354                                       rect.top  + (cy - cyOld),
4355                                       0, 0,
4356                                       SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
4357 
4358             /* Move the size grip */
4359             if (hWndGrip && hdwp)
4360             {
4361                 GetWindowRect(hWndGrip, &rect);
4362                 MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
4363 
4364                 hdwp = DeferWindowPos(hdwp,
4365                                       hWndGrip,
4366                                       HWND_TOP,
4367                                       rect.left + (cx - cxOld),
4368                                       rect.top  + (cy - cyOld),
4369                                       0, 0,
4370                                       SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
4371             }
4372 
4373             if (hdwp)
4374                 EndDeferWindowPos(hdwp);
4375 
4376             /* Hide the size grip if we are in maximized mode */
4377             if (hWndGrip)
4378                 ShowWindow(hWndGrip, (wParam == SIZE_MAXIMIZED) ? SW_HIDE : SW_SHOW);
4379 
4380             cxOld = cx;
4381             cyOld = cy;
4382 
4383             SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, 0);
4384             return (INT_PTR)TRUE;
4385         }
4386     }
4387 
4388     return (INT_PTR)FALSE;
4389 }
4390