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