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         return FALSE;
2742 
2743     StringCbCopyW(KeyPath, cbKeyPath, EVENTLOG_BASE_KEY);
2744     StringCbCatW(KeyPath, cbKeyPath, lpLogName);
2745 
2746     Result = RegOpenKeyExW(hkMachine, KeyPath, 0, KEY_QUERY_VALUE, &hLogKey);
2747     HeapFree(GetProcessHeap(), 0, KeyPath);
2748     if (Result != ERROR_SUCCESS)
2749         return FALSE;
2750 
2751     cbData = sizeof(szModuleName);
2752     Result = RegQueryValueExW(hLogKey,
2753                               L"DisplayNameFile",
2754                               NULL,
2755                               &dwType,
2756                               (LPBYTE)szModuleName,
2757                               &cbData);
2758     if ((Result != ERROR_SUCCESS) || (dwType != REG_EXPAND_SZ && dwType != REG_SZ))
2759     {
2760         szModuleName[0] = UNICODE_NULL;
2761     }
2762     else
2763     {
2764         /* NULL-terminate the string and expand it */
2765         szModuleName[cbData / sizeof(WCHAR) - 1] = UNICODE_NULL;
2766         GetExpandedFilePathName(lpComputerName, szModuleName, lpModuleName, ARRAYSIZE(szModuleName));
2767         Success = TRUE;
2768     }
2769 
2770     /*
2771      * If we have a 'DisplayNameFile', query for 'DisplayNameID';
2772      * otherwise it's not really useful. 'DisplayNameID' is optional.
2773      */
2774     if (Success)
2775     {
2776         cbData = sizeof(dwMessageID);
2777         Result = RegQueryValueExW(hLogKey,
2778                                   L"DisplayNameID",
2779                                   NULL,
2780                                   &dwType,
2781                                   (LPBYTE)&dwMessageID,
2782                                   &cbData);
2783         if ((Result != ERROR_SUCCESS) || (dwType != REG_DWORD))
2784             dwMessageID = 0;
2785 
2786         *pdwMessageID = dwMessageID;
2787     }
2788 
2789     RegCloseKey(hLogKey);
2790 
2791     return Success;
2792 }
2793 
2794 
2795 VOID
2796 BuildLogListAndFilterList(IN LPCWSTR lpComputerName)
2797 {
2798     LONG Result;
2799     HKEY hEventLogKey, hLogKey;
2800     DWORD dwNumLogs = 0;
2801     DWORD dwIndex, dwMaxKeyLength;
2802     DWORD dwType;
2803     PEVENTLOG EventLog;
2804     PEVENTLOGFILTER EventLogFilter;
2805     LPWSTR LogName = NULL;
2806     WCHAR szModuleName[MAX_PATH];
2807     DWORD lpcName;
2808     DWORD dwMessageID;
2809     LPWSTR lpDisplayName;
2810     HTREEITEM hRootNode = NULL, hItem = NULL, hItemDefault = NULL;
2811 
2812     if (hkMachine && hkMachine != HKEY_LOCAL_MACHINE)
2813     {
2814         /* We are connected to some other computer, close the old connection */
2815         RegCloseKey(hkMachine);
2816         hkMachine = NULL;
2817     }
2818     if (!lpComputerName || !*lpComputerName)
2819     {
2820         /* Use the local computer registry */
2821         hkMachine = HKEY_LOCAL_MACHINE;
2822     }
2823     else
2824     {
2825         /* Connect to the remote computer registry */
2826         Result = RegConnectRegistry(lpComputerName, HKEY_LOCAL_MACHINE, &hkMachine);
2827         if (Result != ERROR_SUCCESS)
2828         {
2829             /* Connection failed, display a message and bail out */
2830             hkMachine = NULL;
2831             ShowWin32Error(GetLastError());
2832             return;
2833         }
2834     }
2835 
2836     /* Open the EventLog key */
2837     Result = RegOpenKeyExW(hkMachine, EVENTLOG_BASE_KEY, 0, KEY_READ, &hEventLogKey);
2838     if (Result != ERROR_SUCCESS)
2839     {
2840         return;
2841     }
2842 
2843     /* Retrieve the number of event logs enumerated as registry keys */
2844     Result = RegQueryInfoKeyW(hEventLogKey, NULL, NULL, NULL, &dwNumLogs, &dwMaxKeyLength,
2845                               NULL, NULL, NULL, NULL, NULL, NULL);
2846     if (Result != ERROR_SUCCESS)
2847     {
2848         goto Quit;
2849     }
2850     if (!dwNumLogs)
2851         goto Quit;
2852 
2853     /* Take the NULL terminator into account */
2854     ++dwMaxKeyLength;
2855 
2856     /* Allocate the temporary buffer */
2857     LogName = HeapAlloc(GetProcessHeap(), 0, dwMaxKeyLength * sizeof(WCHAR));
2858     if (!LogName)
2859         goto Quit;
2860 
2861     /* Enumerate and retrieve each event log name */
2862     for (dwIndex = 0; dwIndex < dwNumLogs; dwIndex++)
2863     {
2864         lpcName = dwMaxKeyLength;
2865         Result = RegEnumKeyExW(hEventLogKey, dwIndex, LogName, &lpcName, NULL, NULL, NULL, NULL);
2866         if (Result != ERROR_SUCCESS)
2867             continue;
2868 
2869         /* Take the NULL terminator into account */
2870         ++lpcName;
2871 
2872         /* Allocate a new event log entry */
2873         EventLog = AllocEventLog(lpComputerName, LogName, TRUE);
2874         if (EventLog == NULL)
2875             continue;
2876 
2877         /* Allocate a new event log filter entry for this event log */
2878         EventLogFilter = AllocEventLogFilter(// LogName,
2879                                              TRUE, TRUE, TRUE, TRUE, TRUE,
2880                                              NULL, NULL, NULL,
2881                                              1, &EventLog);
2882         if (EventLogFilter == NULL)
2883         {
2884             EventLog_Free(EventLog);
2885             continue;
2886         }
2887 
2888         /* Add the event log and the filter into their lists */
2889         InsertTailList(&EventLogList, &EventLog->ListEntry);
2890         InsertTailList(&EventLogFilterList, &EventLogFilter->ListEntry);
2891 
2892         EventLog->FileName = NULL;
2893 
2894         /* Retrieve and cache the event log file */
2895         Result = RegOpenKeyExW(hEventLogKey,
2896                                LogName,
2897                                0,
2898                                KEY_QUERY_VALUE,
2899                                &hLogKey);
2900         if (Result == ERROR_SUCCESS)
2901         {
2902             lpcName = 0;
2903             Result = RegQueryValueExW(hLogKey,
2904                                       L"File",
2905                                       NULL,
2906                                       &dwType,
2907                                       NULL,
2908                                       &lpcName);
2909             if ((Result != ERROR_SUCCESS) || (dwType != REG_EXPAND_SZ && dwType != REG_SZ))
2910             {
2911                 // Windows' EventLog uses some kind of default value, we do not.
2912                 EventLog->FileName = NULL;
2913             }
2914             else
2915             {
2916                 lpcName = ROUND_DOWN(lpcName, sizeof(WCHAR));
2917                 EventLog->FileName = HeapAlloc(GetProcessHeap(), 0, lpcName);
2918                 if (EventLog->FileName)
2919                 {
2920                     Result = RegQueryValueExW(hLogKey,
2921                                               L"File",
2922                                               NULL,
2923                                               &dwType,
2924                                               (LPBYTE)EventLog->FileName,
2925                                               &lpcName);
2926                     if (Result != ERROR_SUCCESS)
2927                     {
2928                         HeapFree(GetProcessHeap(), 0, EventLog->FileName);
2929                         EventLog->FileName = NULL;
2930                     }
2931                     else
2932                     {
2933                         EventLog->FileName[lpcName / sizeof(WCHAR) - 1] = UNICODE_NULL;
2934                     }
2935                 }
2936             }
2937 
2938             RegCloseKey(hLogKey);
2939         }
2940 
2941         /* Get the display name for the event log */
2942         lpDisplayName = NULL;
2943 
2944         ZeroMemory(szModuleName, sizeof(szModuleName));
2945         if (GetDisplayNameFileAndID(LogName, szModuleName, &dwMessageID))
2946         {
2947             /* Retrieve the message string without appending extra newlines */
2948             lpDisplayName =
2949             GetMessageStringFromDll(szModuleName,
2950                                     FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
2951                                     FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
2952                                     dwMessageID,
2953                                     0,
2954                                     NULL);
2955         }
2956 
2957         /*
2958          * Select the correct tree root node, whether the log is a System
2959          * or an Application log. Default to Application log otherwise.
2960          */
2961         hRootNode = htiAppLogs;
2962         for (lpcName = 0; lpcName < ARRAYSIZE(SystemLogs); ++lpcName)
2963         {
2964             /* Check whether the log name is part of the system logs */
2965             if (wcsicmp(LogName, SystemLogs[lpcName]) == 0)
2966             {
2967                 hRootNode = htiSystemLogs;
2968                 break;
2969             }
2970         }
2971 
2972         hItem = TreeViewAddItem(hwndTreeView, hRootNode,
2973                                 (lpDisplayName ? lpDisplayName : LogName),
2974                                 2, 3, (LPARAM)EventLogFilter);
2975 
2976         /* Try to get the default event log: "Application" */
2977         if ((hItemDefault == NULL) && (wcsicmp(LogName, SystemLogs[0]) == 0))
2978         {
2979             hItemDefault = hItem;
2980         }
2981 
2982         /* Free the buffer allocated by FormatMessage */
2983         if (lpDisplayName)
2984             LocalFree(lpDisplayName);
2985     }
2986 
2987     HeapFree(GetProcessHeap(), 0, LogName);
2988 
2989 Quit:
2990     RegCloseKey(hEventLogKey);
2991 
2992     /* Select the default event log */
2993     if (hItemDefault)
2994     {
2995         // TreeView_Expand(hwndTreeView, hRootNode, TVE_EXPAND);
2996         TreeView_SelectItem(hwndTreeView, hItemDefault);
2997         TreeView_EnsureVisible(hwndTreeView, hItemDefault);
2998     }
2999     InvalidateRect(hwndTreeView, NULL, FALSE);
3000     SetFocus(hwndTreeView);
3001 
3002     return;
3003 }
3004 
3005 VOID
3006 FreeLogList(VOID)
3007 {
3008     PLIST_ENTRY Entry;
3009     PEVENTLOG EventLog;
3010 
3011     while (!IsListEmpty(&EventLogList))
3012     {
3013         Entry = RemoveHeadList(&EventLogList);
3014         EventLog = (PEVENTLOG)CONTAINING_RECORD(Entry, EVENTLOG, ListEntry);
3015         EventLog_Free(EventLog);
3016     }
3017 
3018     return;
3019 }
3020 
3021 VOID
3022 FreeLogFilterList(VOID)
3023 {
3024     PLIST_ENTRY Entry;
3025     PEVENTLOGFILTER EventLogFilter;
3026 
3027     while (!IsListEmpty(&EventLogFilterList))
3028     {
3029         Entry = RemoveHeadList(&EventLogFilterList);
3030         EventLogFilter = (PEVENTLOGFILTER)CONTAINING_RECORD(Entry, EVENTLOGFILTER, ListEntry);
3031         EventLogFilter_Free(EventLogFilter);
3032     }
3033 
3034     ActiveFilter = NULL;
3035 
3036     return;
3037 }
3038 
3039 BOOL
3040 InitInstance(HINSTANCE hInstance)
3041 {
3042     RECT rcClient, rs;
3043     LONG StatusHeight;
3044     HIMAGELIST hSmall;
3045     LVCOLUMNW lvc = {0};
3046     WCHAR szTemp[256];
3047 
3048     /* Create the main window */
3049     rs = Settings.wpPos.rcNormalPosition;
3050     hwndMainWindow = CreateWindowW(EVENTVWR_WNDCLASS,
3051                                    szTitle,
3052                                    WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
3053                                    rs.left, rs.top,
3054                                    (rs.right  != CW_USEDEFAULT && rs.left != CW_USEDEFAULT) ? rs.right - rs.left : CW_USEDEFAULT,
3055                                    (rs.bottom != CW_USEDEFAULT && rs.top  != CW_USEDEFAULT) ? rs.bottom - rs.top : CW_USEDEFAULT,
3056                                    NULL,
3057                                    NULL,
3058                                    hInstance,
3059                                    NULL);
3060     if (!hwndMainWindow)
3061         return FALSE;
3062 
3063     /* Create the status bar */
3064     hwndStatus = CreateWindowExW(0,                                  // no extended styles
3065                                  STATUSCLASSNAMEW,                   // status bar
3066                                  L"",                                // no text
3067                                  WS_CHILD | WS_VISIBLE | CCS_BOTTOM | SBARS_SIZEGRIP, // styles
3068                                  0, 0, 0, 0,                         // x, y, cx, cy
3069                                  hwndMainWindow,                     // parent window
3070                                  (HMENU)100,                         // window ID
3071                                  hInstance,                          // instance
3072                                  NULL);                              // window data
3073 
3074     GetClientRect(hwndMainWindow, &rcClient);
3075     GetWindowRect(hwndStatus, &rs);
3076     StatusHeight = rs.bottom - rs.top;
3077 
3078     /* Create a progress bar in the status bar (hidden by default) */
3079     StatusBar_GetItemRect(hwndStatus, 0, &rs);
3080     hwndStatusProgress = CreateWindowExW(0,                          // no extended styles
3081                                          PROGRESS_CLASSW,            // status bar
3082                                          NULL,                       // no text
3083                                          WS_CHILD | PBS_SMOOTH,      // styles
3084                                          rs.left, rs.top,            // x, y
3085                                          rs.right - rs.left, rs.bottom - rs.top, // cx, cy
3086                                          hwndStatus,                 // parent window
3087                                          NULL,                       // window ID
3088                                          hInstance,                  // instance
3089                                          NULL);                      // window data
3090     /* Remove its static edge */
3091     SetWindowLongPtrW(hwndStatusProgress, GWL_EXSTYLE,
3092                       GetWindowLongPtrW(hwndStatusProgress, GWL_EXSTYLE) & ~WS_EX_STATICEDGE);
3093     ProgressBar_SetStep(hwndStatusProgress, 1);
3094 
3095     /* Initialize the splitter default positions */
3096     nVSplitPos = Settings.nVSplitPos;
3097     nHSplitPos = Settings.nHSplitPos;
3098 
3099     /* Create the TreeView */
3100     hwndTreeView = CreateWindowExW(WS_EX_CLIENTEDGE,
3101                                    WC_TREEVIEWW,
3102                                    NULL,
3103                                    // WS_CHILD | WS_VISIBLE | TVS_HASLINES | TVS_SHOWSELALWAYS,
3104                                    WS_CHILD | WS_VISIBLE | /* WS_TABSTOP | */ TVS_HASLINES | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_EDITLABELS | TVS_SHOWSELALWAYS,
3105                                    0, 0,
3106                                    nVSplitPos - SPLIT_WIDTH/2,
3107                                    (rcClient.bottom - rcClient.top) - StatusHeight,
3108                                    hwndMainWindow,
3109                                    NULL,
3110                                    hInstance,
3111                                    NULL);
3112 
3113     /* Create the ImageList */
3114     hSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
3115                               GetSystemMetrics(SM_CYSMICON),
3116                               ILC_COLOR32 | ILC_MASK, // ILC_COLOR24
3117                               1, 1);
3118 
3119     /* Add event type icons to the ImageList: closed/opened folder, event log (normal/viewed) */
3120     ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_CLOSED_CATEGORY)));
3121     ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_OPENED_CATEGORY)));
3122     ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_EVENTLOG)));
3123     ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_EVENTVWR)));
3124 
3125     /* Assign the ImageList to the Tree View */
3126     TreeView_SetImageList(hwndTreeView, hSmall, TVSIL_NORMAL);
3127 
3128     /* Add the event logs nodes */
3129     // "System Logs"
3130     LoadStringW(hInstance, IDS_EVENTLOG_SYSTEM, szTemp, ARRAYSIZE(szTemp));
3131     htiSystemLogs = TreeViewAddItem(hwndTreeView, NULL, szTemp, 0, 1, (LPARAM)NULL);
3132     // "Application Logs"
3133     LoadStringW(hInstance, IDS_EVENTLOG_APP, szTemp, ARRAYSIZE(szTemp));
3134     htiAppLogs = TreeViewAddItem(hwndTreeView, NULL, szTemp, 0, 1, (LPARAM)NULL);
3135     // "User Logs"
3136     LoadStringW(hInstance, IDS_EVENTLOG_USER, szTemp, ARRAYSIZE(szTemp));
3137     htiUserLogs = TreeViewAddItem(hwndTreeView, NULL, szTemp, 0, 1, (LPARAM)NULL);
3138 
3139     /* Create the Event details pane (optional) */
3140     hwndEventDetails = CreateEventDetailsCtrl(hInst, hwndMainWindow, (LPARAM)NULL);
3141     if (hwndEventDetails)
3142     {
3143     SetWindowLongPtrW(hwndEventDetails, GWL_EXSTYLE,
3144                       GetWindowLongPtrW(hwndEventDetails, GWL_EXSTYLE) | WS_EX_CLIENTEDGE);
3145     SetWindowPos(hwndEventDetails, NULL,
3146                  nVSplitPos + SPLIT_WIDTH/2,
3147                  nHSplitPos + SPLIT_WIDTH/2,
3148                  (rcClient.right - rcClient.left) - nVSplitPos - SPLIT_WIDTH/2,
3149                  (rcClient.bottom - rcClient.top) - nHSplitPos - SPLIT_WIDTH/2 - StatusHeight,
3150                  SWP_NOZORDER | SWP_NOACTIVATE | (Settings.bShowDetailsPane ? SWP_SHOWWINDOW : SWP_HIDEWINDOW));
3151     }
3152 
3153     /* Create the ListView */
3154     hwndListView = CreateWindowExW(WS_EX_CLIENTEDGE,
3155                                    WC_LISTVIEWW,
3156                                    NULL,
3157                                    WS_CHILD | WS_VISIBLE | LVS_SHOWSELALWAYS | LVS_REPORT,
3158                                    nVSplitPos + SPLIT_WIDTH/2,
3159                                    0,
3160                                    (rcClient.right - rcClient.left) - nVSplitPos - SPLIT_WIDTH/2,
3161                                    hwndEventDetails && Settings.bShowDetailsPane
3162                                        ? nHSplitPos - SPLIT_WIDTH/2
3163                                        : (rcClient.bottom - rcClient.top) - StatusHeight,
3164                                    hwndMainWindow,
3165                                    NULL,
3166                                    hInstance,
3167                                    NULL);
3168 
3169     /* Add the extended ListView styles */
3170     ListView_SetExtendedListViewStyle(hwndListView, LVS_EX_HEADERDRAGDROP | LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP | (Settings.bShowGrid ? LVS_EX_GRIDLINES : 0));
3171 
3172     /* Create the ImageList */
3173     hSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
3174                               GetSystemMetrics(SM_CYSMICON),
3175                               ILC_COLOR32 | ILC_MASK, // ILC_COLOR24
3176                               1, 1);
3177 
3178     /* Add event type icons to the ImageList */
3179     ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_INFORMATIONICON)));
3180     ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_WARNINGICON)));
3181     ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_ERRORICON)));
3182     ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_AUDITSUCCESSICON)));
3183     ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_AUDITFAILUREICON)));
3184 
3185     /* Assign the ImageList to the List View */
3186     ListView_SetImageList(hwndListView, hSmall, LVSIL_SMALL);
3187 
3188     /* Now set up the listview with its columns */
3189     lvc.mask = LVCF_TEXT | LVCF_WIDTH;
3190     lvc.cx = 90;
3191     LoadStringW(hInstance,
3192                 IDS_COLUMNTYPE,
3193                 szTemp,
3194                 ARRAYSIZE(szTemp));
3195     lvc.pszText = szTemp;
3196     ListView_InsertColumn(hwndListView, 0, &lvc);
3197 
3198     lvc.cx = 70;
3199     LoadStringW(hInstance,
3200                 IDS_COLUMNDATE,
3201                 szTemp,
3202                 ARRAYSIZE(szTemp));
3203     lvc.pszText = szTemp;
3204     ListView_InsertColumn(hwndListView, 1, &lvc);
3205 
3206     lvc.cx = 70;
3207     LoadStringW(hInstance,
3208                 IDS_COLUMNTIME,
3209                 szTemp,
3210                 ARRAYSIZE(szTemp));
3211     lvc.pszText = szTemp;
3212     ListView_InsertColumn(hwndListView, 2, &lvc);
3213 
3214     lvc.cx = 150;
3215     LoadStringW(hInstance,
3216                 IDS_COLUMNSOURCE,
3217                 szTemp,
3218                 ARRAYSIZE(szTemp));
3219     lvc.pszText = szTemp;
3220     ListView_InsertColumn(hwndListView, 3, &lvc);
3221 
3222     lvc.cx = 100;
3223     LoadStringW(hInstance,
3224                 IDS_COLUMNCATEGORY,
3225                 szTemp,
3226                 ARRAYSIZE(szTemp));
3227     lvc.pszText = szTemp;
3228     ListView_InsertColumn(hwndListView, 4, &lvc);
3229 
3230     lvc.cx = 60;
3231     LoadStringW(hInstance,
3232                 IDS_COLUMNEVENT,
3233                 szTemp,
3234                 ARRAYSIZE(szTemp));
3235     lvc.pszText = szTemp;
3236     ListView_InsertColumn(hwndListView, 5, &lvc);
3237 
3238     lvc.cx = 120;
3239     LoadStringW(hInstance,
3240                 IDS_COLUMNUSER,
3241                 szTemp,
3242                 ARRAYSIZE(szTemp));
3243     lvc.pszText = szTemp;
3244     ListView_InsertColumn(hwndListView, 6, &lvc);
3245 
3246     lvc.cx = 100;
3247     LoadStringW(hInstance,
3248                 IDS_COLUMNCOMPUTER,
3249                 szTemp,
3250                 ARRAYSIZE(szTemp));
3251     lvc.pszText = szTemp;
3252     ListView_InsertColumn(hwndListView, 7, &lvc);
3253 
3254     /* Initialize the save Dialog */
3255     ZeroMemory(&sfn, sizeof(sfn));
3256     ZeroMemory(szSaveFilter, sizeof(szSaveFilter));
3257 
3258     LoadStringW(hInst, IDS_SAVE_FILTER, szSaveFilter, ARRAYSIZE(szSaveFilter));
3259 
3260     sfn.lStructSize     = sizeof(sfn);
3261     sfn.hwndOwner       = hwndMainWindow;
3262     sfn.hInstance       = hInstance;
3263     sfn.lpstrFilter     = szSaveFilter;
3264     sfn.lpstrInitialDir = NULL;
3265     sfn.Flags           = OFN_HIDEREADONLY | OFN_SHAREAWARE;
3266     sfn.lpstrDefExt     = NULL;
3267 
3268     ShowWindow(hwndMainWindow, Settings.wpPos.showCmd);
3269     UpdateWindow(hwndMainWindow);
3270 
3271     return TRUE;
3272 }
3273 
3274 VOID ResizeWnd(INT cx, INT cy)
3275 {
3276     RECT rs;
3277     LONG StatusHeight;
3278     LONG_PTR dwExStyle;
3279     HDWP hdwp;
3280 
3281     /* Resize the status bar -- now done in WM_SIZE */
3282     // SendMessageW(hwndStatus, WM_SIZE, 0, 0);
3283     GetWindowRect(hwndStatus, &rs);
3284     StatusHeight = rs.bottom - rs.top;
3285 
3286     /*
3287      * Move the progress bar -- Take into account for extra size due to the static edge
3288      * (AdjustWindowRectEx() does not seem to work for the progress bar).
3289      */
3290     StatusBar_GetItemRect(hwndStatus, 0, &rs);
3291     dwExStyle = GetWindowLongPtrW(hwndStatusProgress, GWL_EXSTYLE);
3292     SetWindowLongPtrW(hwndStatusProgress, GWL_EXSTYLE, dwExStyle | WS_EX_STATICEDGE);
3293     MoveWindow(hwndStatusProgress,
3294                rs.left, rs.top, rs.right - rs.left, rs.bottom - rs.top,
3295                IsWindowVisible(hwndStatusProgress) ? TRUE : FALSE);
3296     SetWindowLongPtrW(hwndStatusProgress, GWL_EXSTYLE, dwExStyle);
3297 
3298     /*
3299      * TODO: Adjust the splitter positions:
3300      * - Vertical splitter (1)  : fixed position from the left window side.
3301      * - Horizontal splitter (2): fixed position from the bottom window side.
3302      */
3303     nVSplitPos = min(max(nVSplitPos, SPLIT_WIDTH/2), cx - SPLIT_WIDTH/2); // OK
3304     nHSplitPos = min(max(nHSplitPos, SPLIT_WIDTH/2), cy - SPLIT_WIDTH/2 - StatusHeight); // FIXME!
3305 
3306     hdwp = BeginDeferWindowPos(3);
3307 
3308     if (hdwp)
3309         hdwp = DeferWindowPos(hdwp,
3310                               hwndTreeView,
3311                               HWND_TOP,
3312                               0, 0,
3313                               nVSplitPos - SPLIT_WIDTH/2,
3314                               cy - StatusHeight,
3315                               SWP_NOZORDER | SWP_NOACTIVATE);
3316 
3317     if (hdwp)
3318         hdwp = DeferWindowPos(hdwp,
3319                               hwndListView,
3320                               HWND_TOP,
3321                               nVSplitPos + SPLIT_WIDTH/2, 0,
3322                               cx - nVSplitPos - SPLIT_WIDTH/2,
3323                               hwndEventDetails && Settings.bShowDetailsPane
3324                                   ? nHSplitPos - SPLIT_WIDTH/2
3325                                   : cy - StatusHeight,
3326                               SWP_NOZORDER | SWP_NOACTIVATE);
3327 
3328     if (hwndEventDetails && Settings.bShowDetailsPane && hdwp)
3329         hdwp = DeferWindowPos(hdwp,
3330                               hwndEventDetails,
3331                               HWND_TOP,
3332                               nVSplitPos + SPLIT_WIDTH/2,
3333                               nHSplitPos + SPLIT_WIDTH/2,
3334                               cx - nVSplitPos - SPLIT_WIDTH/2,
3335                               cy - nHSplitPos - SPLIT_WIDTH/2 - StatusHeight,
3336                               SWP_NOZORDER | SWP_NOACTIVATE);
3337 
3338     if (hdwp)
3339         EndDeferWindowPos(hdwp);
3340 }
3341 
3342 
3343 LRESULT CALLBACK
3344 WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3345 {
3346     RECT rect;
3347 
3348     switch (uMsg)
3349     {
3350         case WM_CREATE:
3351             hMainMenu = GetMenu(hWnd);
3352             break;
3353 
3354         case WM_DESTROY:
3355         {
3356             GetWindowPlacement(hwndMainWindow, &Settings.wpPos);
3357             PostQuitMessage(0);
3358             break;
3359         }
3360 
3361         case WM_NOTIFY:
3362         {
3363             LPNMHDR hdr = (LPNMHDR)lParam;
3364 
3365             if (hdr->hwndFrom == hwndListView)
3366             {
3367                 switch (hdr->code)
3368                 {
3369                     case LVN_ITEMCHANGED:
3370                     {
3371                         LPNMLISTVIEW pnmv = (LPNMLISTVIEW)lParam;
3372 
3373                         if ( (pnmv->uChanged  & LVIF_STATE) && /* The state has changed */
3374                              (pnmv->uNewState & LVIS_SELECTED) /* The item has been (de)selected */ )
3375                         {
3376                             if (hwndEventDetails)
3377                                 SendMessageW(hwndEventDetails, EVT_DISPLAY, 0, 0);
3378                         }
3379                         break;
3380                     }
3381 
3382                     case NM_DBLCLK:
3383                     case NM_RETURN:
3384                         SendMessageW(hWnd, WM_COMMAND, IDM_EVENT_DETAILS, 0);
3385                         break;
3386                 }
3387             }
3388             else if (hdr->hwndFrom == hwndTreeView)
3389             {
3390                 switch (hdr->code)
3391                 {
3392                     case TVN_BEGINLABELEDIT:
3393                     {
3394                         HTREEITEM hItem = ((LPNMTVDISPINFO)lParam)->item.hItem;
3395 
3396                         /* Disable label editing for root nodes */
3397                         return ((hItem == htiSystemLogs) ||
3398                                 (hItem == htiAppLogs)    ||
3399                                 (hItem == htiUserLogs));
3400                     }
3401 
3402                     case TVN_ENDLABELEDIT:
3403                     {
3404                         TVITEMW item = ((LPNMTVDISPINFO)lParam)->item;
3405                         HTREEITEM hItem = item.hItem;
3406 
3407                         /* Disable label editing for root nodes */
3408                         if ((hItem == htiSystemLogs) ||
3409                             (hItem == htiAppLogs)    ||
3410                             (hItem == htiUserLogs))
3411                         {
3412                             return FALSE;
3413                         }
3414 
3415                         if (item.pszText)
3416                         {
3417                             LPWSTR pszText = item.pszText;
3418 
3419                             /* Trim leading whitespace */
3420                             while (*pszText && iswspace(*pszText))
3421                                 ++pszText;
3422 
3423                             if (!*pszText)
3424                                 return FALSE;
3425 
3426                             return TRUE;
3427                         }
3428                         else
3429                         {
3430                             return FALSE;
3431                         }
3432                     }
3433 
3434                     case TVN_SELCHANGED:
3435                     {
3436                         PEVENTLOGFILTER EventLogFilter =
3437                             (PEVENTLOGFILTER)((LPNMTREEVIEW)lParam)->itemNew.lParam;
3438 
3439                         // FIXME: It might be nice to reference here the filter,
3440                         // so that we don't have to reference/dereference it many times
3441                         // in the other functions???
3442 
3443                         // FIXME: This is a hack!!
3444                         if (hwndEventDetails && EventLogFilter)
3445                         {
3446                             SendMessageW(hwndEventDetails, EVT_SETFILTER, 0, (LPARAM)EventLogFilter);
3447                         }
3448 
3449                         if (EventLogFilter)
3450                         {
3451                             /*
3452                              * If we have selected a filter, enable the menu commands;
3453                              * they will possibly be updated after events enumeration.
3454                              */
3455                             EnableMenuItem(hMainMenu, IDM_SAVE_EVENTLOG, MF_BYCOMMAND | MF_ENABLED);
3456                             EnableMenuItem(hMainMenu, IDM_CLOSE_EVENTLOG, MF_BYCOMMAND | MF_ENABLED);
3457                             EnableMenuItem(hMainMenu, IDM_CLEAR_EVENTS, MF_BYCOMMAND | MF_ENABLED);
3458                             EnableMenuItem(hMainMenu, IDM_RENAME_EVENTLOG, MF_BYCOMMAND | MF_ENABLED);
3459                             EnableMenuItem(hMainMenu, IDM_EVENTLOG_SETTINGS, MF_BYCOMMAND | MF_ENABLED);
3460                         }
3461                         else
3462                         {
3463                             EnableMenuItem(hMainMenu, IDM_SAVE_EVENTLOG, MF_BYCOMMAND | MF_GRAYED);
3464                             EnableMenuItem(hMainMenu, IDM_CLOSE_EVENTLOG, MF_BYCOMMAND | MF_GRAYED);
3465                             EnableMenuItem(hMainMenu, IDM_CLEAR_EVENTS, MF_BYCOMMAND | MF_GRAYED);
3466                             EnableMenuItem(hMainMenu, IDM_RENAME_EVENTLOG, MF_BYCOMMAND | MF_GRAYED);
3467                             EnableMenuItem(hMainMenu, IDM_EVENTLOG_SETTINGS, MF_BYCOMMAND | MF_GRAYED);
3468                         }
3469 
3470                         /*
3471                          * The enumeration thread that is triggered by EnumEvents
3472                          * will set a new value for the 'ActiveFilter'.
3473                          */
3474                         if (EventLogFilter)
3475                             EnumEvents(EventLogFilter);
3476 
3477                         break;
3478                     }
3479                 }
3480             }
3481             break;
3482         }
3483 
3484         case WM_COMMAND:
3485         {
3486             /* Parse the menu selections */
3487             switch (LOWORD(wParam))
3488             {
3489                 case IDM_OPEN_EVENTLOG:
3490                     OpenUserEventLog();
3491                     break;
3492 
3493                 case IDM_SAVE_EVENTLOG:
3494                     SaveEventLog(GetSelectedFilter(NULL));
3495                     break;
3496 
3497                 case IDM_CLOSE_EVENTLOG:
3498                 {
3499                     HTREEITEM hti;
3500                     PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(&hti);
3501                     CloseUserEventLog(EventLogFilter, hti);
3502                     break;
3503                 }
3504 
3505                 case IDM_CLEAR_EVENTS:
3506                 {
3507                     PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(NULL);
3508                     if (EventLogFilter && ClearEvents(EventLogFilter))
3509                         Refresh(EventLogFilter);
3510                     break;
3511                 }
3512 
3513                 case IDM_RENAME_EVENTLOG:
3514                     if (GetFocus() == hwndTreeView)
3515                         TreeView_EditLabel(hwndTreeView, TreeView_GetSelection(hwndTreeView));
3516                     break;
3517 
3518                 case IDM_EVENTLOG_SETTINGS:
3519                 {
3520                     PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(NULL);
3521                     // TODO: Check the returned value?
3522                     if (EventLogFilter)
3523                         EventLogProperties(hInst, hWnd, EventLogFilter);
3524                     break;
3525                 }
3526 
3527                 case IDM_LIST_NEWEST:
3528                 {
3529                     CheckMenuRadioItem(hMainMenu, IDM_LIST_NEWEST, IDM_LIST_OLDEST, IDM_LIST_NEWEST, MF_BYCOMMAND);
3530                     if (!Settings.bNewestEventsFirst)
3531                     {
3532                         Settings.bNewestEventsFirst = TRUE;
3533                         Refresh(GetSelectedFilter(NULL));
3534                     }
3535                     break;
3536                 }
3537 
3538                 case IDM_LIST_OLDEST:
3539                 {
3540                     CheckMenuRadioItem(hMainMenu, IDM_LIST_NEWEST, IDM_LIST_OLDEST, IDM_LIST_OLDEST, MF_BYCOMMAND);
3541                     if (Settings.bNewestEventsFirst)
3542                     {
3543                         Settings.bNewestEventsFirst = FALSE;
3544                         Refresh(GetSelectedFilter(NULL));
3545                     }
3546                     break;
3547                 }
3548 
3549                 case IDM_EVENT_DETAILS:
3550                 {
3551                     // LPNMITEMACTIVATE lpnmitem = (LPNMITEMACTIVATE)lParam;
3552                     PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(NULL);
3553                     if (/*lpnmitem->iItem != -1 &&*/ EventLogFilter)
3554                     {
3555                         EventLogFilter_AddRef(EventLogFilter);
3556                         DialogBoxParamW(hInst,
3557                                         MAKEINTRESOURCEW(IDD_EVENTDETAILS_DLG),
3558                                         hWnd,
3559                                         EventDetails,
3560                                         (LPARAM)EventLogFilter);
3561                         EventLogFilter_Release(EventLogFilter);
3562                     }
3563                     break;
3564                 }
3565 
3566                 case IDM_REFRESH:
3567                     Refresh(GetSelectedFilter(NULL));
3568                     break;
3569 
3570                 case IDM_EVENT_DETAILS_VIEW:
3571                 {
3572                     Settings.bShowDetailsPane = !Settings.bShowDetailsPane;
3573                     CheckMenuItem(hMainMenu, IDM_EVENT_DETAILS_VIEW,
3574                                   MF_BYCOMMAND | (Settings.bShowDetailsPane ? MF_CHECKED : MF_UNCHECKED));
3575 
3576                     GetClientRect(hWnd, &rect);
3577                     if (Settings.bShowDetailsPane)
3578                     {
3579                         ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
3580                         ShowWindow(hwndEventDetails, SW_SHOW);
3581                     }
3582                     else
3583                     {
3584                         ShowWindow(hwndEventDetails, SW_HIDE);
3585                         ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
3586                     }
3587 
3588                     break;
3589                 }
3590 
3591                 case IDM_LIST_GRID_LINES:
3592                 {
3593                     Settings.bShowGrid = !Settings.bShowGrid;
3594                     CheckMenuItem(hMainMenu, IDM_LIST_GRID_LINES,
3595                                   MF_BYCOMMAND | (Settings.bShowGrid ? MF_CHECKED : MF_UNCHECKED));
3596 
3597                     ListView_SetExtendedListViewStyleEx(hwndListView, LVS_EX_GRIDLINES, (Settings.bShowGrid ? LVS_EX_GRIDLINES : 0));
3598                     break;
3599                 }
3600 
3601                 case IDM_SAVE_SETTINGS:
3602                 {
3603                     Settings.bSaveSettings = !Settings.bSaveSettings;
3604                     CheckMenuItem(hMainMenu, IDM_SAVE_SETTINGS,
3605                                   MF_BYCOMMAND | (Settings.bSaveSettings ? MF_CHECKED : MF_UNCHECKED));
3606                     break;
3607                 }
3608 
3609                 case IDM_ABOUT:
3610                 {
3611                     HICON hIcon;
3612                     WCHAR szCopyright[MAX_LOADSTRING];
3613 
3614                     hIcon = LoadIconW(hInst, MAKEINTRESOURCEW(IDI_EVENTVWR));
3615                     LoadStringW(hInst, IDS_COPYRIGHT, szCopyright, ARRAYSIZE(szCopyright));
3616                     ShellAboutW(hWnd, szTitle, szCopyright, hIcon);
3617                     DeleteObject(hIcon);
3618                     break;
3619                 }
3620 
3621                 case IDM_HELP:
3622                     MessageBoxW(hwndMainWindow,
3623                                 L"Help not implemented yet!",
3624                                 L"Event Log",
3625                                 MB_OK | MB_ICONINFORMATION);
3626                                 break;
3627 
3628                 case IDM_EXIT:
3629                     DestroyWindow(hWnd);
3630                     break;
3631 
3632                 default:
3633                     return DefWindowProcW(hWnd, uMsg, wParam, lParam);
3634             }
3635             break;
3636         }
3637 
3638         case WM_INITMENU:
3639         {
3640             if ((HMENU)wParam != hMainMenu)
3641                 break;
3642 
3643             CheckMenuRadioItem(hMainMenu, IDM_LIST_NEWEST, IDM_LIST_OLDEST,
3644                                Settings.bNewestEventsFirst ? IDM_LIST_NEWEST : IDM_LIST_OLDEST,
3645                                MF_BYCOMMAND);
3646 
3647             if (!hwndEventDetails)
3648             {
3649                 EnableMenuItem(hMainMenu, IDM_EVENT_DETAILS_VIEW,
3650                                MF_BYCOMMAND | MF_GRAYED);
3651             }
3652             CheckMenuItem(hMainMenu, IDM_EVENT_DETAILS_VIEW,
3653                           MF_BYCOMMAND | (Settings.bShowDetailsPane ? MF_CHECKED : MF_UNCHECKED));
3654 
3655             CheckMenuItem(hMainMenu, IDM_LIST_GRID_LINES,
3656                           MF_BYCOMMAND | (Settings.bShowGrid ? MF_CHECKED : MF_UNCHECKED));
3657 
3658             CheckMenuItem(hMainMenu, IDM_SAVE_SETTINGS,
3659                           MF_BYCOMMAND | (Settings.bSaveSettings ? MF_CHECKED : MF_UNCHECKED));
3660 
3661             break;
3662         }
3663 
3664 #if 0
3665         case WM_INITMENUPOPUP:
3666             lParam = lParam;
3667             break;
3668 
3669         case WM_CONTEXTMENU:
3670             lParam = lParam;
3671             break;
3672 #endif
3673 
3674         case WM_SETCURSOR:
3675         {
3676             POINT pt;
3677 
3678             if (LOWORD(lParam) != HTCLIENT)
3679                 goto Default;
3680 
3681             GetCursorPos(&pt);
3682             ScreenToClient(hWnd, &pt);
3683 
3684             /* Set the cursor for the vertical splitter */
3685             if (pt.x >= nVSplitPos - SPLIT_WIDTH/2 && pt.x < nVSplitPos + SPLIT_WIDTH/2 + 1)
3686             {
3687                 RECT rs;
3688                 GetClientRect(hWnd, &rect);
3689                 GetWindowRect(hwndStatus, &rs);
3690                 if (pt.y >= rect.top && pt.y < rect.bottom - (rs.bottom - rs.top))
3691                 {
3692                     SetCursor(LoadCursorW(NULL, IDC_SIZEWE));
3693                     return TRUE;
3694                 }
3695             }
3696             else
3697             /* Set the cursor for the horizontal splitter, if the Event details pane is displayed */
3698             if (hwndEventDetails && Settings.bShowDetailsPane &&
3699                 (pt.y >= nHSplitPos - SPLIT_WIDTH/2 && pt.y < nHSplitPos + SPLIT_WIDTH/2 + 1))
3700             {
3701                 // RECT rs;
3702                 GetClientRect(hWnd, &rect);
3703                 // GetWindowRect(hwndStatus, &rs);
3704                 if (pt.x >= nVSplitPos + SPLIT_WIDTH/2 + 1 /* rect.left + (rs.bottom - rs.top) */ &&
3705                     pt.x < rect.right)
3706                 {
3707                     SetCursor(LoadCursorW(NULL, IDC_SIZENS));
3708                     return TRUE;
3709                 }
3710             }
3711             goto Default;
3712         }
3713 
3714         case WM_LBUTTONDOWN:
3715         {
3716             INT x = GET_X_LPARAM(lParam);
3717             INT y = GET_Y_LPARAM(lParam);
3718 
3719             /* Reset the splitter state */
3720             bSplit = 0;
3721 
3722             /* Capture the cursor for the vertical splitter */
3723             if (x >= nVSplitPos - SPLIT_WIDTH/2 && x < nVSplitPos + SPLIT_WIDTH/2 + 1)
3724             {
3725                 bSplit = 1;
3726                 SetCapture(hWnd);
3727             }
3728             else
3729             /* Capture the cursor for the horizontal splitter, if the Event details pane is displayed */
3730             if (hwndEventDetails && Settings.bShowDetailsPane &&
3731                 (y >= nHSplitPos - SPLIT_WIDTH/2 && y < nHSplitPos + SPLIT_WIDTH/2 + 1))
3732             {
3733                 bSplit = 2;
3734                 SetCapture(hWnd);
3735             }
3736             break;
3737         }
3738 
3739         case WM_LBUTTONUP:
3740         case WM_RBUTTONDOWN:
3741         {
3742             if (GetCapture() != hWnd)
3743                 break;
3744 
3745             /* Adjust the correct splitter position */
3746             if (bSplit == 1)
3747                 nVSplitPos = GET_X_LPARAM(lParam);
3748             else if (bSplit == 2)
3749                 nHSplitPos = GET_Y_LPARAM(lParam);
3750 
3751             /* If we are splitting, resize the windows */
3752             if (bSplit != 0)
3753             {
3754                 GetClientRect(hWnd, &rect);
3755                 ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
3756             }
3757 
3758             /* Reset the splitter state */
3759             bSplit = 0;
3760 
3761             ReleaseCapture();
3762             break;
3763         }
3764 
3765         case WM_MOUSEMOVE:
3766         {
3767             if (GetCapture() != hWnd)
3768                 break;
3769 
3770             /* Move the correct splitter */
3771             if (bSplit == 1)
3772             {
3773                 INT x = GET_X_LPARAM(lParam);
3774 
3775                 GetClientRect(hWnd, &rect);
3776 
3777                 x = min(max(x, SPLIT_WIDTH/2), rect.right - rect.left - SPLIT_WIDTH/2);
3778                 if (nVSplitPos != x)
3779                 {
3780                     nVSplitPos = x;
3781                     ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
3782                 }
3783             }
3784             else if (bSplit == 2)
3785             {
3786                 RECT rs;
3787                 INT y = GET_Y_LPARAM(lParam);
3788 
3789                 GetClientRect(hWnd, &rect);
3790                 GetWindowRect(hwndStatus, &rs);
3791 
3792                 y = min(max(y, SPLIT_WIDTH/2), rect.bottom - rect.top - SPLIT_WIDTH/2 - (rs.bottom - rs.top));
3793                 if (nHSplitPos != y)
3794                 {
3795                     nHSplitPos = y;
3796                     ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
3797                 }
3798             }
3799             break;
3800         }
3801 
3802         case WM_SIZE:
3803         {
3804             if (wParam != SIZE_MINIMIZED)
3805             {
3806                 SendMessageW(hwndStatus, WM_SIZE, 0, 0);
3807                 ResizeWnd(LOWORD(lParam), HIWORD(lParam));
3808                 break;
3809             }
3810             /* Fall through the default case */
3811         }
3812 
3813         default: Default:
3814             return DefWindowProcW(hWnd, uMsg, wParam, lParam);
3815     }
3816 
3817     return 0;
3818 }
3819 
3820 
3821 static
3822 VOID
3823 InitPropertiesDlg(HWND hDlg, PEVENTLOG EventLog)
3824 {
3825     LPWSTR lpLogName = EventLog->LogName;
3826 
3827     DWORD Result, dwType;
3828     DWORD dwMaxSize = 0, dwRetention = 0;
3829     BOOL Success;
3830     WIN32_FIND_DATAW FileInfo; // WIN32_FILE_ATTRIBUTE_DATA
3831     ULARGE_INTEGER FileSize;
3832     WCHAR wszBuf[MAX_PATH];
3833     WCHAR szTemp[MAX_LOADSTRING];
3834     LPWSTR FileName;
3835 
3836     HKEY hLogKey;
3837     WCHAR *KeyPath;
3838     DWORD cbData;
3839     SIZE_T cbKeyPath;
3840 
3841     if (EventLog->Permanent)
3842     {
3843 
3844     cbKeyPath = (wcslen(EVENTLOG_BASE_KEY) + wcslen(lpLogName) + 1) * sizeof(WCHAR);
3845     KeyPath = HeapAlloc(GetProcessHeap(), 0, cbKeyPath);
3846     if (!KeyPath)
3847     {
3848         goto Quit;
3849     }
3850 
3851     StringCbCopyW(KeyPath, cbKeyPath, EVENTLOG_BASE_KEY);
3852     StringCbCatW(KeyPath, cbKeyPath, lpLogName);
3853 
3854     if (RegOpenKeyExW(hkMachine, KeyPath, 0, KEY_QUERY_VALUE, &hLogKey) != ERROR_SUCCESS)
3855     {
3856         HeapFree(GetProcessHeap(), 0, KeyPath);
3857         goto Quit;
3858     }
3859     HeapFree(GetProcessHeap(), 0, KeyPath);
3860 
3861 
3862     cbData = sizeof(dwMaxSize);
3863     Result = RegQueryValueExW(hLogKey,
3864                               L"MaxSize",
3865                               NULL,
3866                               &dwType,
3867                               (LPBYTE)&dwMaxSize,
3868                               &cbData);
3869     if ((Result != ERROR_SUCCESS) || (dwType != REG_DWORD))
3870     {
3871         // dwMaxSize = 512 * 1024; /* 512 kBytes */
3872         dwMaxSize = 0;
3873     }
3874     /* Convert in KB */
3875     dwMaxSize /= 1024;
3876 
3877     cbData = sizeof(dwRetention);
3878     Result = RegQueryValueExW(hLogKey,
3879                               L"Retention",
3880                               NULL,
3881                               &dwType,
3882                               (LPBYTE)&dwRetention,
3883                               &cbData);
3884     if ((Result != ERROR_SUCCESS) || (dwType != REG_DWORD))
3885     {
3886         /* On Windows 2003 it is 604800 (secs) == 7 days */
3887         dwRetention = 0;
3888     }
3889     /* Convert in days, rounded up */ // ROUND_UP
3890     // dwRetention = ROUND_UP(dwRetention, 24*3600) / (24*3600);
3891     dwRetention = (dwRetention + 24*3600 - 1) / (24*3600);
3892 
3893 
3894     RegCloseKey(hLogKey);
3895 
3896     }
3897 
3898 
3899 Quit:
3900 
3901     SetDlgItemTextW(hDlg, IDC_DISPLAYNAME, lpLogName); // FIXME!
3902     SetDlgItemTextW(hDlg, IDC_LOGNAME, lpLogName);
3903 
3904     FileName = EventLog->FileName;
3905     if (FileName && *FileName)
3906     {
3907         /* Expand the file name. If the log file is on a remote computer, retrieve the network share form of the file name. */
3908         GetExpandedFilePathName(EventLog->ComputerName, FileName, wszBuf, ARRAYSIZE(wszBuf));
3909         FileName = wszBuf;
3910     }
3911     else
3912     {
3913         FileName = L"";
3914     }
3915     SetDlgItemTextW(hDlg, IDC_LOGFILE, FileName);
3916 
3917     if (FileName && *FileName)
3918     {
3919         /*
3920          * The general problem here (and in the shell as well) is that
3921          * GetFileAttributesEx fails for files that are opened without
3922          * shared access. To retrieve file information for those we need
3923          * to use something else: FindFirstFile, on the full file name.
3924          */
3925         Success = GetFileAttributesExW(FileName,
3926                                        GetFileExInfoStandard,
3927                                        (LPWIN32_FILE_ATTRIBUTE_DATA)&FileInfo);
3928         if (!Success)
3929         {
3930             HANDLE hFind = FindFirstFileW(FileName, &FileInfo);
3931             Success = (hFind != INVALID_HANDLE_VALUE);
3932             if (Success)
3933                 FindClose(hFind);
3934         }
3935     }
3936     else
3937     {
3938         Success = FALSE;
3939     }
3940 
3941     /* Starting there, FileName becomes invalid because we are reusing wszBuf */
3942 
3943     if (Success)
3944     {
3945         FileSize.u.LowPart = FileInfo.nFileSizeLow;
3946         FileSize.u.HighPart = FileInfo.nFileSizeHigh;
3947         if (FormatFileSizeWithBytes(&FileSize, wszBuf, ARRAYSIZE(wszBuf)))
3948             SetDlgItemTextW(hDlg, IDC_SIZE_LABEL, wszBuf);
3949 
3950         LoadStringW(hInst, IDS_NOT_AVAILABLE, szTemp, ARRAYSIZE(szTemp));
3951 
3952         if (GetFileTimeString(&FileInfo.ftCreationTime, wszBuf, ARRAYSIZE(wszBuf)))
3953             SetDlgItemTextW(hDlg, IDC_CREATED_LABEL, wszBuf);
3954         else
3955             SetDlgItemTextW(hDlg, IDC_CREATED_LABEL, szTemp);
3956 
3957         if (GetFileTimeString(&FileInfo.ftLastWriteTime, wszBuf, ARRAYSIZE(wszBuf)))
3958             SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, wszBuf);
3959         else
3960             SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, szTemp);
3961 
3962         if (GetFileTimeString(&FileInfo.ftLastAccessTime, wszBuf, ARRAYSIZE(wszBuf)))
3963             SetDlgItemTextW(hDlg, IDC_ACCESSED_LABEL, wszBuf);
3964         else
3965             SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, szTemp);
3966     }
3967     else
3968     {
3969         LoadStringW(hInst, IDS_NOT_AVAILABLE, szTemp, ARRAYSIZE(szTemp));
3970 
3971         SetDlgItemTextW(hDlg, IDC_SIZE_LABEL, szTemp);
3972         SetDlgItemTextW(hDlg, IDC_CREATED_LABEL, szTemp);
3973         SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, szTemp);
3974         SetDlgItemTextW(hDlg, IDC_ACCESSED_LABEL, szTemp);
3975     }
3976 
3977     if (EventLog->Permanent)
3978     {
3979         SendDlgItemMessageW(hDlg, IDC_UPDOWN_MAXLOGSIZE, UDM_SETRANGE32, (WPARAM)1, (LPARAM)0x3FFFC0);
3980         SendDlgItemMessageW(hDlg, IDC_UPDOWN_EVENTS_AGE, UDM_SETRANGE, 0, (LPARAM)MAKELONG(365, 1));
3981 
3982         SetDlgItemInt(hDlg, IDC_EDIT_MAXLOGSIZE, dwMaxSize, FALSE);
3983         SetDlgItemInt(hDlg, IDC_EDIT_EVENTS_AGE, dwRetention, FALSE);
3984 
3985         if (dwRetention == 0)
3986         {
3987             CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_OVERWRITE_AS_NEEDED);
3988             EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, FALSE);
3989             EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, FALSE);
3990         }
3991         else if (dwRetention == INFINITE)
3992         {
3993             CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_NO_OVERWRITE);
3994             EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, FALSE);
3995             EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, FALSE);
3996         }
3997         else
3998         {
3999             CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_OVERWRITE_OLDER_THAN);
4000             EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, TRUE);
4001             EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, TRUE);
4002         }
4003     }
4004     else
4005     {
4006         // TODO: Hide the unused controls! Or, just use another type of property sheet!
4007     }
4008 }
4009 
4010 /* Message handler for EventLog Properties dialog */
4011 INT_PTR CALLBACK
4012 EventLogPropProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
4013 {
4014     PEVENTLOG EventLog;
4015 
4016     EventLog = (PEVENTLOG)GetWindowLongPtrW(hDlg, DWLP_USER);
4017 
4018     switch (uMsg)
4019     {
4020         case WM_INITDIALOG:
4021         {
4022             EventLog = (PEVENTLOG)((LPPROPSHEETPAGE)lParam)->lParam;
4023             SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)EventLog);
4024 
4025             InitPropertiesDlg(hDlg, EventLog);
4026 
4027             PropSheet_UnChanged(GetParent(hDlg), hDlg);
4028             return (INT_PTR)TRUE;
4029         }
4030 
4031         case WM_DESTROY:
4032             return (INT_PTR)TRUE;
4033 
4034         case WM_COMMAND:
4035             switch (LOWORD(wParam))
4036             {
4037                 case IDOK:
4038                 case IDCANCEL:
4039                     EndDialog(hDlg, LOWORD(wParam));
4040                     return (INT_PTR)TRUE;
4041 
4042                 case IDC_OVERWRITE_AS_NEEDED:
4043                 {
4044                     CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_OVERWRITE_AS_NEEDED);
4045                     EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, FALSE);
4046                     EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, FALSE);
4047                     break;
4048                 }
4049 
4050                 case IDC_OVERWRITE_OLDER_THAN:
4051                 {
4052                     CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_OVERWRITE_OLDER_THAN);
4053                     EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, TRUE);
4054                     EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, TRUE);
4055                     break;
4056                 }
4057 
4058                 case IDC_NO_OVERWRITE:
4059                 {
4060                     CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_NO_OVERWRITE);
4061                     EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, FALSE);
4062                     EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, FALSE);
4063                     break;
4064                 }
4065 
4066                 case IDHELP:
4067                     MessageBoxW(hDlg,
4068                                 L"Help not implemented yet!",
4069                                 L"Event Log",
4070                                 MB_OK | MB_ICONINFORMATION);
4071                     return (INT_PTR)TRUE;
4072 
4073                 default:
4074                     break;
4075             }
4076             break;
4077     }
4078 
4079     return (INT_PTR)FALSE;
4080 }
4081 
4082 INT_PTR
4083 EventLogProperties(HINSTANCE hInstance, HWND hWndParent, PEVENTLOGFILTER EventLogFilter)
4084 {
4085     INT_PTR ret = 0;
4086     PROPSHEETHEADERW psh;
4087     PROPSHEETPAGEW   psp[1]; // 2
4088 
4089     /*
4090      * Bail out if there is no available filter, or if the filter
4091      * contains more than one log.
4092      */
4093     if (!EventLogFilter)
4094         return 0;
4095 
4096     EventLogFilter_AddRef(EventLogFilter);
4097 
4098     if (EventLogFilter->NumOfEventLogs > 1 ||
4099         EventLogFilter->EventLogs[0] == NULL)
4100     {
4101         goto Quit;
4102     }
4103 
4104     /* Header */
4105     psh.dwSize      = sizeof(psh);
4106     psh.dwFlags     = PSH_PROPSHEETPAGE /*| PSH_USEICONID */ | PSH_PROPTITLE | PSH_HASHELP /*| PSH_NOCONTEXTHELP */ /*| PSH_USECALLBACK */;
4107     psh.hInstance   = hInstance;
4108     psh.hwndParent  = hWndParent;
4109     // psh.pszIcon     = MAKEINTRESOURCEW(IDI_APPICON); // Disabled because it only sets the small icon; the big icon is a stretched version of the small one.
4110     psh.pszCaption  = EventLogFilter->EventLogs[0]->LogName;
4111     psh.nStartPage  = 0;
4112     psh.ppsp        = psp;
4113     psh.nPages      = ARRAYSIZE(psp);
4114     // psh.pfnCallback = PropSheetCallback;
4115 
4116     /* Log properties page */
4117     psp[0].dwSize      = sizeof(psp[0]);
4118     psp[0].dwFlags     = PSP_HASHELP;
4119     psp[0].hInstance   = hInstance;
4120     psp[0].pszTemplate = MAKEINTRESOURCEW(IDD_LOGPROPERTIES_GENERAL);
4121     psp[0].pfnDlgProc  = EventLogPropProc;
4122     psp[0].lParam      = (LPARAM)EventLogFilter->EventLogs[0];
4123 
4124 #if 0
4125     /* TODO: Log sources page */
4126     psp[1].dwSize      = sizeof(psp[1]);
4127     psp[1].dwFlags     = PSP_HASHELP;
4128     psp[1].hInstance   = hInstance;
4129     psp[1].pszTemplate = MAKEINTRESOURCEW(IDD_GENERAL_PAGE);
4130     psp[1].pfnDlgProc  = GeneralPageWndProc;
4131     psp[0].lParam      = (LPARAM)EventLogFilter->EventLogs[0];
4132 #endif
4133 
4134     /* Create the property sheet */
4135     ret = PropertySheetW(&psh);
4136 
4137 Quit:
4138     EventLogFilter_Release(EventLogFilter);
4139     return ret;
4140 }
4141 
4142 /* Message handler for Event Details dialog */
4143 static HWND hWndDetailsCtrl = NULL; // May go into the DWLP_USER
4144 static HWND hWndGrip = NULL;
4145 static INT cxMin, cyMin;    // In window coordinates
4146 static INT cxOld, cyOld;    // In client coordinates
4147 
4148 INT_PTR CALLBACK
4149 EventDetails(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
4150 {
4151     switch (uMsg)
4152     {
4153         case WM_INITDIALOG:
4154         {
4155             LONG_PTR dwStyle;
4156             INT sbVXSize, sbHYSize;
4157             RECT rcWnd, rect;
4158 
4159             hWndDetailsCtrl = CreateEventDetailsCtrl(hInst, hDlg, lParam);
4160             if (!hWndDetailsCtrl)
4161             {
4162                 EndDialog(hDlg, 0);
4163                 return (INT_PTR)TRUE;
4164             }
4165 
4166             /* Create a size grip if the dialog has a sizing border */
4167             GetClientRect(hDlg, &rcWnd);
4168             dwStyle  = GetWindowLongPtrW(hDlg, GWL_STYLE);
4169             sbVXSize = GetSystemMetrics(SM_CXVSCROLL);
4170             sbHYSize = GetSystemMetrics(SM_CYHSCROLL);
4171             if (dwStyle & WS_THICKFRAME /* == WS_SIZEBOX */)
4172             {
4173                 hWndGrip = CreateWindowW(WC_SCROLLBARW,
4174                                          NULL,
4175                                          WS_CHILD | WS_VISIBLE | /**/ WS_CLIPSIBLINGS | /**/ SBS_SIZEGRIP | SBS_SIZEBOXBOTTOMRIGHTALIGN,
4176                                          rcWnd.right - sbVXSize,
4177                                          rcWnd.bottom - sbHYSize,
4178                                          sbVXSize, sbHYSize,
4179                                          hDlg,
4180                                          NULL,
4181                                          hInst,
4182                                          NULL);
4183             }
4184 
4185             // SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)hWndDetailsCtrl);
4186 
4187             /*
4188              * Compute the minimum window size (in window coordinates) by
4189              * adding the widths/heights of the "Help" and "Close" buttons,
4190              * together with the margins, and add some minimal spacing
4191              * between the buttons.
4192              */
4193             GetWindowRect(hDlg, &rcWnd);
4194             cxMin = cyMin = 0;
4195 
4196             GetWindowRect(GetDlgItem(hDlg, IDHELP), &rect);
4197             cxMin += (rect.right - rect.left) + (rect.left - rcWnd.left); // == (rect.right - rcWnd.left);
4198             cyMin += (rect.bottom - rect.top) + (rcWnd.bottom - rect.bottom); // == (rcWnd.bottom - rect.top);
4199 
4200             GetWindowRect(GetDlgItem(hDlg, IDOK), &rect);
4201             cxMin += (rect.right - rect.left) + (rcWnd.right - rect.right); // == (rcWnd.right - rect.left);
4202             cyMin += (rect.bottom - rect.top) + (rcWnd.bottom - rect.bottom); // == (rcWnd.bottom - rect.top);
4203 
4204             /*
4205              * Convert the window rect from window to client coordinates
4206              * in order to retrieve the sizes of the left and top margins,
4207              * and add some extra space.
4208              */
4209             MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rcWnd, sizeof(RECT)/sizeof(POINT));
4210 
4211             cxMin += -2*rcWnd.left;   // Minimal spacing between the buttons == 2 * left margin
4212             cyMin += -rcWnd.top + 12; // Add some space on top
4213 
4214             GetClientRect(hDlg, &rcWnd);
4215             cxOld = rcWnd.right - rcWnd.left;
4216             cyOld = rcWnd.bottom - rcWnd.top;
4217 
4218             /* Show event info on dialog control */
4219             SendMessageW(hWndDetailsCtrl, EVT_DISPLAY, 0, 0);
4220 
4221             // SetWindowPos(hWndDetailsCtrl, NULL,
4222                          // 0, 0,
4223                          // (rcWnd.right - rcWnd.left),
4224                          // (rcWnd.bottom - rcWnd.top),
4225                          // SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
4226 
4227             /*
4228              * Hide the placeholder static control and show the event details
4229              * control instead. Note that the placeholder is here so far just
4230              * to get the dimensions right in the dialog resource editor.
4231              * I plan to remove it and use a custom control with a suitable
4232              * window class for it, that would create the event details control
4233              * instead.
4234              */
4235             ShowWindow(GetDlgItem(hDlg, IDC_STATIC), SW_HIDE);
4236             ShowWindow(hWndDetailsCtrl, SW_SHOW);
4237             return (INT_PTR)TRUE;
4238         }
4239 
4240         case WM_DESTROY:
4241             if (IsWindow(hWndDetailsCtrl))
4242                 DestroyWindow(hWndDetailsCtrl);
4243             hWndDetailsCtrl = NULL;
4244             return (INT_PTR)TRUE;
4245 
4246         case WM_COMMAND:
4247             switch (LOWORD(wParam))
4248             {
4249                 case IDOK:
4250                 case IDCANCEL:
4251                     EndDialog(hDlg, LOWORD(wParam));
4252                     return (INT_PTR)TRUE;
4253 
4254                 case IDHELP:
4255                     MessageBoxW(hDlg,
4256                                 L"Help not implemented yet!",
4257                                 L"Event Log",
4258                                 MB_OK | MB_ICONINFORMATION);
4259                     return (INT_PTR)TRUE;
4260 
4261                 default:
4262                     break;
4263             }
4264             break;
4265 
4266         case WM_SETCURSOR:
4267             if (((HWND)wParam == hWndGrip) && (LOWORD(lParam) == HTCLIENT))
4268             {
4269                 SetCursor(LoadCursorW(NULL, IDC_SIZENWSE));
4270                 SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, TRUE);
4271                 return (INT_PTR)TRUE;
4272             }
4273             break;
4274 
4275         case WM_SIZING:
4276         {
4277             /* Forbid resizing the dialog smaller than its minimal size */
4278             PRECT dragRect = (PRECT)lParam;
4279 
4280             if ((wParam == WMSZ_LEFT) || (wParam == WMSZ_TOPLEFT) || (wParam == WMSZ_BOTTOMLEFT))
4281             {
4282                 if (dragRect->right - dragRect->left < cxMin)
4283                     dragRect->left = dragRect->right - cxMin;
4284             }
4285 
4286             if ((wParam == WMSZ_RIGHT) || (wParam == WMSZ_TOPRIGHT) || (wParam == WMSZ_BOTTOMRIGHT))
4287             {
4288                 if (dragRect->right - dragRect->left < cxMin)
4289                     dragRect->right = dragRect->left + cxMin;
4290             }
4291 
4292             if ((wParam == WMSZ_TOP) || (wParam == WMSZ_TOPLEFT) || (wParam == WMSZ_TOPRIGHT))
4293             {
4294                 if (dragRect->bottom - dragRect->top < cyMin)
4295                     dragRect->top = dragRect->bottom - cyMin;
4296             }
4297 
4298             if ((wParam == WMSZ_BOTTOM) || (wParam == WMSZ_BOTTOMLEFT) || (wParam == WMSZ_BOTTOMRIGHT))
4299             {
4300                 if (dragRect->bottom - dragRect->top < cyMin)
4301                     dragRect->bottom = dragRect->top + cyMin;
4302             }
4303 
4304             SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, TRUE);
4305             return (INT_PTR)TRUE;
4306         }
4307 
4308         case WM_SIZE:
4309         {
4310             INT cx = LOWORD(lParam);
4311             INT cy = HIWORD(lParam);
4312 
4313             HDWP hdwp;
4314             HWND hItemWnd;
4315             RECT rect;
4316 
4317             hdwp = BeginDeferWindowPos(4);
4318 
4319             /* Resize the event details control window */
4320 
4321             hItemWnd = hWndDetailsCtrl;
4322             GetWindowRect(hItemWnd, &rect);
4323             MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
4324 
4325             if (hdwp)
4326                 hdwp = DeferWindowPos(hdwp,
4327                                       hItemWnd,
4328                                       HWND_TOP,
4329                                       0, 0,
4330                                       (rect.right - rect.left) + (cx - cxOld),
4331                                       (rect.bottom - rect.top) + (cy - cyOld),
4332                                       SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
4333 
4334             /* Move the buttons */
4335 
4336             hItemWnd = GetDlgItem(hDlg, IDHELP);
4337             GetWindowRect(hItemWnd, &rect);
4338             MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
4339 
4340             if (hdwp)
4341                 hdwp = DeferWindowPos(hdwp,
4342                                       hItemWnd,
4343                                       HWND_TOP,
4344                                       rect.left,
4345                                       rect.top + (cy - cyOld),
4346                                       0, 0,
4347                                       SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
4348 
4349             hItemWnd = GetDlgItem(hDlg, IDOK);
4350             GetWindowRect(hItemWnd, &rect);
4351             MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
4352 
4353             if (hdwp)
4354                 hdwp = DeferWindowPos(hdwp,
4355                                       hItemWnd,
4356                                       HWND_TOP,
4357                                       rect.left + (cx - cxOld),
4358                                       rect.top  + (cy - cyOld),
4359                                       0, 0,
4360                                       SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
4361 
4362             /* Move the size grip */
4363             if (hWndGrip && hdwp)
4364             {
4365                 GetWindowRect(hWndGrip, &rect);
4366                 MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
4367 
4368                 hdwp = DeferWindowPos(hdwp,
4369                                       hWndGrip,
4370                                       HWND_TOP,
4371                                       rect.left + (cx - cxOld),
4372                                       rect.top  + (cy - cyOld),
4373                                       0, 0,
4374                                       SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
4375             }
4376 
4377             if (hdwp)
4378                 EndDeferWindowPos(hdwp);
4379 
4380             /* Hide the size grip if we are in maximized mode */
4381             if (hWndGrip)
4382                 ShowWindow(hWndGrip, (wParam == SIZE_MAXIMIZED) ? SW_HIDE : SW_SHOW);
4383 
4384             cxOld = cx;
4385             cyOld = cy;
4386 
4387             SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, 0);
4388             return (INT_PTR)TRUE;
4389         }
4390     }
4391 
4392     return (INT_PTR)FALSE;
4393 }
4394