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 = (DWORD)(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     UINT cchWritten, cchRemaining;
1177     LPWSTR pwszEnd;
1178     size_t cchStringRemaining;
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, &cchStringRemaining, 0);
1189     cchRemaining = (UINT)cchStringRemaining;
1190     cchWritten = LoadStringW(hInst, IDS_BYTES_FORMAT, pwszEnd, cchRemaining);
1191     cchRemaining -= cchWritten;
1192 
1193     return cchResultMax - cchRemaining;
1194 }
1195 
1196 LPWSTR
1197 FormatFileSizeWithBytes(const PULARGE_INTEGER lpQwSize, LPWSTR pwszResult, UINT cchResultMax)
1198 {
1199     UINT cchWritten, cchRemaining;
1200     LPWSTR pwszEnd;
1201     size_t cchCopyRemaining;
1202 
1203     /* Format bytes in KBs, MBs etc */
1204     if (StrFormatByteSizeW(lpQwSize->QuadPart, pwszResult, cchResultMax) == NULL)
1205         return NULL;
1206 
1207     /* If there is less bytes than 1KB, we have nothing to do */
1208     if (lpQwSize->QuadPart < 1024)
1209         return pwszResult;
1210 
1211     /* Concatenate " (" */
1212     cchWritten = (UINT)wcslen(pwszResult);
1213     pwszEnd = pwszResult + cchWritten;
1214     cchRemaining = cchResultMax - cchWritten;
1215     StringCchCopyExW(pwszEnd, cchRemaining, L" (", &pwszEnd, &cchCopyRemaining, 0);
1216     cchRemaining = (UINT)cchCopyRemaining;
1217 
1218     /* Write formated bytes count */
1219     cchWritten = FormatByteSize(lpQwSize->QuadPart, pwszEnd, cchRemaining);
1220     pwszEnd += cchWritten;
1221     cchRemaining -= cchWritten;
1222 
1223     /* Copy ")" to the buffer */
1224     StringCchCopyW(pwszEnd, cchRemaining, L")");
1225 
1226     return pwszResult;
1227 }
1228 
1229 /* Adapted from shell32!dialogs/filedefext.cpp:``CFileDefExt::GetFileTimeString'' */
1230 BOOL
1231 GetFileTimeString(LPFILETIME lpFileTime, LPWSTR pwszResult, UINT cchResult)
1232 {
1233     FILETIME ft;
1234     SYSTEMTIME st;
1235     int cchWritten;
1236     UINT cchRemaining = cchResult;
1237     size_t cchCopyRemaining;
1238     LPWSTR pwszEnd = pwszResult;
1239 
1240     if (!FileTimeToLocalFileTime(lpFileTime, &ft) || !FileTimeToSystemTime(&ft, &st))
1241         return FALSE;
1242 
1243     cchWritten = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, pwszEnd, cchRemaining);
1244     if (cchWritten)
1245         --cchWritten; // GetDateFormatW returns count with terminating zero
1246     // else
1247         // ERR("GetDateFormatW failed\n");
1248 
1249     cchRemaining -= cchWritten;
1250     pwszEnd += cchWritten;
1251 
1252     StringCchCopyExW(pwszEnd, cchRemaining, L", ", &pwszEnd, &cchCopyRemaining, 0);
1253     cchRemaining = (UINT)cchCopyRemaining;
1254 
1255     cchWritten = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, pwszEnd, cchRemaining);
1256     if (cchWritten)
1257         --cchWritten; // GetTimeFormatW returns count with terminating zero
1258     // else
1259         // ERR("GetTimeFormatW failed\n");
1260 
1261     return TRUE;
1262 }
1263 
1264 
1265 HTREEITEM
1266 TreeViewAddItem(IN HWND hTreeView,
1267                 IN HTREEITEM hParent,
1268                 IN LPWSTR lpText,
1269                 IN INT Image,
1270                 IN INT SelectedImage,
1271                 IN LPARAM lParam)
1272 {
1273     TV_INSERTSTRUCTW Insert;
1274 
1275     ZeroMemory(&Insert, sizeof(Insert));
1276 
1277     Insert.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
1278     Insert.hInsertAfter = TVI_LAST;
1279     Insert.hParent = hParent;
1280     Insert.item.pszText = lpText;
1281     Insert.item.iImage = Image;
1282     Insert.item.iSelectedImage = SelectedImage;
1283     Insert.item.lParam = lParam;
1284 
1285     Insert.item.mask |= TVIF_STATE;
1286     Insert.item.stateMask = TVIS_OVERLAYMASK;
1287     Insert.item.state = INDEXTOOVERLAYMASK(1);
1288 
1289     return TreeView_InsertItem(hTreeView, &Insert);
1290 }
1291 
1292 
1293 /* LOG HELPER FUNCTIONS *******************************************************/
1294 
1295 PEVENTLOG
1296 AllocEventLog(IN PCWSTR ComputerName OPTIONAL,
1297               IN PCWSTR LogName,
1298               IN BOOL Permanent)
1299 {
1300     PEVENTLOG EventLog;
1301     SIZE_T cchName;
1302 
1303     /* Allocate a new event log entry */
1304     EventLog = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*EventLog));
1305     if (!EventLog)
1306         return NULL;
1307 
1308     /* Allocate the computer name string (optional) and copy it */
1309     if (ComputerName)
1310     {
1311         cchName = wcslen(ComputerName) + 1;
1312         EventLog->ComputerName = HeapAlloc(GetProcessHeap(), 0, cchName * sizeof(WCHAR));
1313         if (EventLog->ComputerName)
1314             StringCchCopyW(EventLog->ComputerName, cchName, ComputerName);
1315     }
1316 
1317     /* Allocate the event log name string and copy it */
1318     cchName = wcslen(LogName) + 1;
1319     EventLog->LogName = HeapAlloc(GetProcessHeap(), 0, cchName * sizeof(WCHAR));
1320     if (!EventLog->LogName)
1321     {
1322         if (EventLog->ComputerName)
1323             HeapFree(GetProcessHeap(), 0, EventLog->ComputerName);
1324         HeapFree(GetProcessHeap(), 0, EventLog);
1325         return NULL;
1326     }
1327     StringCchCopyW(EventLog->LogName, cchName, LogName);
1328 
1329     EventLog->Permanent = Permanent;
1330 
1331     return EventLog;
1332 }
1333 
1334 VOID
1335 EventLog_Free(IN PEVENTLOG EventLog)
1336 {
1337     if (EventLog->LogName)
1338         HeapFree(GetProcessHeap(), 0, EventLog->LogName);
1339 
1340     if (EventLog->ComputerName)
1341         HeapFree(GetProcessHeap(), 0, EventLog->ComputerName);
1342 
1343     if (EventLog->FileName)
1344         HeapFree(GetProcessHeap(), 0, EventLog->FileName);
1345 
1346     HeapFree(GetProcessHeap(), 0, EventLog);
1347 }
1348 
1349 
1350 PWSTR
1351 AllocAndCopyMultiStr(IN PCWSTR MultiStr OPTIONAL)
1352 {
1353     PWSTR pStr;
1354     ULONG Length;
1355 
1356     if (!MultiStr)
1357         return NULL;
1358 
1359     pStr = (PWSTR)MultiStr;
1360     while (*pStr) pStr += (wcslen(pStr) + 1);
1361     Length = MultiStr - pStr + 2;
1362 
1363     pStr = HeapAlloc(GetProcessHeap(), 0, Length * sizeof(WCHAR));
1364     // NOTE: If we failed allocating the string, then fall back into no filter!
1365     if (pStr)
1366         RtlCopyMemory(pStr, MultiStr, Length * sizeof(WCHAR));
1367 
1368     return pStr;
1369 }
1370 
1371 PEVENTLOGFILTER
1372 AllocEventLogFilter(// IN PCWSTR FilterName,
1373                     IN BOOL Information,
1374                     IN BOOL Warning,
1375                     IN BOOL Error,
1376                     IN BOOL AuditSuccess,
1377                     IN BOOL AuditFailure,
1378                     IN PCWSTR Sources OPTIONAL,
1379                     IN PCWSTR Users OPTIONAL,
1380                     IN PCWSTR ComputerNames OPTIONAL,
1381                     IN ULONG NumOfEventLogs,
1382                     IN PEVENTLOG* EventLogs)
1383 {
1384     PEVENTLOGFILTER EventLogFilter;
1385 
1386     /* Allocate a new event log filter entry, big enough to accommodate the list of logs */
1387     EventLogFilter = HeapAlloc(GetProcessHeap(),
1388                                HEAP_ZERO_MEMORY,
1389                                FIELD_OFFSET(EVENTLOGFILTER, EventLogs[NumOfEventLogs]));
1390     if (!EventLogFilter)
1391         return NULL;
1392 
1393     EventLogFilter->Information  = Information;
1394     EventLogFilter->Warning      = Warning;
1395     EventLogFilter->Error        = Error;
1396     EventLogFilter->AuditSuccess = AuditSuccess;
1397     EventLogFilter->AuditFailure = AuditFailure;
1398 
1399     /* Allocate and copy the sources, users, and computers multi-strings */
1400     EventLogFilter->Sources = AllocAndCopyMultiStr(Sources);
1401     EventLogFilter->Users   = AllocAndCopyMultiStr(Users);
1402     EventLogFilter->ComputerNames = AllocAndCopyMultiStr(ComputerNames);
1403 
1404     /* Copy the list of event logs */
1405     EventLogFilter->NumOfEventLogs = NumOfEventLogs;
1406     RtlCopyMemory(EventLogFilter->EventLogs, EventLogs, NumOfEventLogs * sizeof(PEVENTLOG));
1407 
1408     /* Initialize the filter reference count */
1409     EventLogFilter->ReferenceCount = 1;
1410 
1411     return EventLogFilter;
1412 }
1413 
1414 VOID
1415 EventLogFilter_Free(IN PEVENTLOGFILTER EventLogFilter)
1416 {
1417     if (EventLogFilter->Sources)
1418         HeapFree(GetProcessHeap(), 0, EventLogFilter->Sources);
1419 
1420     if (EventLogFilter->Users)
1421         HeapFree(GetProcessHeap(), 0, EventLogFilter->Users);
1422 
1423     if (EventLogFilter->ComputerNames)
1424         HeapFree(GetProcessHeap(), 0, EventLogFilter->ComputerNames);
1425 
1426     HeapFree(GetProcessHeap(), 0, EventLogFilter);
1427 }
1428 
1429 LONG EventLogFilter_AddRef(IN PEVENTLOGFILTER EventLogFilter)
1430 {
1431     ASSERT(EventLogFilter);
1432     return InterlockedIncrement(&EventLogFilter->ReferenceCount);
1433 }
1434 
1435 LONG EventLogFilter_Release(IN PEVENTLOGFILTER EventLogFilter)
1436 {
1437     LONG RefCount;
1438 
1439     ASSERT(EventLogFilter);
1440 
1441     /* When the reference count reaches zero, delete the filter */
1442     RefCount = InterlockedDecrement(&EventLogFilter->ReferenceCount);
1443     if (RefCount <= 0)
1444     {
1445         /* Remove the filter from the list */
1446         /** RemoveEntryList(&EventLogFilter->ListEntry); **/
1447         EventLogFilter_Free(EventLogFilter);
1448     }
1449 
1450     return RefCount;
1451 }
1452 
1453 void
1454 TrimNulls(LPWSTR s)
1455 {
1456     WCHAR *c;
1457 
1458     if (s != NULL)
1459     {
1460         c = s + wcslen(s) - 1;
1461         while (c >= s && iswspace(*c))
1462             --c;
1463         *++c = L'\0';
1464     }
1465 }
1466 
1467 DWORD
1468 GetExpandedFilePathName(
1469     IN LPCWSTR ComputerName OPTIONAL,
1470     IN LPCWSTR lpFileName,
1471     OUT LPWSTR lpFullFileName OPTIONAL,
1472     IN DWORD nSize)
1473 {
1474     SIZE_T dwLength;
1475 
1476     /* Determine the needed size after expansion of any environment strings */
1477     dwLength = ExpandEnvironmentStringsW(lpFileName, NULL, 0);
1478     if (dwLength == 0)
1479     {
1480         /* We failed, bail out */
1481         return 0;
1482     }
1483 
1484     /* If the file path is on a remote computer, estimate its length */
1485     // FIXME: Use WNetGetUniversalName instead?
1486     if (ComputerName && *ComputerName)
1487     {
1488         /* Skip any leading backslashes */
1489         while (*ComputerName == L'\\')
1490             ++ComputerName;
1491 
1492         if (*ComputerName)
1493         {
1494             /* Count 2 backslashes plus the computer name and one backslash separator */
1495             dwLength += 2 + wcslen(ComputerName) + 1;
1496         }
1497     }
1498 
1499     /* Check whether we have enough space */
1500     if (dwLength > nSize)
1501     {
1502         /* No, return the needed size in characters (includes NULL-terminator) */
1503         return dwLength;
1504     }
1505 
1506 
1507     /* Now expand the file path */
1508     ASSERT(dwLength <= nSize);
1509 
1510     /* Expand any existing environment strings */
1511     if (ExpandEnvironmentStringsW(lpFileName, lpFullFileName, dwLength) == 0)
1512     {
1513         /* We failed, bail out */
1514         return 0;
1515     }
1516 
1517     /* If the file path is on a remote computer, retrieve the network share form of the file name */
1518     // FIXME: Use WNetGetUniversalName instead?
1519     if (ComputerName && *ComputerName)
1520     {
1521         /* Note that we previously skipped any potential leading backslashes */
1522 
1523         /* Replace ':' by '$' in the drive letter */
1524         if (*lpFullFileName && lpFullFileName[1] == L':')
1525             lpFullFileName[1] = L'$';
1526 
1527         /* Prepend the computer name */
1528         RtlMoveMemory(lpFullFileName + 2 + wcslen(ComputerName) + 1,
1529                       lpFullFileName, dwLength * sizeof(WCHAR) - (2 + wcslen(ComputerName) + 1) * sizeof(WCHAR));
1530         lpFullFileName[0] = L'\\';
1531         lpFullFileName[1] = L'\\';
1532         wcsncpy(lpFullFileName + 2, ComputerName, wcslen(ComputerName));
1533         lpFullFileName[2 + wcslen(ComputerName)] = L'\\';
1534     }
1535 
1536     /* Return the number of stored characters (includes NULL-terminator) */
1537     return dwLength;
1538 }
1539 
1540 BOOL
1541 GetEventMessageFileDLL(IN LPCWSTR lpLogName,
1542                        IN LPCWSTR SourceName,
1543                        IN LPCWSTR EntryName,
1544                        OUT PWCHAR lpModuleName) // TODO: Add IN DWORD BufLen
1545 {
1546     BOOL Success = FALSE;
1547     LONG Result;
1548     DWORD dwType, dwSize;
1549     WCHAR szModuleName[MAX_PATH];
1550     WCHAR szKeyName[MAX_PATH];
1551     HKEY hLogKey = NULL;
1552     HKEY hSourceKey = NULL;
1553 
1554     StringCbCopyW(szKeyName, sizeof(szKeyName), EVENTLOG_BASE_KEY);
1555     StringCbCatW(szKeyName, sizeof(szKeyName), lpLogName);
1556 
1557     Result = RegOpenKeyExW(hkMachine,
1558                            szKeyName,
1559                            0,
1560                            KEY_READ,
1561                            &hLogKey);
1562     if (Result != ERROR_SUCCESS)
1563         return FALSE;
1564 
1565     Result = RegOpenKeyExW(hLogKey,
1566                            SourceName,
1567                            0,
1568                            KEY_QUERY_VALUE,
1569                            &hSourceKey);
1570     if (Result != ERROR_SUCCESS)
1571     {
1572         RegCloseKey(hLogKey);
1573         return FALSE;
1574     }
1575 
1576     dwSize = sizeof(szModuleName);
1577     Result = RegQueryValueExW(hSourceKey,
1578                               EntryName,
1579                               NULL,
1580                               &dwType,
1581                               (LPBYTE)szModuleName,
1582                               &dwSize);
1583     if ((Result != ERROR_SUCCESS) || (dwType != REG_EXPAND_SZ && dwType != REG_SZ))
1584     {
1585         szModuleName[0] = UNICODE_NULL;
1586     }
1587     else
1588     {
1589         /* NULL-terminate the string and expand it */
1590         szModuleName[dwSize / sizeof(WCHAR) - 1] = UNICODE_NULL;
1591         GetExpandedFilePathName(lpComputerName, szModuleName, lpModuleName, ARRAYSIZE(szModuleName));
1592         Success = TRUE;
1593     }
1594 
1595     RegCloseKey(hSourceKey);
1596     RegCloseKey(hLogKey);
1597 
1598     return Success;
1599 }
1600 
1601 BOOL
1602 GetEventCategory(IN LPCWSTR KeyName,
1603                  IN LPCWSTR SourceName,
1604                  IN PEVENTLOGRECORD pevlr,
1605                  OUT PWCHAR CategoryName) // TODO: Add IN DWORD BufLen
1606 {
1607     BOOL Success = FALSE;
1608     WCHAR szMessageDLL[MAX_PATH];
1609     LPWSTR lpMsgBuf = NULL;
1610 
1611     if (!GetEventMessageFileDLL(KeyName, SourceName, EVENT_CATEGORY_MESSAGE_FILE, szMessageDLL))
1612         goto Quit;
1613 
1614     /* Retrieve the message string without appending extra newlines */
1615     lpMsgBuf =
1616     GetMessageStringFromDllList(szMessageDLL,
1617                                 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
1618                                 FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
1619                                 pevlr->EventCategory,
1620                                 EVENT_MESSAGE_FILE_BUFFER,
1621                                 NULL);
1622     if (lpMsgBuf)
1623     {
1624         /* Trim the string */
1625         TrimNulls(lpMsgBuf);
1626 
1627         /* Copy the category name */
1628         StringCchCopyW(CategoryName, MAX_PATH, lpMsgBuf);
1629 
1630         /* Free the buffer allocated by FormatMessage */
1631         LocalFree(lpMsgBuf);
1632 
1633         /* The ID was found and the message was formatted */
1634         Success = TRUE;
1635     }
1636 
1637 Quit:
1638     if (!Success)
1639     {
1640         if (pevlr->EventCategory != 0)
1641         {
1642             StringCchPrintfW(CategoryName, MAX_PATH, L"(%lu)", pevlr->EventCategory);
1643             Success = TRUE;
1644         }
1645     }
1646 
1647     return Success;
1648 }
1649 
1650 
1651 BOOL                                                                        // NOTE: Used by evtdetctl.c
1652 GetEventMessage(IN LPCWSTR KeyName,
1653                 IN LPCWSTR SourceName,
1654                 IN PEVENTLOGRECORD pevlr,
1655                 OUT PWCHAR EventText) // TODO: Add IN DWORD BufLen
1656 {
1657     BOOL Success = FALSE;
1658     DWORD i;
1659     size_t cch;
1660     WCHAR SourceModuleName[1024];
1661     WCHAR ParameterModuleName[1024];
1662     BOOL IsParamModNameCached = FALSE;
1663     LPWSTR lpMsgBuf = NULL;
1664     LPWSTR szStringArray, szMessage;
1665     LPWSTR *szArguments;
1666 
1667     /* Get the event string array */
1668     szStringArray = (LPWSTR)((LPBYTE)pevlr + pevlr->StringOffset);
1669 
1670     /* NOTE: GetEventMessageFileDLL can return a comma-separated list of DLLs */
1671     if (!GetEventMessageFileDLL(KeyName, SourceName, EVENT_MESSAGE_FILE, SourceModuleName))
1672         goto Quit;
1673 
1674     /* Allocate space for insertion strings */
1675     szArguments = HeapAlloc(GetProcessHeap(), 0, pevlr->NumStrings * sizeof(LPVOID));
1676     if (!szArguments)
1677         goto Quit;
1678 
1679     if (!IsParamModNameCached)
1680     {
1681         /* Now that the parameter file list is loaded, no need to reload it at the next run! */
1682         IsParamModNameCached = GetEventMessageFileDLL(KeyName, SourceName, EVENT_PARAMETER_MESSAGE_FILE, ParameterModuleName);
1683         // FIXME: If the string loading failed the first time, no need to retry it just after???
1684     }
1685 
1686     if (IsParamModNameCached)
1687     {
1688         /* Not yet support for reading messages from parameter message DLL */
1689     }
1690 
1691     szMessage = szStringArray;
1692     /*
1693      * HACK:
1694      * We do some hackish preformatting of the cached event strings...
1695      * That's because after we pass the string to FormatMessage
1696      * (via GetMessageStringFromDllList) with the FORMAT_MESSAGE_ARGUMENT_ARRAY
1697      * flag, instead of ignoring the insertion parameters and do the formatting
1698      * by ourselves. Therefore, the resulting string should have the parameter
1699      * string placeholders starting with a single '%' instead of a mix of one
1700      * and two '%'.
1701      */
1702     /* HACK part 1: Compute the full length of the string array */
1703     cch = 0;
1704     for (i = 0; i < pevlr->NumStrings; i++)
1705     {
1706         szMessage += wcslen(szMessage) + 1;
1707     }
1708     cch = szMessage - szStringArray;
1709 
1710     /* HACK part 2: Now do the HACK proper! */
1711     szMessage = szStringArray;
1712     for (i = 0; i < pevlr->NumStrings; i++)
1713     {
1714         lpMsgBuf = szMessage;
1715         while ((lpMsgBuf = wcsstr(lpMsgBuf, L"%%")))
1716         {
1717             if (iswdigit(lpMsgBuf[2]))
1718             {
1719                 RtlMoveMemory(lpMsgBuf, lpMsgBuf+1, ((szStringArray + cch) - lpMsgBuf - 1) * sizeof(WCHAR));
1720             }
1721         }
1722 
1723         szArguments[i] = szMessage;
1724         szMessage += wcslen(szMessage) + 1;
1725     }
1726 
1727     /* Retrieve the message string without appending extra newlines */
1728     lpMsgBuf =
1729     GetMessageStringFromDllList(SourceModuleName,
1730                                 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
1731                                 FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_MAX_WIDTH_MASK,
1732                                 pevlr->EventID,
1733                                 0,
1734                                 (va_list*)szArguments);
1735     if (lpMsgBuf)
1736     {
1737         /* Trim the string */
1738         TrimNulls(lpMsgBuf);
1739 
1740         szMessage = NULL;
1741         Success = (ApplyParameterStringsToMessage(ParameterModuleName,
1742                                                   TRUE,
1743                                                   lpMsgBuf,
1744                                                   &szMessage) == ERROR_SUCCESS);
1745         if (Success && szMessage)
1746         {
1747             /* Free the buffer allocated by FormatMessage */
1748             LocalFree(lpMsgBuf);
1749             lpMsgBuf = szMessage;
1750         }
1751 
1752         /* Copy the event text */
1753         StringCchCopyW(EventText, EVENT_MESSAGE_EVENTTEXT_BUFFER, lpMsgBuf);
1754 
1755         /* Free the buffer allocated by FormatMessage */
1756         LocalFree(lpMsgBuf);
1757     }
1758 
1759     HeapFree(GetProcessHeap(), 0, szArguments);
1760 
1761 Quit:
1762     if (!Success)
1763     {
1764         /* Get a read-only pointer to the "event-not-found" string */
1765         lpMsgBuf = HeapAlloc(GetProcessHeap(), 0, EVENT_MESSAGE_EVENTTEXT_BUFFER * sizeof(WCHAR));
1766         LoadStringW(hInst, IDS_EVENTSTRINGIDNOTFOUND, lpMsgBuf, EVENT_MESSAGE_EVENTTEXT_BUFFER);
1767         StringCchPrintfW(EventText, EVENT_MESSAGE_EVENTTEXT_BUFFER, lpMsgBuf, (pevlr->EventID & 0xFFFF), SourceName);
1768 
1769         /* Append the strings */
1770         szMessage = szStringArray;
1771         for (i = 0; i < pevlr->NumStrings; i++)
1772         {
1773             StringCchCatW(EventText, EVENT_MESSAGE_EVENTTEXT_BUFFER, szMessage);
1774             StringCchCatW(EventText, EVENT_MESSAGE_EVENTTEXT_BUFFER, L"\n");
1775             szMessage += wcslen(szMessage) + 1;
1776         }
1777     }
1778 
1779     return Success;
1780 }
1781 
1782 VOID
1783 GetEventType(IN WORD dwEventType,
1784              OUT PWCHAR eventTypeText) // TODO: Add IN DWORD BufLen
1785 {
1786     switch (dwEventType)
1787     {
1788         case EVENTLOG_ERROR_TYPE:
1789             LoadStringW(hInst, IDS_EVENTLOG_ERROR_TYPE, eventTypeText, MAX_LOADSTRING);
1790             break;
1791         case EVENTLOG_WARNING_TYPE:
1792             LoadStringW(hInst, IDS_EVENTLOG_WARNING_TYPE, eventTypeText, MAX_LOADSTRING);
1793             break;
1794         case EVENTLOG_INFORMATION_TYPE:
1795             LoadStringW(hInst, IDS_EVENTLOG_INFORMATION_TYPE, eventTypeText, MAX_LOADSTRING);
1796             break;
1797         case EVENTLOG_SUCCESS:
1798             LoadStringW(hInst, IDS_EVENTLOG_SUCCESS, eventTypeText, MAX_LOADSTRING);
1799             break;
1800         case EVENTLOG_AUDIT_SUCCESS:
1801             LoadStringW(hInst, IDS_EVENTLOG_AUDIT_SUCCESS, eventTypeText, MAX_LOADSTRING);
1802             break;
1803         case EVENTLOG_AUDIT_FAILURE:
1804             LoadStringW(hInst, IDS_EVENTLOG_AUDIT_FAILURE, eventTypeText, MAX_LOADSTRING);
1805             break;
1806         default:
1807             LoadStringW(hInst, IDS_EVENTLOG_UNKNOWN_TYPE, eventTypeText, MAX_LOADSTRING);
1808             break;
1809     }
1810 }
1811 
1812 BOOL
1813 GetEventUserName(IN PEVENTLOGRECORD pelr,
1814                  IN OUT PSID *pLastSid,
1815                  OUT PWCHAR pszUser) // TODO: Add IN DWORD BufLen
1816 {
1817     PSID pCurrentSid;
1818     PWSTR StringSid;
1819     WCHAR szName[1024];
1820     WCHAR szDomain[1024];
1821     SID_NAME_USE peUse;
1822     DWORD cchName = ARRAYSIZE(szName);
1823     DWORD cchDomain = ARRAYSIZE(szDomain);
1824     BOOL Success = FALSE;
1825 
1826     /* Point to the SID */
1827     pCurrentSid = (PSID)((LPBYTE)pelr + pelr->UserSidOffset);
1828 
1829     if (!IsValidSid(pCurrentSid))
1830     {
1831         *pLastSid = NULL;
1832         return FALSE;
1833     }
1834     else if (*pLastSid && EqualSid(*pLastSid, pCurrentSid))
1835     {
1836         return TRUE;
1837     }
1838 
1839     /* User SID */
1840     if (pelr->UserSidLength > 0)
1841     {
1842         /*
1843          * Try to retrieve the user account name and domain name corresponding
1844          * to the SID. If it cannot be retrieved, try to convert the SID to a
1845          * string-form. It should not be bigger than the user-provided buffer
1846          * 'pszUser', otherwise we return an error.
1847          */
1848         if (LookupAccountSidW(lpComputerName,
1849                               pCurrentSid,
1850                               szName,
1851                               &cchName,
1852                               szDomain,
1853                               &cchDomain,
1854                               &peUse))
1855         {
1856             StringCchCopyW(pszUser, MAX_PATH, szName);
1857             Success = TRUE;
1858         }
1859         else if (ConvertSidToStringSidW(pCurrentSid, &StringSid))
1860         {
1861             /* Copy the string only if the user-provided buffer is big enough */
1862             if (wcslen(StringSid) + 1 <= MAX_PATH) // + 1 for NULL-terminator
1863             {
1864                 StringCchCopyW(pszUser, MAX_PATH, StringSid);
1865                 Success = TRUE;
1866             }
1867             else
1868             {
1869                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1870                 Success = FALSE;
1871             }
1872 
1873             /* Free the allocated buffer */
1874             LocalFree(StringSid);
1875         }
1876     }
1877 
1878     *pLastSid = Success ? pCurrentSid : NULL;
1879 
1880     return Success;
1881 }
1882 
1883 
1884 static VOID FreeRecords(VOID)
1885 {
1886     DWORD iIndex;
1887 
1888     if (!g_RecordPtrs)
1889         return;
1890 
1891     for (iIndex = 0; iIndex < g_TotalRecords; iIndex++)
1892     {
1893         if (g_RecordPtrs[iIndex])
1894             HeapFree(GetProcessHeap(), 0, g_RecordPtrs[iIndex]);
1895     }
1896     HeapFree(GetProcessHeap(), 0, g_RecordPtrs);
1897     g_RecordPtrs = NULL;
1898     g_TotalRecords = 0;
1899 }
1900 
1901 BOOL
1902 FilterByType(IN PEVENTLOGFILTER EventLogFilter,
1903              IN PEVENTLOGRECORD pevlr)
1904 {
1905     if ((pevlr->EventType == EVENTLOG_SUCCESS          && !EventLogFilter->Information ) ||
1906         (pevlr->EventType == EVENTLOG_INFORMATION_TYPE && !EventLogFilter->Information ) ||
1907         (pevlr->EventType == EVENTLOG_WARNING_TYPE     && !EventLogFilter->Warning     ) ||
1908         (pevlr->EventType == EVENTLOG_ERROR_TYPE       && !EventLogFilter->Error       ) ||
1909         (pevlr->EventType == EVENTLOG_AUDIT_SUCCESS    && !EventLogFilter->AuditSuccess) ||
1910         (pevlr->EventType == EVENTLOG_AUDIT_FAILURE    && !EventLogFilter->AuditFailure))
1911     {
1912         return FALSE;
1913     }
1914     return TRUE;
1915 }
1916 
1917 BOOL
1918 FilterByString(IN PCWSTR FilterString, // This is a multi-string
1919                IN PWSTR  String)
1920 {
1921     PCWSTR pStr;
1922 
1923     /* The filter string is NULL so it does not filter anything */
1924     if (!FilterString)
1925         return TRUE;
1926 
1927     /*
1928      * If the filter string filters for an empty string AND the source string
1929      * is an empty string, we have a match (particular case of the last one).
1930      */
1931     if (!*FilterString && !*String)
1932         return TRUE;
1933 
1934     // if (*FilterString || *String)
1935 
1936     /*
1937      * If the filter string is empty BUT the source string is not empty,
1938      * OR vice-versa, we cannot have a match.
1939      */
1940     if ( (!*FilterString && *String) || (*FilterString && !*String) )
1941         return FALSE;
1942 
1943     /*
1944      * If the filter string filters for at least a non-empty string,
1945      * browse it and search for a string that matches the source string.
1946      */
1947     // else if (*FilterString && *String)
1948     {
1949         pStr = FilterString;
1950         while (*pStr)
1951         {
1952             if (wcsicmp(pStr, String) == 0)
1953             {
1954                 /* We have a match, break the loop */
1955                 break;
1956             }
1957 
1958             pStr += (wcslen(pStr) + 1);
1959         }
1960         if (!*pStr) // && *String
1961         {
1962             /* We do not have a match */
1963             return FALSE;
1964         }
1965     }
1966 
1967     /* We have a match */
1968     return TRUE;
1969 }
1970 
1971 /*
1972  * The events enumerator thread.
1973  */
1974 static DWORD WINAPI
1975 EnumEventsThread(IN LPVOID lpParameter)
1976 {
1977     PEVENTLOGFILTER EventLogFilter = (PEVENTLOGFILTER)lpParameter;
1978     PEVENTLOG EventLog;
1979 
1980     ULONG LogIndex;
1981     HANDLE hEventLog;
1982     PEVENTLOGRECORD pEvlr = NULL;
1983     PBYTE pEvlrEnd;
1984     PBYTE pEvlrBuffer;
1985     DWORD dwWanted, dwRead, dwNeeded, dwStatus = ERROR_SUCCESS;
1986     DWORD dwTotalRecords = 0, dwCurrentRecord = 0;
1987     DWORD dwFlags, dwMaxLength;
1988     size_t cchRemaining;
1989     LPWSTR lpszSourceName;
1990     LPWSTR lpszComputerName;
1991     BOOL bResult = TRUE; /* Read succeeded */
1992     HANDLE hProcessHeap = GetProcessHeap();
1993     PSID pLastSid = NULL;
1994 
1995     UINT uStep = 0, uStepAt = 0, uPos = 0;
1996 
1997     WCHAR szWindowTitle[MAX_PATH];
1998     WCHAR szStatusText[MAX_PATH];
1999     WCHAR szLocalDate[MAX_PATH];
2000     WCHAR szLocalTime[MAX_PATH];
2001     WCHAR szEventID[MAX_PATH];
2002     WCHAR szEventTypeText[MAX_LOADSTRING];
2003     WCHAR szCategoryID[MAX_PATH];
2004     WCHAR szUsername[MAX_PATH];
2005     WCHAR szNoUsername[MAX_PATH];
2006     WCHAR szCategory[MAX_PATH];
2007     WCHAR szNoCategory[MAX_PATH];
2008     PWCHAR lpTitleTemplateEnd;
2009 
2010     SYSTEMTIME time;
2011     LVITEMW lviEventItem;
2012 
2013     /* Save the current event log filter globally */
2014     EventLogFilter_AddRef(EventLogFilter);
2015     ActiveFilter = EventLogFilter;
2016 
2017 
2018     /** HACK!! **/
2019     EventLog = EventLogFilter->EventLogs[0];
2020 
2021     // FIXME: Use something else instead of EventLog->LogName !!
2022 
2023     /*
2024      * Use a different formatting, whether the event log filter holds
2025      * only one log, or many logs (the latter case is WIP TODO!)
2026      */
2027     if (EventLogFilter->NumOfEventLogs <= 1)
2028     {
2029         StringCchPrintfExW(szWindowTitle,
2030                            ARRAYSIZE(szWindowTitle),
2031                            &lpTitleTemplateEnd,
2032                            &cchRemaining,
2033                            0,
2034                            szTitleTemplate, szTitle, EventLog->LogName); /* i = number of characters written */
2035         dwMaxLength = (DWORD)cchRemaining;
2036         if (!EventLog->ComputerName)
2037             GetComputerNameW(lpTitleTemplateEnd, &dwMaxLength);
2038         else
2039             StringCchCopyW(lpTitleTemplateEnd, dwMaxLength, EventLog->ComputerName);
2040 
2041         StringCbPrintfW(szStatusText,
2042                         sizeof(szStatusText),
2043                         szStatusBarTemplate,
2044                         EventLog->LogName,
2045                         0,
2046                         0);
2047     }
2048     else
2049     {
2050         // TODO: Use a different title & implement filtering for multi-log filters !!
2051         // (EventLogFilter->NumOfEventLogs > 1)
2052         MessageBoxW(hwndMainWindow,
2053                     L"Many-logs filtering is not implemented yet!!",
2054                     L"Event Log",
2055                     MB_OK | MB_ICONINFORMATION);
2056     }
2057 
2058     /* Set the window title */
2059     SetWindowTextW(hwndMainWindow, szWindowTitle);
2060 
2061     /* Update the status bar */
2062     StatusBar_SetText(hwndStatus, 0, szStatusText);
2063 
2064 
2065     /* Disable list view redraw */
2066     SendMessageW(hwndListView, WM_SETREDRAW, FALSE, 0);
2067 
2068     /* Clear the list view and free the cached records */
2069     ListView_DeleteAllItems(hwndListView);
2070     FreeRecords();
2071 
2072     SendMessageW(hwndListView, LVM_PROGRESS, 0, TRUE);
2073     ProgressBar_SetRange(hwndStatusProgress, 0);
2074     StatusBar_SetText(hwndStatus, 0, NULL);
2075     ShowWindow(hwndStatusProgress, SW_SHOW);
2076 
2077     /* Do a loop over the logs enumerated in the filter */
2078     // FIXME: For now we only support 1 event log per filter!
2079     LogIndex = 0;
2080     // for (LogIndex = 0; LogIndex < EventLogFilter->NumOfEventLogs; ++LogIndex)
2081     {
2082 
2083     EventLog = EventLogFilter->EventLogs[LogIndex];
2084 
2085     /* Open the event log */
2086     if (EventLog->Permanent)
2087         hEventLog = OpenEventLogW(EventLog->ComputerName, EventLog->LogName);
2088     else
2089         hEventLog = OpenBackupEventLogW(EventLog->ComputerName, EventLog->LogName); // FileName
2090 
2091     if (hEventLog == NULL)
2092     {
2093         ShowWin32Error(GetLastError());
2094         goto Cleanup;
2095     }
2096 
2097     // GetOldestEventLogRecord(hEventLog, &dwThisRecord);
2098 
2099     /* Get the total number of event log records */
2100     GetNumberOfEventLogRecords(hEventLog, &dwTotalRecords);
2101 
2102     if (dwTotalRecords > 0)
2103     {
2104         EnableMenuItem(hMainMenu, IDM_CLEAR_EVENTS, MF_BYCOMMAND | MF_ENABLED);
2105         EnableMenuItem(hMainMenu, IDM_SAVE_EVENTLOG, MF_BYCOMMAND | MF_ENABLED);
2106     }
2107     else
2108     {
2109         EnableMenuItem(hMainMenu, IDM_CLEAR_EVENTS, MF_BYCOMMAND | MF_GRAYED);
2110         EnableMenuItem(hMainMenu, IDM_SAVE_EVENTLOG, MF_BYCOMMAND | MF_GRAYED);
2111     }
2112 
2113     /* Set up the event records cache */
2114     g_RecordPtrs = HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, dwTotalRecords * sizeof(*g_RecordPtrs));
2115     if (!g_RecordPtrs)
2116     {
2117         // ShowWin32Error(GetLastError());
2118         goto Quit;
2119     }
2120     g_TotalRecords = dwTotalRecords;
2121 
2122     if (WaitForSingleObject(hStopEnumEvent, 0) == WAIT_OBJECT_0)
2123         goto Quit;
2124 
2125     LoadStringW(hInst, IDS_NOT_AVAILABLE, szNoUsername, ARRAYSIZE(szNoUsername));
2126     LoadStringW(hInst, IDS_NONE, szNoCategory, ARRAYSIZE(szNoCategory));
2127 
2128     ProgressBar_SetRange(hwndStatusProgress, MAKELPARAM(0, 100));
2129     uStepAt = (dwTotalRecords / 100) + 1;
2130 
2131     dwFlags = EVENTLOG_SEQUENTIAL_READ | (Settings.bNewestEventsFirst ? EVENTLOG_FORWARDS_READ : EVENTLOG_BACKWARDS_READ);
2132 
2133     /* 0x7ffff is the maximum buffer size ReadEventLog will accept */
2134     dwWanted = 0x7ffff;
2135     pEvlr = HeapAlloc(hProcessHeap, 0, dwWanted);
2136 
2137     if (!pEvlr)
2138         goto Quit;
2139 
2140     while (dwStatus == ERROR_SUCCESS)
2141     {
2142         bResult =  ReadEventLogW(hEventLog, dwFlags, 0, pEvlr, dwWanted, &dwRead, &dwNeeded);
2143         dwStatus = GetLastError();
2144 
2145         if (!bResult && dwStatus == ERROR_INSUFFICIENT_BUFFER)
2146         {
2147             pEvlr = HeapReAlloc(hProcessHeap, 0, pEvlr, dwNeeded);
2148             dwWanted = dwNeeded;
2149 
2150             if (!pEvlr)
2151                 break;
2152 
2153             bResult =  ReadEventLogW(hEventLog, dwFlags, 0, pEvlr, dwNeeded, &dwRead, &dwNeeded);
2154 
2155             if (!bResult)
2156                 break;
2157         }
2158         else if (!bResult)
2159         {
2160             /* Exit on other errors (ERROR_HANDLE_EOF) */
2161             break;
2162         }
2163 
2164         pEvlrBuffer = (LPBYTE)pEvlr;
2165         pEvlrEnd = pEvlrBuffer + dwRead;
2166 
2167         while (pEvlrBuffer < pEvlrEnd)
2168         {
2169             PEVENTLOGRECORD pEvlrTmp = (PEVENTLOGRECORD)pEvlrBuffer;
2170             PWSTR lpszUsername, lpszCategoryName;
2171             g_RecordPtrs[dwCurrentRecord] = NULL;
2172 
2173             // ProgressBar_StepIt(hwndStatusProgress);
2174             uStep++;
2175             if (uStep % uStepAt == 0)
2176             {
2177                 ++uPos;
2178                 ProgressBar_SetPos(hwndStatusProgress, uPos);
2179             }
2180 
2181            if (WaitForSingleObject(hStopEnumEvent, 0) == WAIT_OBJECT_0)
2182                 goto Quit;
2183 
2184             /* Filter by event type */
2185             if (!FilterByType(EventLogFilter, pEvlrTmp))
2186                 goto SkipEvent;
2187 
2188             /* Get the event source name and filter it */
2189             lpszSourceName = (LPWSTR)(pEvlrBuffer + sizeof(EVENTLOGRECORD));
2190             if (!FilterByString(EventLogFilter->Sources, lpszSourceName))
2191                 goto SkipEvent;
2192 
2193             /* Get the computer name and filter it */
2194             lpszComputerName = (LPWSTR)(pEvlrBuffer + sizeof(EVENTLOGRECORD) + (wcslen(lpszSourceName) + 1) * sizeof(WCHAR));
2195             if (!FilterByString(EventLogFilter->ComputerNames, lpszComputerName))
2196                 goto SkipEvent;
2197 
2198             /* Compute the event time */
2199             EventTimeToSystemTime(pEvlrTmp->TimeWritten, &time);
2200             GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time, NULL, szLocalDate, ARRAYSIZE(szLocalDate));
2201             GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &time, NULL, szLocalTime, ARRAYSIZE(szLocalTime));
2202 
2203             /* Get the username that generated the event, and filter it */
2204             lpszUsername = GetEventUserName(pEvlrTmp, &pLastSid, szUsername) ? szUsername : szNoUsername;
2205 
2206             if (!FilterByString(EventLogFilter->Users, lpszUsername))
2207                 goto SkipEvent;
2208 
2209             // TODO: Filter by event ID and category
2210             GetEventType(pEvlrTmp->EventType, szEventTypeText);
2211 
2212             lpszCategoryName = GetEventCategory(EventLog->LogName, lpszSourceName, pEvlrTmp, szCategory) ? szCategory : szNoCategory;
2213 
2214             StringCbPrintfW(szEventID, sizeof(szEventID), L"%u", (pEvlrTmp->EventID & 0xFFFF));
2215             StringCbPrintfW(szCategoryID, sizeof(szCategoryID), L"%u", pEvlrTmp->EventCategory);
2216 
2217             g_RecordPtrs[dwCurrentRecord] = HeapAlloc(hProcessHeap, 0, pEvlrTmp->Length);
2218             RtlCopyMemory(g_RecordPtrs[dwCurrentRecord], pEvlrTmp, pEvlrTmp->Length);
2219 
2220             lviEventItem.mask = LVIF_IMAGE | LVIF_TEXT | LVIF_PARAM;
2221             lviEventItem.iItem = 0;
2222             lviEventItem.iSubItem = 0;
2223             lviEventItem.lParam = (LPARAM)g_RecordPtrs[dwCurrentRecord];
2224             lviEventItem.pszText = szEventTypeText;
2225 
2226             switch (pEvlrTmp->EventType)
2227             {
2228                 case EVENTLOG_SUCCESS:
2229                 case EVENTLOG_INFORMATION_TYPE:
2230                     lviEventItem.iImage = 0;
2231                     break;
2232 
2233                 case EVENTLOG_WARNING_TYPE:
2234                     lviEventItem.iImage = 1;
2235                     break;
2236 
2237                 case EVENTLOG_ERROR_TYPE:
2238                     lviEventItem.iImage = 2;
2239                     break;
2240 
2241                 case EVENTLOG_AUDIT_SUCCESS:
2242                     lviEventItem.iImage = 3;
2243                     break;
2244 
2245                 case EVENTLOG_AUDIT_FAILURE:
2246                     lviEventItem.iImage = 4;
2247                     break;
2248             }
2249 
2250             lviEventItem.iItem = ListView_InsertItem(hwndListView, &lviEventItem);
2251 
2252             ListView_SetItemText(hwndListView, lviEventItem.iItem, 1, szLocalDate);
2253             ListView_SetItemText(hwndListView, lviEventItem.iItem, 2, szLocalTime);
2254             ListView_SetItemText(hwndListView, lviEventItem.iItem, 3, lpszSourceName);
2255             ListView_SetItemText(hwndListView, lviEventItem.iItem, 4, lpszCategoryName);
2256             ListView_SetItemText(hwndListView, lviEventItem.iItem, 5, szEventID);
2257             ListView_SetItemText(hwndListView, lviEventItem.iItem, 6, lpszUsername);
2258             ListView_SetItemText(hwndListView, lviEventItem.iItem, 7, lpszComputerName);
2259 
2260 SkipEvent:
2261             pEvlrBuffer += pEvlrTmp->Length;
2262             dwCurrentRecord++;
2263         }
2264     }
2265 
2266 Quit:
2267 
2268     if (pEvlr)
2269         HeapFree(hProcessHeap, 0, pEvlr);
2270 
2271     /* Close the event log */
2272     CloseEventLog(hEventLog);
2273 
2274     } // end-for (LogIndex)
2275 
2276     /* All events loaded */
2277 
2278 Cleanup:
2279 
2280     ShowWindow(hwndStatusProgress, SW_HIDE);
2281     SendMessageW(hwndListView, LVM_PROGRESS, 0, FALSE);
2282 
2283     // FIXME: Use something else instead of EventLog->LogName !!
2284 
2285     /*
2286      * Use a different formatting, whether the event log filter holds
2287      * only one log, or many logs (the latter case is WIP TODO!)
2288      */
2289     if (EventLogFilter->NumOfEventLogs <= 1)
2290     {
2291         StringCbPrintfW(szStatusText,
2292                         sizeof(szStatusText),
2293                         szStatusBarTemplate,
2294                         EventLog->LogName,
2295                         dwTotalRecords,
2296                         ListView_GetItemCount(hwndListView));
2297     }
2298     else
2299     {
2300         // TODO: Use a different title & implement filtering for multi-log filters !!
2301         // (EventLogFilter->NumOfEventLogs > 1)
2302     }
2303 
2304     /* Update the status bar */
2305     StatusBar_SetText(hwndStatus, 0, szStatusText);
2306 
2307     /* Resume list view redraw */
2308     SendMessageW(hwndListView, WM_SETREDRAW, TRUE, 0);
2309 
2310     EventLogFilter_Release(EventLogFilter);
2311 
2312     CloseHandle(hStopEnumEvent);
2313     InterlockedExchangePointer((PVOID*)&hStopEnumEvent, NULL);
2314 
2315     return 0;
2316 }
2317 
2318 /*
2319  * The purpose of this thread is to serialize the creation of the events
2320  * enumeration thread, since the Event Log Viewer currently only supports
2321  * one view, one event list, one enumeration.
2322  */
2323 static DWORD WINAPI
2324 StartStopEnumEventsThread(IN LPVOID lpParameter)
2325 {
2326     HANDLE WaitHandles[2];
2327     DWORD  WaitResult;
2328 
2329     WaitHandles[0] = hStartStopEnumEvent; // End-of-application event
2330     WaitHandles[1] = hStartEnumEvent;     // Command event
2331 
2332     while (TRUE)
2333     {
2334         WaitResult = WaitForMultipleObjects(ARRAYSIZE(WaitHandles),
2335                                             WaitHandles,
2336                                             FALSE, // WaitAny
2337                                             INFINITE);
2338         switch (WaitResult)
2339         {
2340             case WAIT_OBJECT_0 + 0:
2341             {
2342                 /* End-of-application event signaled, quit this thread */
2343 
2344                 /* Stop the previous enumeration */
2345                 if (hEnumEventsThread)
2346                 {
2347                     if (hStopEnumEvent)
2348                     {
2349                         SetEvent(hStopEnumEvent);
2350                         WaitForSingleObject(hEnumEventsThread, INFINITE);
2351                         // NOTE: The following is done by the enumeration thread just before terminating.
2352                         // hStopEnumEvent = NULL;
2353                     }
2354 
2355                     CloseHandle(hEnumEventsThread);
2356                     hEnumEventsThread = NULL;
2357                 }
2358 
2359                 /* Clear the list view and free the cached records */
2360                 ListView_DeleteAllItems(hwndListView);
2361                 FreeRecords();
2362 
2363                 /* Reset the active filter */
2364                 ActiveFilter = NULL;
2365 
2366                 return 0;
2367             }
2368 
2369             case WAIT_OBJECT_0 + 1:
2370             {
2371                 /* Restart a new enumeration if needed */
2372                 PEVENTLOGFILTER EventLogFilter;
2373 
2374                 /* Stop the previous enumeration */
2375                 if (hEnumEventsThread)
2376                 {
2377                     if (hStopEnumEvent)
2378                     {
2379                         SetEvent(hStopEnumEvent);
2380                         WaitForSingleObject(hEnumEventsThread, INFINITE);
2381                         // NOTE: The following is done by the enumeration thread just before terminating.
2382                         // hStopEnumEvent = NULL;
2383                     }
2384 
2385                     CloseHandle(hEnumEventsThread);
2386                     hEnumEventsThread = NULL;
2387                 }
2388 
2389                 /* Clear the list view and free the cached records */
2390                 ListView_DeleteAllItems(hwndListView);
2391                 FreeRecords();
2392 
2393                 /* Reset the active filter */
2394                 ActiveFilter = NULL;
2395 
2396                 EventLogFilter = InterlockedExchangePointer((PVOID*)&EnumFilter, NULL);
2397                 if (!EventLogFilter)
2398                     break;
2399 
2400                 // Manual-reset event
2401                 hStopEnumEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
2402                 if (!hStopEnumEvent)
2403                     break;
2404 
2405                 hEnumEventsThread = CreateThread(NULL,
2406                                                  0,
2407                                                  EnumEventsThread,
2408                                                  (LPVOID)EventLogFilter,
2409                                                  CREATE_SUSPENDED,
2410                                                  NULL);
2411                 if (!hEnumEventsThread)
2412                 {
2413                     CloseHandle(hStopEnumEvent);
2414                     hStopEnumEvent = NULL;
2415                     break;
2416                 }
2417                 // CloseHandle(hEnumEventsThread);
2418                 ResumeThread(hEnumEventsThread);
2419 
2420                 break;
2421             }
2422 
2423             default:
2424             {
2425                 /* Unknown command, must never go there! */
2426                 return GetLastError();
2427             }
2428         }
2429     }
2430 
2431     return 0;
2432 }
2433 
2434 VOID
2435 EnumEvents(IN PEVENTLOGFILTER EventLogFilter)
2436 {
2437     /* Signal the enumerator thread we want to enumerate events */
2438     InterlockedExchangePointer((PVOID*)&EnumFilter, EventLogFilter);
2439     SetEvent(hStartEnumEvent);
2440     return;
2441 }
2442 
2443 
2444 PEVENTLOGFILTER
2445 GetSelectedFilter(OUT HTREEITEM* phti OPTIONAL)
2446 {
2447     TVITEMEXW tvItemEx;
2448     HTREEITEM hti;
2449 
2450     if (phti)
2451         *phti = NULL;
2452 
2453     /* Get index of selected item */
2454     hti = TreeView_GetSelection(hwndTreeView);
2455     if (hti == NULL)
2456         return NULL; // No filter
2457 
2458     tvItemEx.mask = TVIF_PARAM;
2459     tvItemEx.hItem = hti;
2460 
2461     TreeView_GetItem(hwndTreeView, &tvItemEx);
2462 
2463     if (phti)
2464         *phti = tvItemEx.hItem;
2465 
2466     return (PEVENTLOGFILTER)tvItemEx.lParam;
2467 }
2468 
2469 
2470 VOID
2471 OpenUserEventLogFile(IN LPCWSTR lpszFileName)
2472 {
2473     WIN32_FIND_DATAW FindData;
2474     HANDLE hFind;
2475     PEVENTLOG EventLog;
2476     PEVENTLOGFILTER EventLogFilter;
2477     SIZE_T cchFileName;
2478     HTREEITEM hItem = NULL;
2479 
2480     /* Check whether the file actually exists */
2481     hFind = FindFirstFileW(lpszFileName, &FindData);
2482     if (hFind == INVALID_HANDLE_VALUE)
2483     {
2484         ShowWin32Error(GetLastError());
2485         return;
2486     }
2487     FindClose(hFind);
2488 
2489     /* Allocate a new event log entry */
2490     EventLog = AllocEventLog(NULL, lpszFileName, FALSE);
2491     if (EventLog == NULL)
2492     {
2493         ShowWin32Error(ERROR_NOT_ENOUGH_MEMORY);
2494         return;
2495     }
2496 
2497     /* Allocate a new event log filter entry for this event log */
2498     EventLogFilter = AllocEventLogFilter(// LogName,
2499                                          TRUE, TRUE, TRUE, TRUE, TRUE,
2500                                          NULL, NULL, NULL,
2501                                          1, &EventLog);
2502     if (EventLogFilter == NULL)
2503     {
2504         ShowWin32Error(ERROR_NOT_ENOUGH_MEMORY);
2505         EventLog_Free(EventLog);
2506         return;
2507     }
2508 
2509     /* Add the event log and the filter into their lists */
2510     InsertTailList(&EventLogList, &EventLog->ListEntry);
2511     InsertTailList(&EventLogFilterList, &EventLogFilter->ListEntry);
2512 
2513     /* Retrieve and cache the event log file */
2514     cchFileName = wcslen(lpszFileName) + 1;
2515     EventLog->FileName = HeapAlloc(GetProcessHeap(), 0, cchFileName * sizeof(WCHAR));
2516     if (EventLog->FileName)
2517         StringCchCopyW(EventLog->FileName, cchFileName, lpszFileName);
2518 
2519     hItem = TreeViewAddItem(hwndTreeView, htiUserLogs,
2520                             (LPWSTR)lpszFileName,
2521                             2, 3, (LPARAM)EventLogFilter);
2522 
2523     /* Select the event log */
2524     if (hItem)
2525     {
2526         // TreeView_Expand(hwndTreeView, htiUserLogs, TVE_EXPAND);
2527         TreeView_SelectItem(hwndTreeView, hItem);
2528         TreeView_EnsureVisible(hwndTreeView, hItem);
2529     }
2530     InvalidateRect(hwndTreeView, NULL, FALSE);
2531     SetFocus(hwndTreeView);
2532 }
2533 
2534 VOID
2535 OpenUserEventLog(VOID)
2536 {
2537     WCHAR szFileName[MAX_PATH];
2538 
2539     ZeroMemory(szFileName, sizeof(szFileName));
2540 
2541     sfn.lpstrFile = szFileName;
2542     sfn.nMaxFile  = ARRAYSIZE(szFileName);
2543 
2544     if (!GetOpenFileNameW(&sfn))
2545         return;
2546     sfn.lpstrFile[sfn.nMaxFile-1] = UNICODE_NULL;
2547 
2548     OpenUserEventLogFile(sfn.lpstrFile);
2549 }
2550 
2551 VOID
2552 SaveEventLog(IN PEVENTLOGFILTER EventLogFilter)
2553 {
2554     PEVENTLOG EventLog;
2555     HANDLE hEventLog;
2556     WCHAR szFileName[MAX_PATH];
2557 
2558     /* Bail out if there is no available filter */
2559     if (!EventLogFilter)
2560         return;
2561 
2562     ZeroMemory(szFileName, sizeof(szFileName));
2563 
2564     sfn.lpstrFile = szFileName;
2565     sfn.nMaxFile  = ARRAYSIZE(szFileName);
2566 
2567     if (!GetSaveFileNameW(&sfn))
2568         return;
2569 
2570     EventLogFilter_AddRef(EventLogFilter);
2571 
2572     EventLog = EventLogFilter->EventLogs[0];
2573     hEventLog = OpenEventLogW(EventLog->ComputerName, EventLog->LogName);
2574 
2575     EventLogFilter_Release(EventLogFilter);
2576 
2577     if (!hEventLog)
2578     {
2579         ShowWin32Error(GetLastError());
2580         return;
2581     }
2582 
2583     if (!BackupEventLogW(hEventLog, szFileName))
2584         ShowWin32Error(GetLastError());
2585 
2586     CloseEventLog(hEventLog);
2587 }
2588 
2589 VOID
2590 CloseUserEventLog(IN PEVENTLOGFILTER EventLogFilter, IN HTREEITEM hti)
2591 {
2592     /* Bail out if there is no available filter */
2593     if (!EventLogFilter)
2594         return;
2595 
2596     if (InterlockedCompareExchangePointer((PVOID*)&ActiveFilter, NULL, NULL) == EventLogFilter)
2597     {
2598         /* Signal the enumerator thread we want to stop enumerating events */
2599         // EnumEvents(NULL);
2600         InterlockedExchangePointer((PVOID*)&EnumFilter, NULL);
2601         SetEvent(hStartEnumEvent);
2602     }
2603 
2604     /*
2605      * The deletion of the item automatically triggers a TVN_SELCHANGED
2606      * notification, that will reset the ActiveFilter (in case the item
2607      * selected is a filter). Otherwise we reset it there.
2608      */
2609     TreeView_DeleteItem(hwndTreeView, hti);
2610 
2611     /* Remove the filter from the list */
2612     RemoveEntryList(&EventLogFilter->ListEntry);
2613     EventLogFilter_Release(EventLogFilter);
2614 
2615     // /* Select the default event log */
2616     // // TreeView_Expand(hwndTreeView, htiUserLogs, TVE_EXPAND);
2617     // TreeView_SelectItem(hwndTreeView, hItem);
2618     // TreeView_EnsureVisible(hwndTreeView, hItem);
2619     InvalidateRect(hwndTreeView, NULL, FALSE);
2620     SetFocus(hwndTreeView);
2621 }
2622 
2623 
2624 BOOL
2625 ClearEvents(IN PEVENTLOGFILTER EventLogFilter)
2626 {
2627     BOOL Success;
2628     PEVENTLOG EventLog;
2629     HANDLE hEventLog;
2630     WCHAR szFileName[MAX_PATH];
2631     WCHAR szMessage[MAX_LOADSTRING];
2632 
2633     /* Bail out if there is no available filter */
2634     if (!EventLogFilter)
2635         return FALSE;
2636 
2637     ZeroMemory(szFileName, sizeof(szFileName));
2638     ZeroMemory(szMessage, sizeof(szMessage));
2639 
2640     LoadStringW(hInst, IDS_CLEAREVENTS_MSG, szMessage, ARRAYSIZE(szMessage));
2641 
2642     sfn.lpstrFile = szFileName;
2643     sfn.nMaxFile  = ARRAYSIZE(szFileName);
2644 
2645     switch (MessageBoxW(hwndMainWindow, szMessage, szTitle, MB_YESNOCANCEL | MB_ICONINFORMATION))
2646     {
2647         case IDCANCEL:
2648             return FALSE;
2649 
2650         case IDNO:
2651             sfn.lpstrFile = NULL;
2652             break;
2653 
2654         case IDYES:
2655             if (!GetSaveFileNameW(&sfn))
2656                 return FALSE;
2657             break;
2658     }
2659 
2660     EventLogFilter_AddRef(EventLogFilter);
2661 
2662     EventLog = EventLogFilter->EventLogs[0];
2663     hEventLog = OpenEventLogW(EventLog->ComputerName, EventLog->LogName);
2664 
2665     EventLogFilter_Release(EventLogFilter);
2666 
2667     if (!hEventLog)
2668     {
2669         ShowWin32Error(GetLastError());
2670         return FALSE;
2671     }
2672 
2673     Success = ClearEventLogW(hEventLog, sfn.lpstrFile);
2674     if (!Success)
2675         ShowWin32Error(GetLastError());
2676 
2677     CloseEventLog(hEventLog);
2678     return Success;
2679 }
2680 
2681 
2682 VOID
2683 Refresh(IN PEVENTLOGFILTER EventLogFilter)
2684 {
2685     /* Bail out if there is no available filter */
2686     if (!EventLogFilter)
2687         return;
2688 
2689     /* Reenumerate the events through the filter */
2690     EnumEvents(EventLogFilter);
2691 }
2692 
2693 
2694 ATOM
2695 MyRegisterClass(HINSTANCE hInstance)
2696 {
2697     WNDCLASSEXW wcex;
2698 
2699     wcex.cbSize = sizeof(wcex);
2700     wcex.style = 0;
2701     wcex.lpfnWndProc = WndProc;
2702     wcex.cbClsExtra = 0;
2703     wcex.cbWndExtra = 0;
2704     wcex.hInstance = hInstance;
2705     wcex.hIcon = LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_EVENTVWR));
2706     wcex.hCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_ARROW));
2707     wcex.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); // COLOR_WINDOW + 1
2708     wcex.lpszMenuName = MAKEINTRESOURCEW(IDM_EVENTVWR);
2709     wcex.lpszClassName = EVENTVWR_WNDCLASS;
2710     wcex.hIconSm = (HICON)LoadImageW(hInstance,
2711                                      MAKEINTRESOURCEW(IDI_EVENTVWR),
2712                                      IMAGE_ICON,
2713                                      16,
2714                                      16,
2715                                      LR_SHARED);
2716 
2717     return RegisterClassExW(&wcex);
2718 }
2719 
2720 
2721 BOOL
2722 GetDisplayNameFileAndID(IN LPCWSTR lpLogName,
2723                         OUT PWCHAR lpModuleName, // TODO: Add IN DWORD BufLen
2724                         OUT PDWORD pdwMessageID)
2725 {
2726     BOOL Success = FALSE;
2727     LONG Result;
2728     HKEY hLogKey;
2729     WCHAR *KeyPath;
2730     SIZE_T cbKeyPath;
2731     DWORD dwType, cbData;
2732     DWORD dwMessageID = 0;
2733     WCHAR szModuleName[MAX_PATH];
2734 
2735     /* Use a default value for the message ID */
2736     *pdwMessageID = 0;
2737 
2738     cbKeyPath = (wcslen(EVENTLOG_BASE_KEY) + wcslen(lpLogName) + 1) * sizeof(WCHAR);
2739     KeyPath = HeapAlloc(GetProcessHeap(), 0, cbKeyPath);
2740     if (!KeyPath)
2741     {
2742         ShowWin32Error(ERROR_NOT_ENOUGH_MEMORY);
2743         return FALSE;
2744     }
2745 
2746     StringCbCopyW(KeyPath, cbKeyPath, EVENTLOG_BASE_KEY);
2747     StringCbCatW(KeyPath, cbKeyPath, lpLogName);
2748 
2749     Result = RegOpenKeyExW(hkMachine, KeyPath, 0, KEY_QUERY_VALUE, &hLogKey);
2750     HeapFree(GetProcessHeap(), 0, KeyPath);
2751     if (Result != ERROR_SUCCESS)
2752     {
2753         ShowWin32Error(Result);
2754         return FALSE;
2755     }
2756 
2757     cbData = sizeof(szModuleName);
2758     Result = RegQueryValueExW(hLogKey,
2759                               L"DisplayNameFile",
2760                               NULL,
2761                               &dwType,
2762                               (LPBYTE)szModuleName,
2763                               &cbData);
2764     if ((Result != ERROR_SUCCESS) || (dwType != REG_EXPAND_SZ && dwType != REG_SZ))
2765     {
2766         szModuleName[0] = UNICODE_NULL;
2767     }
2768     else
2769     {
2770         /* NULL-terminate the string and expand it */
2771         szModuleName[cbData / sizeof(WCHAR) - 1] = UNICODE_NULL;
2772         GetExpandedFilePathName(lpComputerName, szModuleName, lpModuleName, ARRAYSIZE(szModuleName));
2773         Success = TRUE;
2774     }
2775 
2776     /*
2777      * If we have a 'DisplayNameFile', query for 'DisplayNameID';
2778      * otherwise it's not really useful. 'DisplayNameID' is optional.
2779      */
2780     if (Success)
2781     {
2782         cbData = sizeof(dwMessageID);
2783         Result = RegQueryValueExW(hLogKey,
2784                                   L"DisplayNameID",
2785                                   NULL,
2786                                   &dwType,
2787                                   (LPBYTE)&dwMessageID,
2788                                   &cbData);
2789         if ((Result != ERROR_SUCCESS) || (dwType != REG_DWORD))
2790             dwMessageID = 0;
2791 
2792         *pdwMessageID = dwMessageID;
2793     }
2794 
2795     RegCloseKey(hLogKey);
2796 
2797     return Success;
2798 }
2799 
2800 
2801 VOID
2802 BuildLogListAndFilterList(IN LPCWSTR lpComputerName)
2803 {
2804     LONG Result;
2805     HKEY hEventLogKey, hLogKey;
2806     DWORD dwNumLogs = 0;
2807     DWORD dwIndex, dwMaxKeyLength;
2808     DWORD dwType;
2809     PEVENTLOG EventLog;
2810     PEVENTLOGFILTER EventLogFilter;
2811     LPWSTR LogName = NULL;
2812     WCHAR szModuleName[MAX_PATH];
2813     DWORD lpcName;
2814     DWORD dwMessageID;
2815     LPWSTR lpDisplayName;
2816     HTREEITEM hRootNode = NULL, hItem = NULL, hItemDefault = NULL;
2817 
2818     if (hkMachine && hkMachine != HKEY_LOCAL_MACHINE)
2819     {
2820         /* We are connected to some other computer, close the old connection */
2821         RegCloseKey(hkMachine);
2822         hkMachine = NULL;
2823     }
2824     if (!lpComputerName || !*lpComputerName)
2825     {
2826         /* Use the local computer registry */
2827         hkMachine = HKEY_LOCAL_MACHINE;
2828     }
2829     else
2830     {
2831         /* Connect to the remote computer registry */
2832         Result = RegConnectRegistry(lpComputerName, HKEY_LOCAL_MACHINE, &hkMachine);
2833         if (Result != ERROR_SUCCESS)
2834         {
2835             /* Connection failed, display a message and bail out */
2836             hkMachine = NULL;
2837             ShowWin32Error(GetLastError());
2838             return;
2839         }
2840     }
2841 
2842     /* Open the EventLog key */
2843     Result = RegOpenKeyExW(hkMachine, EVENTLOG_BASE_KEY, 0, KEY_READ, &hEventLogKey);
2844     if (Result != ERROR_SUCCESS)
2845     {
2846         return;
2847     }
2848 
2849     /* Retrieve the number of event logs enumerated as registry keys */
2850     Result = RegQueryInfoKeyW(hEventLogKey, NULL, NULL, NULL, &dwNumLogs, &dwMaxKeyLength,
2851                               NULL, NULL, NULL, NULL, NULL, NULL);
2852     if (Result != ERROR_SUCCESS)
2853     {
2854         goto Quit;
2855     }
2856     if (!dwNumLogs)
2857         goto Quit;
2858 
2859     /* Take the NULL terminator into account */
2860     ++dwMaxKeyLength;
2861 
2862     /* Allocate the temporary buffer */
2863     LogName = HeapAlloc(GetProcessHeap(), 0, dwMaxKeyLength * sizeof(WCHAR));
2864     if (!LogName)
2865         goto Quit;
2866 
2867     /* Enumerate and retrieve each event log name */
2868     for (dwIndex = 0; dwIndex < dwNumLogs; dwIndex++)
2869     {
2870         lpcName = dwMaxKeyLength;
2871         Result = RegEnumKeyExW(hEventLogKey, dwIndex, LogName, &lpcName, NULL, NULL, NULL, NULL);
2872         if (Result != ERROR_SUCCESS)
2873             continue;
2874 
2875         /* Take the NULL terminator into account */
2876         ++lpcName;
2877 
2878         /* Allocate a new event log entry */
2879         EventLog = AllocEventLog(lpComputerName, LogName, TRUE);
2880         if (EventLog == NULL)
2881             continue;
2882 
2883         /* Allocate a new event log filter entry for this event log */
2884         EventLogFilter = AllocEventLogFilter(// LogName,
2885                                              TRUE, TRUE, TRUE, TRUE, TRUE,
2886                                              NULL, NULL, NULL,
2887                                              1, &EventLog);
2888         if (EventLogFilter == NULL)
2889         {
2890             EventLog_Free(EventLog);
2891             continue;
2892         }
2893 
2894         /* Add the event log and the filter into their lists */
2895         InsertTailList(&EventLogList, &EventLog->ListEntry);
2896         InsertTailList(&EventLogFilterList, &EventLogFilter->ListEntry);
2897 
2898         EventLog->FileName = NULL;
2899 
2900         /* Retrieve and cache the event log file */
2901         Result = RegOpenKeyExW(hEventLogKey,
2902                                LogName,
2903                                0,
2904                                KEY_QUERY_VALUE,
2905                                &hLogKey);
2906         if (Result == ERROR_SUCCESS)
2907         {
2908             lpcName = 0;
2909             Result = RegQueryValueExW(hLogKey,
2910                                       L"File",
2911                                       NULL,
2912                                       &dwType,
2913                                       NULL,
2914                                       &lpcName);
2915             if ((Result != ERROR_SUCCESS) || (dwType != REG_EXPAND_SZ && dwType != REG_SZ))
2916             {
2917                 // Windows' EventLog uses some kind of default value, we do not.
2918                 EventLog->FileName = NULL;
2919             }
2920             else
2921             {
2922                 lpcName = ROUND_DOWN(lpcName, sizeof(WCHAR));
2923                 EventLog->FileName = HeapAlloc(GetProcessHeap(), 0, lpcName);
2924                 if (EventLog->FileName)
2925                 {
2926                     Result = RegQueryValueExW(hLogKey,
2927                                               L"File",
2928                                               NULL,
2929                                               &dwType,
2930                                               (LPBYTE)EventLog->FileName,
2931                                               &lpcName);
2932                     if (Result != ERROR_SUCCESS)
2933                     {
2934                         HeapFree(GetProcessHeap(), 0, EventLog->FileName);
2935                         EventLog->FileName = NULL;
2936                     }
2937                     else
2938                     {
2939                         EventLog->FileName[lpcName / sizeof(WCHAR) - 1] = UNICODE_NULL;
2940                     }
2941                 }
2942             }
2943 
2944             RegCloseKey(hLogKey);
2945         }
2946 
2947         /* Get the display name for the event log */
2948         lpDisplayName = NULL;
2949 
2950         ZeroMemory(szModuleName, sizeof(szModuleName));
2951         if (GetDisplayNameFileAndID(LogName, szModuleName, &dwMessageID))
2952         {
2953             /* Retrieve the message string without appending extra newlines */
2954             lpDisplayName =
2955             GetMessageStringFromDll(szModuleName,
2956                                     FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
2957                                     FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
2958                                     dwMessageID,
2959                                     0,
2960                                     NULL);
2961         }
2962 
2963         /*
2964          * Select the correct tree root node, whether the log is a System
2965          * or an Application log. Default to Application log otherwise.
2966          */
2967         hRootNode = htiAppLogs;
2968         for (lpcName = 0; lpcName < ARRAYSIZE(SystemLogs); ++lpcName)
2969         {
2970             /* Check whether the log name is part of the system logs */
2971             if (wcsicmp(LogName, SystemLogs[lpcName]) == 0)
2972             {
2973                 hRootNode = htiSystemLogs;
2974                 break;
2975             }
2976         }
2977 
2978         hItem = TreeViewAddItem(hwndTreeView, hRootNode,
2979                                 (lpDisplayName ? lpDisplayName : LogName),
2980                                 2, 3, (LPARAM)EventLogFilter);
2981 
2982         /* Try to get the default event log: "Application" */
2983         if ((hItemDefault == NULL) && (wcsicmp(LogName, SystemLogs[0]) == 0))
2984         {
2985             hItemDefault = hItem;
2986         }
2987 
2988         /* Free the buffer allocated by FormatMessage */
2989         if (lpDisplayName)
2990             LocalFree(lpDisplayName);
2991     }
2992 
2993     HeapFree(GetProcessHeap(), 0, LogName);
2994 
2995 Quit:
2996     RegCloseKey(hEventLogKey);
2997 
2998     /* Select the default event log */
2999     if (hItemDefault)
3000     {
3001         // TreeView_Expand(hwndTreeView, hRootNode, TVE_EXPAND);
3002         TreeView_SelectItem(hwndTreeView, hItemDefault);
3003         TreeView_EnsureVisible(hwndTreeView, hItemDefault);
3004     }
3005     InvalidateRect(hwndTreeView, NULL, FALSE);
3006     SetFocus(hwndTreeView);
3007 
3008     return;
3009 }
3010 
3011 VOID
3012 FreeLogList(VOID)
3013 {
3014     PLIST_ENTRY Entry;
3015     PEVENTLOG EventLog;
3016 
3017     while (!IsListEmpty(&EventLogList))
3018     {
3019         Entry = RemoveHeadList(&EventLogList);
3020         EventLog = (PEVENTLOG)CONTAINING_RECORD(Entry, EVENTLOG, ListEntry);
3021         EventLog_Free(EventLog);
3022     }
3023 
3024     return;
3025 }
3026 
3027 VOID
3028 FreeLogFilterList(VOID)
3029 {
3030     PLIST_ENTRY Entry;
3031     PEVENTLOGFILTER EventLogFilter;
3032 
3033     while (!IsListEmpty(&EventLogFilterList))
3034     {
3035         Entry = RemoveHeadList(&EventLogFilterList);
3036         EventLogFilter = (PEVENTLOGFILTER)CONTAINING_RECORD(Entry, EVENTLOGFILTER, ListEntry);
3037         EventLogFilter_Free(EventLogFilter);
3038     }
3039 
3040     ActiveFilter = NULL;
3041 
3042     return;
3043 }
3044 
3045 BOOL
3046 InitInstance(HINSTANCE hInstance)
3047 {
3048     RECT rcClient, rs;
3049     LONG StatusHeight;
3050     HIMAGELIST hSmall;
3051     LVCOLUMNW lvc = {0};
3052     WCHAR szTemp[256];
3053 
3054     /* Create the main window */
3055     rs = Settings.wpPos.rcNormalPosition;
3056     hwndMainWindow = CreateWindowW(EVENTVWR_WNDCLASS,
3057                                    szTitle,
3058                                    WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
3059                                    rs.left, rs.top,
3060                                    (rs.right  != CW_USEDEFAULT && rs.left != CW_USEDEFAULT) ? rs.right - rs.left : CW_USEDEFAULT,
3061                                    (rs.bottom != CW_USEDEFAULT && rs.top  != CW_USEDEFAULT) ? rs.bottom - rs.top : CW_USEDEFAULT,
3062                                    NULL,
3063                                    NULL,
3064                                    hInstance,
3065                                    NULL);
3066     if (!hwndMainWindow)
3067         return FALSE;
3068 
3069     /* Create the status bar */
3070     hwndStatus = CreateWindowExW(0,                                  // no extended styles
3071                                  STATUSCLASSNAMEW,                   // status bar
3072                                  L"",                                // no text
3073                                  WS_CHILD | WS_VISIBLE | CCS_BOTTOM | SBARS_SIZEGRIP, // styles
3074                                  0, 0, 0, 0,                         // x, y, cx, cy
3075                                  hwndMainWindow,                     // parent window
3076                                  (HMENU)100,                         // window ID
3077                                  hInstance,                          // instance
3078                                  NULL);                              // window data
3079 
3080     GetClientRect(hwndMainWindow, &rcClient);
3081     GetWindowRect(hwndStatus, &rs);
3082     StatusHeight = rs.bottom - rs.top;
3083 
3084     /* Create a progress bar in the status bar (hidden by default) */
3085     StatusBar_GetItemRect(hwndStatus, 0, &rs);
3086     hwndStatusProgress = CreateWindowExW(0,                          // no extended styles
3087                                          PROGRESS_CLASSW,            // status bar
3088                                          NULL,                       // no text
3089                                          WS_CHILD | PBS_SMOOTH,      // styles
3090                                          rs.left, rs.top,            // x, y
3091                                          rs.right - rs.left, rs.bottom - rs.top, // cx, cy
3092                                          hwndStatus,                 // parent window
3093                                          NULL,                       // window ID
3094                                          hInstance,                  // instance
3095                                          NULL);                      // window data
3096     /* Remove its static edge */
3097     SetWindowLongPtrW(hwndStatusProgress, GWL_EXSTYLE,
3098                       GetWindowLongPtrW(hwndStatusProgress, GWL_EXSTYLE) & ~WS_EX_STATICEDGE);
3099     ProgressBar_SetStep(hwndStatusProgress, 1);
3100 
3101     /* Initialize the splitter default positions */
3102     nVSplitPos = Settings.nVSplitPos;
3103     nHSplitPos = Settings.nHSplitPos;
3104 
3105     /* Create the TreeView */
3106     hwndTreeView = CreateWindowExW(WS_EX_CLIENTEDGE,
3107                                    WC_TREEVIEWW,
3108                                    NULL,
3109                                    // WS_CHILD | WS_VISIBLE | TVS_HASLINES | TVS_SHOWSELALWAYS,
3110                                    WS_CHILD | WS_VISIBLE | /* WS_TABSTOP | */ TVS_HASLINES | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_EDITLABELS | TVS_SHOWSELALWAYS,
3111                                    0, 0,
3112                                    nVSplitPos - SPLIT_WIDTH/2,
3113                                    (rcClient.bottom - rcClient.top) - StatusHeight,
3114                                    hwndMainWindow,
3115                                    NULL,
3116                                    hInstance,
3117                                    NULL);
3118 
3119     /* Create the ImageList */
3120     hSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
3121                               GetSystemMetrics(SM_CYSMICON),
3122                               ILC_COLOR32 | ILC_MASK, // ILC_COLOR24
3123                               1, 1);
3124 
3125     /* Add event type icons to the ImageList: closed/opened folder, event log (normal/viewed) */
3126     ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_CLOSED_CATEGORY)));
3127     ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_OPENED_CATEGORY)));
3128     ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_EVENTLOG)));
3129     ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_EVENTVWR)));
3130 
3131     /* Assign the ImageList to the Tree View */
3132     TreeView_SetImageList(hwndTreeView, hSmall, TVSIL_NORMAL);
3133 
3134     /* Add the event logs nodes */
3135     // "System Logs"
3136     LoadStringW(hInstance, IDS_EVENTLOG_SYSTEM, szTemp, ARRAYSIZE(szTemp));
3137     htiSystemLogs = TreeViewAddItem(hwndTreeView, NULL, szTemp, 0, 1, (LPARAM)NULL);
3138     // "Application Logs"
3139     LoadStringW(hInstance, IDS_EVENTLOG_APP, szTemp, ARRAYSIZE(szTemp));
3140     htiAppLogs = TreeViewAddItem(hwndTreeView, NULL, szTemp, 0, 1, (LPARAM)NULL);
3141     // "User Logs"
3142     LoadStringW(hInstance, IDS_EVENTLOG_USER, szTemp, ARRAYSIZE(szTemp));
3143     htiUserLogs = TreeViewAddItem(hwndTreeView, NULL, szTemp, 0, 1, (LPARAM)NULL);
3144 
3145     /* Create the Event details pane (optional) */
3146     hwndEventDetails = CreateEventDetailsCtrl(hInst, hwndMainWindow, (LPARAM)NULL);
3147     if (hwndEventDetails)
3148     {
3149     SetWindowLongPtrW(hwndEventDetails, GWL_EXSTYLE,
3150                       GetWindowLongPtrW(hwndEventDetails, GWL_EXSTYLE) | WS_EX_CLIENTEDGE);
3151     SetWindowPos(hwndEventDetails, NULL,
3152                  nVSplitPos + SPLIT_WIDTH/2,
3153                  nHSplitPos + SPLIT_WIDTH/2,
3154                  (rcClient.right - rcClient.left) - nVSplitPos - SPLIT_WIDTH/2,
3155                  (rcClient.bottom - rcClient.top) - nHSplitPos - SPLIT_WIDTH/2 - StatusHeight,
3156                  SWP_NOZORDER | SWP_NOACTIVATE | (Settings.bShowDetailsPane ? SWP_SHOWWINDOW : SWP_HIDEWINDOW));
3157     }
3158 
3159     /* Create the ListView */
3160     hwndListView = CreateWindowExW(WS_EX_CLIENTEDGE,
3161                                    WC_LISTVIEWW,
3162                                    NULL,
3163                                    WS_CHILD | WS_VISIBLE | LVS_SHOWSELALWAYS | LVS_REPORT,
3164                                    nVSplitPos + SPLIT_WIDTH/2,
3165                                    0,
3166                                    (rcClient.right - rcClient.left) - nVSplitPos - SPLIT_WIDTH/2,
3167                                    hwndEventDetails && Settings.bShowDetailsPane
3168                                        ? nHSplitPos - SPLIT_WIDTH/2
3169                                        : (rcClient.bottom - rcClient.top) - StatusHeight,
3170                                    hwndMainWindow,
3171                                    NULL,
3172                                    hInstance,
3173                                    NULL);
3174 
3175     /* Add the extended ListView styles */
3176     ListView_SetExtendedListViewStyle(hwndListView, LVS_EX_HEADERDRAGDROP | LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP | (Settings.bShowGrid ? LVS_EX_GRIDLINES : 0));
3177 
3178     /* Create the ImageList */
3179     hSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
3180                               GetSystemMetrics(SM_CYSMICON),
3181                               ILC_COLOR32 | ILC_MASK, // ILC_COLOR24
3182                               1, 1);
3183 
3184     /* Add event type icons to the ImageList */
3185     ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_INFORMATIONICON)));
3186     ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_WARNINGICON)));
3187     ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_ERRORICON)));
3188     ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_AUDITSUCCESSICON)));
3189     ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_AUDITFAILUREICON)));
3190 
3191     /* Assign the ImageList to the List View */
3192     ListView_SetImageList(hwndListView, hSmall, LVSIL_SMALL);
3193 
3194     /* Now set up the listview with its columns */
3195     lvc.mask = LVCF_TEXT | LVCF_WIDTH;
3196     lvc.cx = 90;
3197     LoadStringW(hInstance,
3198                 IDS_COLUMNTYPE,
3199                 szTemp,
3200                 ARRAYSIZE(szTemp));
3201     lvc.pszText = szTemp;
3202     ListView_InsertColumn(hwndListView, 0, &lvc);
3203 
3204     lvc.cx = 70;
3205     LoadStringW(hInstance,
3206                 IDS_COLUMNDATE,
3207                 szTemp,
3208                 ARRAYSIZE(szTemp));
3209     lvc.pszText = szTemp;
3210     ListView_InsertColumn(hwndListView, 1, &lvc);
3211 
3212     lvc.cx = 70;
3213     LoadStringW(hInstance,
3214                 IDS_COLUMNTIME,
3215                 szTemp,
3216                 ARRAYSIZE(szTemp));
3217     lvc.pszText = szTemp;
3218     ListView_InsertColumn(hwndListView, 2, &lvc);
3219 
3220     lvc.cx = 150;
3221     LoadStringW(hInstance,
3222                 IDS_COLUMNSOURCE,
3223                 szTemp,
3224                 ARRAYSIZE(szTemp));
3225     lvc.pszText = szTemp;
3226     ListView_InsertColumn(hwndListView, 3, &lvc);
3227 
3228     lvc.cx = 100;
3229     LoadStringW(hInstance,
3230                 IDS_COLUMNCATEGORY,
3231                 szTemp,
3232                 ARRAYSIZE(szTemp));
3233     lvc.pszText = szTemp;
3234     ListView_InsertColumn(hwndListView, 4, &lvc);
3235 
3236     lvc.cx = 60;
3237     LoadStringW(hInstance,
3238                 IDS_COLUMNEVENT,
3239                 szTemp,
3240                 ARRAYSIZE(szTemp));
3241     lvc.pszText = szTemp;
3242     ListView_InsertColumn(hwndListView, 5, &lvc);
3243 
3244     lvc.cx = 120;
3245     LoadStringW(hInstance,
3246                 IDS_COLUMNUSER,
3247                 szTemp,
3248                 ARRAYSIZE(szTemp));
3249     lvc.pszText = szTemp;
3250     ListView_InsertColumn(hwndListView, 6, &lvc);
3251 
3252     lvc.cx = 100;
3253     LoadStringW(hInstance,
3254                 IDS_COLUMNCOMPUTER,
3255                 szTemp,
3256                 ARRAYSIZE(szTemp));
3257     lvc.pszText = szTemp;
3258     ListView_InsertColumn(hwndListView, 7, &lvc);
3259 
3260     /* Initialize the save Dialog */
3261     ZeroMemory(&sfn, sizeof(sfn));
3262     ZeroMemory(szSaveFilter, sizeof(szSaveFilter));
3263 
3264     LoadStringW(hInst, IDS_SAVE_FILTER, szSaveFilter, ARRAYSIZE(szSaveFilter));
3265 
3266     sfn.lStructSize     = sizeof(sfn);
3267     sfn.hwndOwner       = hwndMainWindow;
3268     sfn.hInstance       = hInstance;
3269     sfn.lpstrFilter     = szSaveFilter;
3270     sfn.lpstrInitialDir = NULL;
3271     sfn.Flags           = OFN_EXPLORER | OFN_HIDEREADONLY | OFN_SHAREAWARE;
3272     sfn.lpstrDefExt     = NULL;
3273 
3274     ShowWindow(hwndMainWindow, Settings.wpPos.showCmd);
3275     UpdateWindow(hwndMainWindow);
3276 
3277     return TRUE;
3278 }
3279 
3280 VOID ResizeWnd(INT cx, INT cy)
3281 {
3282     RECT rs;
3283     LONG StatusHeight;
3284     LONG_PTR dwExStyle;
3285     HDWP hdwp;
3286 
3287     /* Resize the status bar -- now done in WM_SIZE */
3288     // SendMessageW(hwndStatus, WM_SIZE, 0, 0);
3289     GetWindowRect(hwndStatus, &rs);
3290     StatusHeight = rs.bottom - rs.top;
3291 
3292     /*
3293      * Move the progress bar -- Take into account for extra size due to the static edge
3294      * (AdjustWindowRectEx() does not seem to work for the progress bar).
3295      */
3296     StatusBar_GetItemRect(hwndStatus, 0, &rs);
3297     dwExStyle = GetWindowLongPtrW(hwndStatusProgress, GWL_EXSTYLE);
3298     SetWindowLongPtrW(hwndStatusProgress, GWL_EXSTYLE, dwExStyle | WS_EX_STATICEDGE);
3299     MoveWindow(hwndStatusProgress,
3300                rs.left, rs.top, rs.right - rs.left, rs.bottom - rs.top,
3301                IsWindowVisible(hwndStatusProgress) ? TRUE : FALSE);
3302     SetWindowLongPtrW(hwndStatusProgress, GWL_EXSTYLE, dwExStyle);
3303 
3304     /*
3305      * TODO: Adjust the splitter positions:
3306      * - Vertical splitter (1)  : fixed position from the left window side.
3307      * - Horizontal splitter (2): fixed position from the bottom window side.
3308      */
3309     nVSplitPos = min(max(nVSplitPos, SPLIT_WIDTH/2), cx - SPLIT_WIDTH/2); // OK
3310     nHSplitPos = min(max(nHSplitPos, SPLIT_WIDTH/2), cy - SPLIT_WIDTH/2 - StatusHeight); // FIXME!
3311 
3312     hdwp = BeginDeferWindowPos(3);
3313 
3314     if (hdwp)
3315         hdwp = DeferWindowPos(hdwp,
3316                               hwndTreeView,
3317                               HWND_TOP,
3318                               0, 0,
3319                               nVSplitPos - SPLIT_WIDTH/2,
3320                               cy - StatusHeight,
3321                               SWP_NOZORDER | SWP_NOACTIVATE);
3322 
3323     if (hdwp)
3324         hdwp = DeferWindowPos(hdwp,
3325                               hwndListView,
3326                               HWND_TOP,
3327                               nVSplitPos + SPLIT_WIDTH/2, 0,
3328                               cx - nVSplitPos - SPLIT_WIDTH/2,
3329                               hwndEventDetails && Settings.bShowDetailsPane
3330                                   ? nHSplitPos - SPLIT_WIDTH/2
3331                                   : cy - StatusHeight,
3332                               SWP_NOZORDER | SWP_NOACTIVATE);
3333 
3334     if (hwndEventDetails && Settings.bShowDetailsPane && hdwp)
3335         hdwp = DeferWindowPos(hdwp,
3336                               hwndEventDetails,
3337                               HWND_TOP,
3338                               nVSplitPos + SPLIT_WIDTH/2,
3339                               nHSplitPos + SPLIT_WIDTH/2,
3340                               cx - nVSplitPos - SPLIT_WIDTH/2,
3341                               cy - nHSplitPos - SPLIT_WIDTH/2 - StatusHeight,
3342                               SWP_NOZORDER | SWP_NOACTIVATE);
3343 
3344     if (hdwp)
3345         EndDeferWindowPos(hdwp);
3346 }
3347 
3348 
3349 LRESULT CALLBACK
3350 WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3351 {
3352     RECT rect;
3353 
3354     switch (uMsg)
3355     {
3356         case WM_CREATE:
3357             hMainMenu = GetMenu(hWnd);
3358             break;
3359 
3360         case WM_DESTROY:
3361         {
3362             GetWindowPlacement(hwndMainWindow, &Settings.wpPos);
3363             PostQuitMessage(0);
3364             break;
3365         }
3366 
3367         case WM_NOTIFY:
3368         {
3369             LPNMHDR hdr = (LPNMHDR)lParam;
3370 
3371             if (hdr->hwndFrom == hwndListView)
3372             {
3373                 switch (hdr->code)
3374                 {
3375                     case LVN_ITEMCHANGED:
3376                     {
3377                         LPNMLISTVIEW pnmv = (LPNMLISTVIEW)lParam;
3378 
3379                         if ( (pnmv->uChanged  & LVIF_STATE) && /* The state has changed */
3380                              (pnmv->uNewState & LVIS_SELECTED) /* The item has been (de)selected */ )
3381                         {
3382                             if (hwndEventDetails)
3383                                 SendMessageW(hwndEventDetails, EVT_DISPLAY, 0, 0);
3384                         }
3385                         break;
3386                     }
3387 
3388                     case NM_DBLCLK:
3389                     case NM_RETURN:
3390                         SendMessageW(hWnd, WM_COMMAND, IDM_EVENT_DETAILS, 0);
3391                         break;
3392                 }
3393             }
3394             else if (hdr->hwndFrom == hwndTreeView)
3395             {
3396                 switch (hdr->code)
3397                 {
3398                     case TVN_BEGINLABELEDIT:
3399                     {
3400                         HTREEITEM hItem = ((LPNMTVDISPINFO)lParam)->item.hItem;
3401 
3402                         /* Disable label editing for root nodes */
3403                         return ((hItem == htiSystemLogs) ||
3404                                 (hItem == htiAppLogs)    ||
3405                                 (hItem == htiUserLogs));
3406                     }
3407 
3408                     case TVN_ENDLABELEDIT:
3409                     {
3410                         TVITEMW item = ((LPNMTVDISPINFO)lParam)->item;
3411                         HTREEITEM hItem = item.hItem;
3412 
3413                         /* Disable label editing for root nodes */
3414                         if ((hItem == htiSystemLogs) ||
3415                             (hItem == htiAppLogs)    ||
3416                             (hItem == htiUserLogs))
3417                         {
3418                             return FALSE;
3419                         }
3420 
3421                         if (item.pszText)
3422                         {
3423                             LPWSTR pszText = item.pszText;
3424 
3425                             /* Trim leading whitespace */
3426                             while (*pszText && iswspace(*pszText))
3427                                 ++pszText;
3428 
3429                             if (!*pszText)
3430                                 return FALSE;
3431 
3432                             return TRUE;
3433                         }
3434                         else
3435                         {
3436                             return FALSE;
3437                         }
3438                     }
3439 
3440                     case TVN_SELCHANGED:
3441                     {
3442                         PEVENTLOGFILTER EventLogFilter =
3443                             (PEVENTLOGFILTER)((LPNMTREEVIEW)lParam)->itemNew.lParam;
3444 
3445                         // FIXME: It might be nice to reference here the filter,
3446                         // so that we don't have to reference/dereference it many times
3447                         // in the other functions???
3448 
3449                         // FIXME: This is a hack!!
3450                         if (hwndEventDetails && EventLogFilter)
3451                         {
3452                             SendMessageW(hwndEventDetails, EVT_SETFILTER, 0, (LPARAM)EventLogFilter);
3453                         }
3454 
3455                         if (EventLogFilter)
3456                         {
3457                             /*
3458                              * If we have selected a filter, enable the menu commands;
3459                              * they will possibly be updated after events enumeration.
3460                              */
3461                             EnableMenuItem(hMainMenu, IDM_SAVE_EVENTLOG, MF_BYCOMMAND | MF_ENABLED);
3462                             EnableMenuItem(hMainMenu, IDM_CLOSE_EVENTLOG, MF_BYCOMMAND | MF_ENABLED);
3463                             EnableMenuItem(hMainMenu, IDM_CLEAR_EVENTS, MF_BYCOMMAND | MF_ENABLED);
3464                             EnableMenuItem(hMainMenu, IDM_RENAME_EVENTLOG, MF_BYCOMMAND | MF_ENABLED);
3465                             EnableMenuItem(hMainMenu, IDM_EVENTLOG_SETTINGS, MF_BYCOMMAND | MF_ENABLED);
3466                         }
3467                         else
3468                         {
3469                             EnableMenuItem(hMainMenu, IDM_SAVE_EVENTLOG, MF_BYCOMMAND | MF_GRAYED);
3470                             EnableMenuItem(hMainMenu, IDM_CLOSE_EVENTLOG, MF_BYCOMMAND | MF_GRAYED);
3471                             EnableMenuItem(hMainMenu, IDM_CLEAR_EVENTS, MF_BYCOMMAND | MF_GRAYED);
3472                             EnableMenuItem(hMainMenu, IDM_RENAME_EVENTLOG, MF_BYCOMMAND | MF_GRAYED);
3473                             EnableMenuItem(hMainMenu, IDM_EVENTLOG_SETTINGS, MF_BYCOMMAND | MF_GRAYED);
3474                         }
3475 
3476                         /*
3477                          * The enumeration thread that is triggered by EnumEvents
3478                          * will set a new value for the 'ActiveFilter'.
3479                          */
3480                         if (EventLogFilter)
3481                             EnumEvents(EventLogFilter);
3482 
3483                         break;
3484                     }
3485                 }
3486             }
3487             break;
3488         }
3489 
3490         case WM_COMMAND:
3491         {
3492             /* Parse the menu selections */
3493             switch (LOWORD(wParam))
3494             {
3495                 case IDM_OPEN_EVENTLOG:
3496                     OpenUserEventLog();
3497                     break;
3498 
3499                 case IDM_SAVE_EVENTLOG:
3500                     SaveEventLog(GetSelectedFilter(NULL));
3501                     break;
3502 
3503                 case IDM_CLOSE_EVENTLOG:
3504                 {
3505                     HTREEITEM hti;
3506                     PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(&hti);
3507                     CloseUserEventLog(EventLogFilter, hti);
3508                     break;
3509                 }
3510 
3511                 case IDM_CLEAR_EVENTS:
3512                 {
3513                     PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(NULL);
3514                     if (EventLogFilter && ClearEvents(EventLogFilter))
3515                         Refresh(EventLogFilter);
3516                     break;
3517                 }
3518 
3519                 case IDM_RENAME_EVENTLOG:
3520                     if (GetFocus() == hwndTreeView)
3521                         TreeView_EditLabel(hwndTreeView, TreeView_GetSelection(hwndTreeView));
3522                     break;
3523 
3524                 case IDM_EVENTLOG_SETTINGS:
3525                 {
3526                     PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(NULL);
3527                     // TODO: Check the returned value?
3528                     if (EventLogFilter)
3529                         EventLogProperties(hInst, hWnd, EventLogFilter);
3530                     break;
3531                 }
3532 
3533                 case IDM_LIST_NEWEST:
3534                 {
3535                     CheckMenuRadioItem(hMainMenu, IDM_LIST_NEWEST, IDM_LIST_OLDEST, IDM_LIST_NEWEST, MF_BYCOMMAND);
3536                     if (!Settings.bNewestEventsFirst)
3537                     {
3538                         Settings.bNewestEventsFirst = TRUE;
3539                         Refresh(GetSelectedFilter(NULL));
3540                     }
3541                     break;
3542                 }
3543 
3544                 case IDM_LIST_OLDEST:
3545                 {
3546                     CheckMenuRadioItem(hMainMenu, IDM_LIST_NEWEST, IDM_LIST_OLDEST, IDM_LIST_OLDEST, MF_BYCOMMAND);
3547                     if (Settings.bNewestEventsFirst)
3548                     {
3549                         Settings.bNewestEventsFirst = FALSE;
3550                         Refresh(GetSelectedFilter(NULL));
3551                     }
3552                     break;
3553                 }
3554 
3555                 case IDM_EVENT_DETAILS:
3556                 {
3557                     // LPNMITEMACTIVATE lpnmitem = (LPNMITEMACTIVATE)lParam;
3558                     PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(NULL);
3559                     if (/*lpnmitem->iItem != -1 &&*/ EventLogFilter)
3560                     {
3561                         EventLogFilter_AddRef(EventLogFilter);
3562                         DialogBoxParamW(hInst,
3563                                         MAKEINTRESOURCEW(IDD_EVENTDETAILS_DLG),
3564                                         hWnd,
3565                                         EventDetails,
3566                                         (LPARAM)EventLogFilter);
3567                         EventLogFilter_Release(EventLogFilter);
3568                     }
3569                     break;
3570                 }
3571 
3572                 case IDM_REFRESH:
3573                     Refresh(GetSelectedFilter(NULL));
3574                     break;
3575 
3576                 case IDM_EVENT_DETAILS_VIEW:
3577                 {
3578                     Settings.bShowDetailsPane = !Settings.bShowDetailsPane;
3579                     CheckMenuItem(hMainMenu, IDM_EVENT_DETAILS_VIEW,
3580                                   MF_BYCOMMAND | (Settings.bShowDetailsPane ? MF_CHECKED : MF_UNCHECKED));
3581 
3582                     GetClientRect(hWnd, &rect);
3583                     if (Settings.bShowDetailsPane)
3584                     {
3585                         ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
3586                         ShowWindow(hwndEventDetails, SW_SHOW);
3587                     }
3588                     else
3589                     {
3590                         ShowWindow(hwndEventDetails, SW_HIDE);
3591                         ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
3592                     }
3593 
3594                     break;
3595                 }
3596 
3597                 case IDM_LIST_GRID_LINES:
3598                 {
3599                     Settings.bShowGrid = !Settings.bShowGrid;
3600                     CheckMenuItem(hMainMenu, IDM_LIST_GRID_LINES,
3601                                   MF_BYCOMMAND | (Settings.bShowGrid ? MF_CHECKED : MF_UNCHECKED));
3602 
3603                     ListView_SetExtendedListViewStyleEx(hwndListView, LVS_EX_GRIDLINES, (Settings.bShowGrid ? LVS_EX_GRIDLINES : 0));
3604                     break;
3605                 }
3606 
3607                 case IDM_SAVE_SETTINGS:
3608                 {
3609                     Settings.bSaveSettings = !Settings.bSaveSettings;
3610                     CheckMenuItem(hMainMenu, IDM_SAVE_SETTINGS,
3611                                   MF_BYCOMMAND | (Settings.bSaveSettings ? MF_CHECKED : MF_UNCHECKED));
3612                     break;
3613                 }
3614 
3615                 case IDM_ABOUT:
3616                 {
3617                     HICON hIcon;
3618                     WCHAR szCopyright[MAX_LOADSTRING];
3619 
3620                     hIcon = LoadIconW(hInst, MAKEINTRESOURCEW(IDI_EVENTVWR));
3621                     LoadStringW(hInst, IDS_COPYRIGHT, szCopyright, ARRAYSIZE(szCopyright));
3622                     ShellAboutW(hWnd, szTitle, szCopyright, hIcon);
3623                     DeleteObject(hIcon);
3624                     break;
3625                 }
3626 
3627                 case IDM_HELP:
3628                     MessageBoxW(hwndMainWindow,
3629                                 L"Help not implemented yet!",
3630                                 L"Event Log",
3631                                 MB_OK | MB_ICONINFORMATION);
3632                                 break;
3633 
3634                 case IDM_EXIT:
3635                     DestroyWindow(hWnd);
3636                     break;
3637 
3638                 default:
3639                     return DefWindowProcW(hWnd, uMsg, wParam, lParam);
3640             }
3641             break;
3642         }
3643 
3644         case WM_INITMENU:
3645         {
3646             if ((HMENU)wParam != hMainMenu)
3647                 break;
3648 
3649             CheckMenuRadioItem(hMainMenu, IDM_LIST_NEWEST, IDM_LIST_OLDEST,
3650                                Settings.bNewestEventsFirst ? IDM_LIST_NEWEST : IDM_LIST_OLDEST,
3651                                MF_BYCOMMAND);
3652 
3653             if (!hwndEventDetails)
3654             {
3655                 EnableMenuItem(hMainMenu, IDM_EVENT_DETAILS_VIEW,
3656                                MF_BYCOMMAND | MF_GRAYED);
3657             }
3658             CheckMenuItem(hMainMenu, IDM_EVENT_DETAILS_VIEW,
3659                           MF_BYCOMMAND | (Settings.bShowDetailsPane ? MF_CHECKED : MF_UNCHECKED));
3660 
3661             CheckMenuItem(hMainMenu, IDM_LIST_GRID_LINES,
3662                           MF_BYCOMMAND | (Settings.bShowGrid ? MF_CHECKED : MF_UNCHECKED));
3663 
3664             CheckMenuItem(hMainMenu, IDM_SAVE_SETTINGS,
3665                           MF_BYCOMMAND | (Settings.bSaveSettings ? MF_CHECKED : MF_UNCHECKED));
3666 
3667             break;
3668         }
3669 
3670 #if 0
3671         case WM_INITMENUPOPUP:
3672             lParam = lParam;
3673             break;
3674 
3675         case WM_CONTEXTMENU:
3676             lParam = lParam;
3677             break;
3678 #endif
3679 
3680         case WM_SETCURSOR:
3681         {
3682             POINT pt;
3683 
3684             if (LOWORD(lParam) != HTCLIENT)
3685                 goto Default;
3686 
3687             GetCursorPos(&pt);
3688             ScreenToClient(hWnd, &pt);
3689 
3690             /* Set the cursor for the vertical splitter */
3691             if (pt.x >= nVSplitPos - SPLIT_WIDTH/2 && pt.x < nVSplitPos + SPLIT_WIDTH/2 + 1)
3692             {
3693                 RECT rs;
3694                 GetClientRect(hWnd, &rect);
3695                 GetWindowRect(hwndStatus, &rs);
3696                 if (pt.y >= rect.top && pt.y < rect.bottom - (rs.bottom - rs.top))
3697                 {
3698                     SetCursor(LoadCursorW(NULL, IDC_SIZEWE));
3699                     return TRUE;
3700                 }
3701             }
3702             else
3703             /* Set the cursor for the horizontal splitter, if the Event details pane is displayed */
3704             if (hwndEventDetails && Settings.bShowDetailsPane &&
3705                 (pt.y >= nHSplitPos - SPLIT_WIDTH/2 && pt.y < nHSplitPos + SPLIT_WIDTH/2 + 1))
3706             {
3707                 // RECT rs;
3708                 GetClientRect(hWnd, &rect);
3709                 // GetWindowRect(hwndStatus, &rs);
3710                 if (pt.x >= nVSplitPos + SPLIT_WIDTH/2 + 1 /* rect.left + (rs.bottom - rs.top) */ &&
3711                     pt.x < rect.right)
3712                 {
3713                     SetCursor(LoadCursorW(NULL, IDC_SIZENS));
3714                     return TRUE;
3715                 }
3716             }
3717             goto Default;
3718         }
3719 
3720         case WM_LBUTTONDOWN:
3721         {
3722             INT x = GET_X_LPARAM(lParam);
3723             INT y = GET_Y_LPARAM(lParam);
3724 
3725             /* Reset the splitter state */
3726             bSplit = 0;
3727 
3728             /* Capture the cursor for the vertical splitter */
3729             if (x >= nVSplitPos - SPLIT_WIDTH/2 && x < nVSplitPos + SPLIT_WIDTH/2 + 1)
3730             {
3731                 bSplit = 1;
3732                 SetCapture(hWnd);
3733             }
3734             else
3735             /* Capture the cursor for the horizontal splitter, if the Event details pane is displayed */
3736             if (hwndEventDetails && Settings.bShowDetailsPane &&
3737                 (y >= nHSplitPos - SPLIT_WIDTH/2 && y < nHSplitPos + SPLIT_WIDTH/2 + 1))
3738             {
3739                 bSplit = 2;
3740                 SetCapture(hWnd);
3741             }
3742             break;
3743         }
3744 
3745         case WM_LBUTTONUP:
3746         case WM_RBUTTONDOWN:
3747         {
3748             if (GetCapture() != hWnd)
3749                 break;
3750 
3751             /* Adjust the correct splitter position */
3752             if (bSplit == 1)
3753                 nVSplitPos = GET_X_LPARAM(lParam);
3754             else if (bSplit == 2)
3755                 nHSplitPos = GET_Y_LPARAM(lParam);
3756 
3757             /* If we are splitting, resize the windows */
3758             if (bSplit != 0)
3759             {
3760                 GetClientRect(hWnd, &rect);
3761                 ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
3762             }
3763 
3764             /* Reset the splitter state */
3765             bSplit = 0;
3766 
3767             ReleaseCapture();
3768             break;
3769         }
3770 
3771         case WM_MOUSEMOVE:
3772         {
3773             if (GetCapture() != hWnd)
3774                 break;
3775 
3776             /* Move the correct splitter */
3777             if (bSplit == 1)
3778             {
3779                 INT x = GET_X_LPARAM(lParam);
3780 
3781                 GetClientRect(hWnd, &rect);
3782 
3783                 x = min(max(x, SPLIT_WIDTH/2), rect.right - rect.left - SPLIT_WIDTH/2);
3784                 if (nVSplitPos != x)
3785                 {
3786                     nVSplitPos = x;
3787                     ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
3788                 }
3789             }
3790             else if (bSplit == 2)
3791             {
3792                 RECT rs;
3793                 INT y = GET_Y_LPARAM(lParam);
3794 
3795                 GetClientRect(hWnd, &rect);
3796                 GetWindowRect(hwndStatus, &rs);
3797 
3798                 y = min(max(y, SPLIT_WIDTH/2), rect.bottom - rect.top - SPLIT_WIDTH/2 - (rs.bottom - rs.top));
3799                 if (nHSplitPos != y)
3800                 {
3801                     nHSplitPos = y;
3802                     ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
3803                 }
3804             }
3805             break;
3806         }
3807 
3808         case WM_SIZE:
3809         {
3810             if (wParam != SIZE_MINIMIZED)
3811             {
3812                 SendMessageW(hwndStatus, WM_SIZE, 0, 0);
3813                 ResizeWnd(LOWORD(lParam), HIWORD(lParam));
3814                 break;
3815             }
3816             /* Fall through the default case */
3817         }
3818 
3819         default: Default:
3820             return DefWindowProcW(hWnd, uMsg, wParam, lParam);
3821     }
3822 
3823     return 0;
3824 }
3825 
3826 
3827 static
3828 VOID
3829 InitPropertiesDlg(HWND hDlg, PEVENTLOG EventLog)
3830 {
3831     LPWSTR lpLogName = EventLog->LogName;
3832 
3833     DWORD Result, dwType;
3834     DWORD dwMaxSize = 0, dwRetention = 0;
3835     BOOL Success;
3836     WIN32_FIND_DATAW FileInfo; // WIN32_FILE_ATTRIBUTE_DATA
3837     ULARGE_INTEGER FileSize;
3838     WCHAR wszBuf[MAX_PATH];
3839     WCHAR szTemp[MAX_LOADSTRING];
3840     LPWSTR FileName;
3841 
3842     HKEY hLogKey;
3843     WCHAR *KeyPath;
3844     DWORD cbData;
3845     SIZE_T cbKeyPath;
3846 
3847     if (EventLog->Permanent)
3848     {
3849 
3850     cbKeyPath = (wcslen(EVENTLOG_BASE_KEY) + wcslen(lpLogName) + 1) * sizeof(WCHAR);
3851     KeyPath = HeapAlloc(GetProcessHeap(), 0, cbKeyPath);
3852     if (!KeyPath)
3853     {
3854         ShowWin32Error(ERROR_NOT_ENOUGH_MEMORY);
3855         goto Quit;
3856     }
3857 
3858     StringCbCopyW(KeyPath, cbKeyPath, EVENTLOG_BASE_KEY);
3859     StringCbCatW(KeyPath, cbKeyPath, lpLogName);
3860 
3861     Result = RegOpenKeyExW(hkMachine, KeyPath, 0, KEY_QUERY_VALUE, &hLogKey);
3862     HeapFree(GetProcessHeap(), 0, KeyPath);
3863     if (Result != ERROR_SUCCESS)
3864     {
3865         ShowWin32Error(Result);
3866         goto Quit;
3867     }
3868 
3869 
3870     cbData = sizeof(dwMaxSize);
3871     Result = RegQueryValueExW(hLogKey,
3872                               L"MaxSize",
3873                               NULL,
3874                               &dwType,
3875                               (LPBYTE)&dwMaxSize,
3876                               &cbData);
3877     if ((Result != ERROR_SUCCESS) || (dwType != REG_DWORD))
3878     {
3879         // dwMaxSize = 512 * 1024; /* 512 kBytes */
3880         /* Workstation: 512 KB; Server: 16384 KB */
3881         dwMaxSize = 16384 * 1024;
3882     }
3883     /* Convert in KB */
3884     dwMaxSize /= 1024;
3885 
3886     cbData = sizeof(dwRetention);
3887     Result = RegQueryValueExW(hLogKey,
3888                               L"Retention",
3889                               NULL,
3890                               &dwType,
3891                               (LPBYTE)&dwRetention,
3892                               &cbData);
3893     if ((Result != ERROR_SUCCESS) || (dwType != REG_DWORD))
3894     {
3895         /* By default, it is 604800 (secs) == 7 days. On Server, the retention is zeroed out. */
3896         dwRetention = 0;
3897     }
3898     /* Convert in days, rounded up */
3899     if (dwRetention != INFINITE)
3900         dwRetention = (dwRetention + 24*3600 - 1) / (24*3600);
3901 
3902     RegCloseKey(hLogKey);
3903 
3904     }
3905 
3906 Quit:
3907 
3908     SetDlgItemTextW(hDlg, IDC_DISPLAYNAME, lpLogName); // FIXME!
3909     SetDlgItemTextW(hDlg, IDC_LOGNAME, lpLogName);
3910 
3911     FileName = EventLog->FileName;
3912     if (FileName && *FileName)
3913     {
3914         /* Expand the file name. If the log file is on a remote computer, retrieve the network share form of the file name. */
3915         GetExpandedFilePathName(EventLog->ComputerName, FileName, wszBuf, ARRAYSIZE(wszBuf));
3916         FileName = wszBuf;
3917     }
3918     else
3919     {
3920         FileName = L"";
3921     }
3922     SetDlgItemTextW(hDlg, IDC_LOGFILE, FileName);
3923 
3924     if (FileName && *FileName)
3925     {
3926         /*
3927          * The general problem here (and in the shell as well) is that
3928          * GetFileAttributesEx fails for files that are opened without
3929          * shared access. To retrieve file information for those we need
3930          * to use something else: FindFirstFile, on the full file name.
3931          */
3932         Success = GetFileAttributesExW(FileName,
3933                                        GetFileExInfoStandard,
3934                                        (LPWIN32_FILE_ATTRIBUTE_DATA)&FileInfo);
3935         if (!Success)
3936         {
3937             HANDLE hFind = FindFirstFileW(FileName, &FileInfo);
3938             Success = (hFind != INVALID_HANDLE_VALUE);
3939             if (Success)
3940                 FindClose(hFind);
3941         }
3942     }
3943     else
3944     {
3945         Success = FALSE;
3946     }
3947 
3948     /* Starting there, FileName becomes invalid because we are reusing wszBuf */
3949 
3950     if (Success)
3951     {
3952         FileSize.u.LowPart = FileInfo.nFileSizeLow;
3953         FileSize.u.HighPart = FileInfo.nFileSizeHigh;
3954         if (FormatFileSizeWithBytes(&FileSize, wszBuf, ARRAYSIZE(wszBuf)))
3955             SetDlgItemTextW(hDlg, IDC_SIZE_LABEL, wszBuf);
3956 
3957         LoadStringW(hInst, IDS_NOT_AVAILABLE, szTemp, ARRAYSIZE(szTemp));
3958 
3959         if (GetFileTimeString(&FileInfo.ftCreationTime, wszBuf, ARRAYSIZE(wszBuf)))
3960             SetDlgItemTextW(hDlg, IDC_CREATED_LABEL, wszBuf);
3961         else
3962             SetDlgItemTextW(hDlg, IDC_CREATED_LABEL, szTemp);
3963 
3964         if (GetFileTimeString(&FileInfo.ftLastWriteTime, wszBuf, ARRAYSIZE(wszBuf)))
3965             SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, wszBuf);
3966         else
3967             SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, szTemp);
3968 
3969         if (GetFileTimeString(&FileInfo.ftLastAccessTime, wszBuf, ARRAYSIZE(wszBuf)))
3970             SetDlgItemTextW(hDlg, IDC_ACCESSED_LABEL, wszBuf);
3971         else
3972             SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, szTemp);
3973     }
3974     else
3975     {
3976         LoadStringW(hInst, IDS_NOT_AVAILABLE, szTemp, ARRAYSIZE(szTemp));
3977 
3978         SetDlgItemTextW(hDlg, IDC_SIZE_LABEL, szTemp);
3979         SetDlgItemTextW(hDlg, IDC_CREATED_LABEL, szTemp);
3980         SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, szTemp);
3981         SetDlgItemTextW(hDlg, IDC_ACCESSED_LABEL, szTemp);
3982     }
3983 
3984     if (EventLog->Permanent)
3985     {
3986         SendDlgItemMessageW(hDlg, IDC_UPDOWN_MAXLOGSIZE, UDM_SETRANGE32, (WPARAM)1, (LPARAM)0x3FFFC0);
3987         SendDlgItemMessageW(hDlg, IDC_UPDOWN_EVENTS_AGE, UDM_SETRANGE, 0, (LPARAM)MAKELONG(365, 1));
3988 
3989         SetDlgItemInt(hDlg, IDC_EDIT_MAXLOGSIZE, dwMaxSize, FALSE);
3990         SetDlgItemInt(hDlg, IDC_EDIT_EVENTS_AGE, (dwRetention == 0) ? 7 : dwRetention, FALSE);
3991 
3992         if (dwRetention == 0)
3993         {
3994             CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_OVERWRITE_AS_NEEDED);
3995             EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, FALSE);
3996             EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, FALSE);
3997         }
3998         else if (dwRetention == INFINITE)
3999         {
4000             CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_NO_OVERWRITE);
4001             EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, FALSE);
4002             EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, FALSE);
4003         }
4004         else
4005         {
4006             CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_OVERWRITE_OLDER_THAN);
4007             EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, TRUE);
4008             EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, TRUE);
4009         }
4010     }
4011     else
4012     {
4013         // TODO: Hide the unused controls! Or, just use another type of property sheet!
4014     }
4015 }
4016 
4017 static
4018 VOID
4019 SavePropertiesDlg(HWND hDlg, PEVENTLOG EventLog)
4020 {
4021     LPWSTR lpLogName = EventLog->LogName;
4022 
4023     LONG Result;
4024     DWORD dwMaxSize = 0, dwRetention = 0;
4025     HKEY hLogKey;
4026     WCHAR *KeyPath;
4027     SIZE_T cbKeyPath;
4028 
4029     if (!EventLog->Permanent)
4030         return;
4031 
4032     cbKeyPath = (wcslen(EVENTLOG_BASE_KEY) + wcslen(lpLogName) + 1) * sizeof(WCHAR);
4033     KeyPath = HeapAlloc(GetProcessHeap(), 0, cbKeyPath);
4034     if (!KeyPath)
4035     {
4036         ShowWin32Error(ERROR_NOT_ENOUGH_MEMORY);
4037         return;
4038     }
4039 
4040     StringCbCopyW(KeyPath, cbKeyPath, EVENTLOG_BASE_KEY);
4041     StringCbCatW(KeyPath, cbKeyPath, lpLogName);
4042 
4043     Result = RegOpenKeyExW(hkMachine, KeyPath, 0, KEY_SET_VALUE, &hLogKey);
4044     HeapFree(GetProcessHeap(), 0, KeyPath);
4045     if (Result != ERROR_SUCCESS)
4046     {
4047         ShowWin32Error(Result);
4048         return;
4049     }
4050 
4051     dwMaxSize = GetDlgItemInt(hDlg, IDC_EDIT_MAXLOGSIZE, NULL, FALSE) * 1024;
4052     RegSetValueExW(hLogKey,
4053                    L"MaxSize",
4054                    0,
4055                    REG_DWORD,
4056                    (LPBYTE)&dwMaxSize,
4057                    sizeof(dwMaxSize));
4058 
4059     if (IsDlgButtonChecked(hDlg, IDC_OVERWRITE_AS_NEEDED) == BST_CHECKED)
4060         dwRetention = 0;
4061     else if (IsDlgButtonChecked(hDlg, IDC_NO_OVERWRITE) == BST_CHECKED)
4062         dwRetention = INFINITE;
4063     else // if (IsDlgButtonChecked(hDlg, IDC_OVERWRITE_OLDER_THAN) == BST_CHECKED)
4064         dwRetention = GetDlgItemInt(hDlg, IDC_EDIT_EVENTS_AGE, NULL, FALSE) * (24*3600);
4065 
4066     RegSetValueExW(hLogKey,
4067                    L"Retention",
4068                    0,
4069                    REG_DWORD,
4070                    (LPBYTE)&dwRetention,
4071                    sizeof(dwRetention));
4072 
4073     RegCloseKey(hLogKey);
4074 }
4075 
4076 /* Message handler for EventLog Properties dialog */
4077 INT_PTR CALLBACK
4078 EventLogPropProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
4079 {
4080     PEVENTLOG EventLog;
4081     WCHAR szText[MAX_LOADSTRING];
4082 
4083     EventLog = (PEVENTLOG)GetWindowLongPtrW(hDlg, DWLP_USER);
4084 
4085     switch (uMsg)
4086     {
4087         case WM_INITDIALOG:
4088         {
4089             EventLog = (PEVENTLOG)((LPPROPSHEETPAGE)lParam)->lParam;
4090             SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)EventLog);
4091 
4092             InitPropertiesDlg(hDlg, EventLog);
4093 
4094             PropSheet_UnChanged(GetParent(hDlg), hDlg);
4095             return (INT_PTR)TRUE;
4096         }
4097 
4098         case WM_DESTROY:
4099             return (INT_PTR)TRUE;
4100 
4101         case WM_NOTIFY:
4102             switch (((LPNMHDR)lParam)->code)
4103             {
4104                 case PSN_APPLY:
4105                     PropSheet_UnChanged(GetParent(hDlg), hDlg);
4106                     SavePropertiesDlg(hDlg, EventLog);
4107                     return (INT_PTR)TRUE;
4108             }
4109             break;
4110 
4111         case WM_COMMAND:
4112             switch (LOWORD(wParam))
4113             {
4114                 case IDOK:
4115                 case IDCANCEL:
4116                     EndDialog(hDlg, LOWORD(wParam));
4117                     return (INT_PTR)TRUE;
4118 
4119                 case ID_CLEARLOG:
4120                 {
4121                     PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(NULL);
4122                     if (EventLogFilter && ClearEvents(EventLogFilter))
4123                     {
4124                         Refresh(EventLogFilter);
4125                         InitPropertiesDlg(hDlg, EventLog);
4126                     }
4127                     return (INT_PTR)TRUE;
4128                 }
4129 
4130                 case IDC_EDIT_EVENTS_AGE:
4131                 case IDC_EDIT_MAXLOGSIZE:
4132                     if (HIWORD(wParam) == EN_CHANGE)
4133                     {
4134                         PropSheet_Changed(GetParent(hDlg), hDlg);
4135                     }
4136                     return (INT_PTR)TRUE;
4137 
4138                 case IDC_OVERWRITE_AS_NEEDED:
4139                 {
4140                     CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_OVERWRITE_AS_NEEDED);
4141                     EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, FALSE);
4142                     EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, FALSE);
4143                     PropSheet_Changed(GetParent(hDlg), hDlg);
4144                     return (INT_PTR)TRUE;
4145                 }
4146 
4147                 case IDC_OVERWRITE_OLDER_THAN:
4148                 {
4149                     CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_OVERWRITE_OLDER_THAN);
4150                     EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, TRUE);
4151                     EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, TRUE);
4152                     PropSheet_Changed(GetParent(hDlg), hDlg);
4153                     return (INT_PTR)TRUE;
4154                 }
4155 
4156                 case IDC_NO_OVERWRITE:
4157                 {
4158                     CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_NO_OVERWRITE);
4159                     EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, FALSE);
4160                     EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, FALSE);
4161                     PropSheet_Changed(GetParent(hDlg), hDlg);
4162                     return (INT_PTR)TRUE;
4163                 }
4164 
4165                 case IDC_RESTOREDEFAULTS:
4166                 {
4167                     LoadStringW(hInst, IDS_RESTOREDEFAULTS, szText, _countof(szText));
4168 
4169                     if (MessageBoxW(hDlg, szText, szTitle, MB_YESNO | MB_ICONQUESTION) == IDYES)
4170                     {
4171                         CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_OVERWRITE_AS_NEEDED);
4172                         /* Workstation: 512 KB; Server: 16384 KB */
4173                         SetDlgItemInt(hDlg, IDC_EDIT_MAXLOGSIZE, 5120, FALSE);
4174                         SetDlgItemInt(hDlg, IDC_EDIT_EVENTS_AGE, 7, FALSE);
4175                         EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, FALSE);
4176                         EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, FALSE);
4177                         PropSheet_Changed(GetParent(hDlg), hDlg);
4178                     }
4179                     return (INT_PTR)TRUE;
4180                 }
4181 
4182                 case IDHELP:
4183                     MessageBoxW(hDlg,
4184                                 L"Help not implemented yet!",
4185                                 L"Event Log",
4186                                 MB_OK | MB_ICONINFORMATION);
4187                     return (INT_PTR)TRUE;
4188 
4189                 default:
4190                     break;
4191             }
4192             break;
4193     }
4194 
4195     return (INT_PTR)FALSE;
4196 }
4197 
4198 INT_PTR
4199 EventLogProperties(HINSTANCE hInstance, HWND hWndParent, PEVENTLOGFILTER EventLogFilter)
4200 {
4201     INT_PTR ret = 0;
4202     PROPSHEETHEADERW psh;
4203     PROPSHEETPAGEW   psp[1]; // 2
4204 
4205     /*
4206      * Bail out if there is no available filter, or if the filter
4207      * contains more than one log.
4208      */
4209     if (!EventLogFilter)
4210         return 0;
4211 
4212     EventLogFilter_AddRef(EventLogFilter);
4213 
4214     if (EventLogFilter->NumOfEventLogs > 1 ||
4215         EventLogFilter->EventLogs[0] == NULL)
4216     {
4217         goto Quit;
4218     }
4219 
4220     /* Header */
4221     psh.dwSize      = sizeof(psh);
4222     psh.dwFlags     = PSH_PROPSHEETPAGE /*| PSH_USEICONID */ | PSH_PROPTITLE | PSH_HASHELP /*| PSH_NOCONTEXTHELP */ /*| PSH_USECALLBACK */;
4223     psh.hInstance   = hInstance;
4224     psh.hwndParent  = hWndParent;
4225     // psh.pszIcon     = MAKEINTRESOURCEW(IDI_APPICON); // Disabled because it only sets the small icon; the big icon is a stretched version of the small one.
4226     psh.pszCaption  = EventLogFilter->EventLogs[0]->LogName;
4227     psh.nStartPage  = 0;
4228     psh.ppsp        = psp;
4229     psh.nPages      = ARRAYSIZE(psp);
4230     // psh.pfnCallback = PropSheetCallback;
4231 
4232     /* Log properties page */
4233     psp[0].dwSize      = sizeof(psp[0]);
4234     psp[0].dwFlags     = PSP_HASHELP;
4235     psp[0].hInstance   = hInstance;
4236     psp[0].pszTemplate = MAKEINTRESOURCEW(IDD_LOGPROPERTIES_GENERAL);
4237     psp[0].pfnDlgProc  = EventLogPropProc;
4238     psp[0].lParam      = (LPARAM)EventLogFilter->EventLogs[0];
4239 
4240 #if 0
4241     /* TODO: Log sources page */
4242     psp[1].dwSize      = sizeof(psp[1]);
4243     psp[1].dwFlags     = PSP_HASHELP;
4244     psp[1].hInstance   = hInstance;
4245     psp[1].pszTemplate = MAKEINTRESOURCEW(IDD_GENERAL_PAGE);
4246     psp[1].pfnDlgProc  = GeneralPageWndProc;
4247     psp[1].lParam      = (LPARAM)EventLogFilter->EventLogs[0];
4248 #endif
4249 
4250     /* Create the property sheet */
4251     ret = PropertySheetW(&psh);
4252 
4253 Quit:
4254     EventLogFilter_Release(EventLogFilter);
4255     return ret;
4256 }
4257 
4258 /* Message handler for Event Details dialog */
4259 static HWND hWndDetailsCtrl = NULL; // May go into the DWLP_USER
4260 static HWND hWndGrip = NULL;
4261 static INT cxMin, cyMin;    // In window coordinates
4262 static INT cxOld, cyOld;    // In client coordinates
4263 
4264 INT_PTR CALLBACK
4265 EventDetails(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
4266 {
4267     switch (uMsg)
4268     {
4269         case WM_INITDIALOG:
4270         {
4271             LONG_PTR dwStyle;
4272             INT sbVXSize, sbHYSize;
4273             RECT rcWnd, rect;
4274 
4275             hWndDetailsCtrl = CreateEventDetailsCtrl(hInst, hDlg, lParam);
4276             if (!hWndDetailsCtrl)
4277             {
4278                 EndDialog(hDlg, 0);
4279                 return (INT_PTR)TRUE;
4280             }
4281 
4282             /* Create a size grip if the dialog has a sizing border */
4283             GetClientRect(hDlg, &rcWnd);
4284             dwStyle  = GetWindowLongPtrW(hDlg, GWL_STYLE);
4285             sbVXSize = GetSystemMetrics(SM_CXVSCROLL);
4286             sbHYSize = GetSystemMetrics(SM_CYHSCROLL);
4287             if (dwStyle & WS_THICKFRAME /* == WS_SIZEBOX */)
4288             {
4289                 hWndGrip = CreateWindowW(WC_SCROLLBARW,
4290                                          NULL,
4291                                          WS_CHILD | WS_VISIBLE | /**/ WS_CLIPSIBLINGS | /**/ SBS_SIZEGRIP | SBS_SIZEBOXBOTTOMRIGHTALIGN,
4292                                          rcWnd.right - sbVXSize,
4293                                          rcWnd.bottom - sbHYSize,
4294                                          sbVXSize, sbHYSize,
4295                                          hDlg,
4296                                          NULL,
4297                                          hInst,
4298                                          NULL);
4299             }
4300 
4301             // SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)hWndDetailsCtrl);
4302 
4303             /*
4304              * Compute the minimum window size (in window coordinates) by
4305              * adding the widths/heights of the "Help" and "Close" buttons,
4306              * together with the margins, and add some minimal spacing
4307              * between the buttons.
4308              */
4309             GetWindowRect(hDlg, &rcWnd);
4310             cxMin = cyMin = 0;
4311 
4312             GetWindowRect(GetDlgItem(hDlg, IDHELP), &rect);
4313             cxMin += (rect.right - rect.left) + (rect.left - rcWnd.left); // == (rect.right - rcWnd.left);
4314             cyMin += (rect.bottom - rect.top) + (rcWnd.bottom - rect.bottom); // == (rcWnd.bottom - rect.top);
4315 
4316             GetWindowRect(GetDlgItem(hDlg, IDOK), &rect);
4317             cxMin += (rect.right - rect.left) + (rcWnd.right - rect.right); // == (rcWnd.right - rect.left);
4318             cyMin += (rect.bottom - rect.top) + (rcWnd.bottom - rect.bottom); // == (rcWnd.bottom - rect.top);
4319 
4320             /*
4321              * Convert the window rect from window to client coordinates
4322              * in order to retrieve the sizes of the left and top margins,
4323              * and add some extra space.
4324              */
4325             MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rcWnd, sizeof(RECT)/sizeof(POINT));
4326 
4327             cxMin += -2*rcWnd.left;   // Minimal spacing between the buttons == 2 * left margin
4328             cyMin += -rcWnd.top + 12; // Add some space on top
4329 
4330             GetClientRect(hDlg, &rcWnd);
4331             cxOld = rcWnd.right - rcWnd.left;
4332             cyOld = rcWnd.bottom - rcWnd.top;
4333 
4334             /* Show event info on dialog control */
4335             SendMessageW(hWndDetailsCtrl, EVT_DISPLAY, 0, 0);
4336 
4337             // SetWindowPos(hWndDetailsCtrl, NULL,
4338                          // 0, 0,
4339                          // (rcWnd.right - rcWnd.left),
4340                          // (rcWnd.bottom - rcWnd.top),
4341                          // SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
4342 
4343             /*
4344              * Hide the placeholder static control and show the event details
4345              * control instead. Note that the placeholder is here so far just
4346              * to get the dimensions right in the dialog resource editor.
4347              * I plan to remove it and use a custom control with a suitable
4348              * window class for it, that would create the event details control
4349              * instead.
4350              */
4351             ShowWindow(GetDlgItem(hDlg, IDC_STATIC), SW_HIDE);
4352             ShowWindow(hWndDetailsCtrl, SW_SHOW);
4353             return (INT_PTR)TRUE;
4354         }
4355 
4356         case WM_DESTROY:
4357             if (IsWindow(hWndDetailsCtrl))
4358                 DestroyWindow(hWndDetailsCtrl);
4359             hWndDetailsCtrl = NULL;
4360             return (INT_PTR)TRUE;
4361 
4362         case WM_COMMAND:
4363             switch (LOWORD(wParam))
4364             {
4365                 case IDOK:
4366                 case IDCANCEL:
4367                     EndDialog(hDlg, LOWORD(wParam));
4368                     return (INT_PTR)TRUE;
4369 
4370                 case IDHELP:
4371                     MessageBoxW(hDlg,
4372                                 L"Help not implemented yet!",
4373                                 L"Event Log",
4374                                 MB_OK | MB_ICONINFORMATION);
4375                     return (INT_PTR)TRUE;
4376 
4377                 default:
4378                     break;
4379             }
4380             break;
4381 
4382         case WM_SETCURSOR:
4383             if (((HWND)wParam == hWndGrip) && (LOWORD(lParam) == HTCLIENT))
4384             {
4385                 SetCursor(LoadCursorW(NULL, IDC_SIZENWSE));
4386                 SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, TRUE);
4387                 return (INT_PTR)TRUE;
4388             }
4389             break;
4390 
4391         case WM_SIZING:
4392         {
4393             /* Forbid resizing the dialog smaller than its minimal size */
4394             PRECT dragRect = (PRECT)lParam;
4395 
4396             if ((wParam == WMSZ_LEFT) || (wParam == WMSZ_TOPLEFT) || (wParam == WMSZ_BOTTOMLEFT))
4397             {
4398                 if (dragRect->right - dragRect->left < cxMin)
4399                     dragRect->left = dragRect->right - cxMin;
4400             }
4401 
4402             if ((wParam == WMSZ_RIGHT) || (wParam == WMSZ_TOPRIGHT) || (wParam == WMSZ_BOTTOMRIGHT))
4403             {
4404                 if (dragRect->right - dragRect->left < cxMin)
4405                     dragRect->right = dragRect->left + cxMin;
4406             }
4407 
4408             if ((wParam == WMSZ_TOP) || (wParam == WMSZ_TOPLEFT) || (wParam == WMSZ_TOPRIGHT))
4409             {
4410                 if (dragRect->bottom - dragRect->top < cyMin)
4411                     dragRect->top = dragRect->bottom - cyMin;
4412             }
4413 
4414             if ((wParam == WMSZ_BOTTOM) || (wParam == WMSZ_BOTTOMLEFT) || (wParam == WMSZ_BOTTOMRIGHT))
4415             {
4416                 if (dragRect->bottom - dragRect->top < cyMin)
4417                     dragRect->bottom = dragRect->top + cyMin;
4418             }
4419 
4420             SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, TRUE);
4421             return (INT_PTR)TRUE;
4422         }
4423 
4424         case WM_SIZE:
4425         {
4426             INT cx = LOWORD(lParam);
4427             INT cy = HIWORD(lParam);
4428 
4429             HDWP hdwp;
4430             HWND hItemWnd;
4431             RECT rect;
4432 
4433             hdwp = BeginDeferWindowPos(4);
4434 
4435             /* Resize the event details control window */
4436 
4437             hItemWnd = hWndDetailsCtrl;
4438             GetWindowRect(hItemWnd, &rect);
4439             MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
4440 
4441             if (hdwp)
4442                 hdwp = DeferWindowPos(hdwp,
4443                                       hItemWnd,
4444                                       HWND_TOP,
4445                                       0, 0,
4446                                       (rect.right - rect.left) + (cx - cxOld),
4447                                       (rect.bottom - rect.top) + (cy - cyOld),
4448                                       SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
4449 
4450             /* Move the buttons */
4451 
4452             hItemWnd = GetDlgItem(hDlg, IDHELP);
4453             GetWindowRect(hItemWnd, &rect);
4454             MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
4455 
4456             if (hdwp)
4457                 hdwp = DeferWindowPos(hdwp,
4458                                       hItemWnd,
4459                                       HWND_TOP,
4460                                       rect.left,
4461                                       rect.top + (cy - cyOld),
4462                                       0, 0,
4463                                       SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
4464 
4465             hItemWnd = GetDlgItem(hDlg, IDOK);
4466             GetWindowRect(hItemWnd, &rect);
4467             MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
4468 
4469             if (hdwp)
4470                 hdwp = DeferWindowPos(hdwp,
4471                                       hItemWnd,
4472                                       HWND_TOP,
4473                                       rect.left + (cx - cxOld),
4474                                       rect.top  + (cy - cyOld),
4475                                       0, 0,
4476                                       SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
4477 
4478             /* Move the size grip */
4479             if (hWndGrip && hdwp)
4480             {
4481                 GetWindowRect(hWndGrip, &rect);
4482                 MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
4483 
4484                 hdwp = DeferWindowPos(hdwp,
4485                                       hWndGrip,
4486                                       HWND_TOP,
4487                                       rect.left + (cx - cxOld),
4488                                       rect.top  + (cy - cyOld),
4489                                       0, 0,
4490                                       SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
4491             }
4492 
4493             if (hdwp)
4494                 EndDeferWindowPos(hdwp);
4495 
4496             /* Hide the size grip if we are in maximized mode */
4497             if (hWndGrip)
4498                 ShowWindow(hWndGrip, (wParam == SIZE_MAXIMIZED) ? SW_HIDE : SW_SHOW);
4499 
4500             cxOld = cx;
4501             cyOld = cy;
4502 
4503             SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, 0);
4504             return (INT_PTR)TRUE;
4505         }
4506     }
4507 
4508     return (INT_PTR)FALSE;
4509 }
4510