[EVENTVWR] Additions for the Event Viewer.
[reactos.git] / base / applications / mscutils / eventvwr / eventvwr.c
1 /*
2 * ReactOS Win32 Applications
3 * Copyright (C) 2007 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 /*
20 * PROJECT: ReactOS Event Log Viewer
21 * LICENSE: GPL - See COPYING in the top level directory
22 * FILE: base/applications/mscutils/eventvwr/eventvwr.c
23 * PURPOSE: Event Log Viewer main file
24 * PROGRAMMERS: Marc Piulachs (marc.piulachs at codexchange [dot] net)
25 * Eric Kohl
26 * Hermes Belusca-Maito
27 */
28
29 #include "eventvwr.h"
30 #include "evtdetctl.h"
31
32 #include <sddl.h> // For ConvertSidToStringSidW
33 #include <shellapi.h>
34 #include <shlwapi.h>
35
36 // #include "resource.h"
37
38 #define LVM_PROGRESS (WM_APP + 1) // Used by the subclassed ListView
39
40 static const LPCWSTR EVENTVWR_WNDCLASS = L"EVENTVWR"; /* The main window class name */
41 static const LPCWSTR EVENTLOG_BASE_KEY = L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\";
42 static const LPCWSTR EVNTVWR_PARAM_KEY = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Event Viewer";
43
44 /* The 3 system logs that should always exist in the user's system */
45 static const LPCWSTR SystemLogs[] =
46 {
47 L"Application",
48 L"Security",
49 L"System"
50 };
51
52 /* MessageFile message buffer size */
53 #define EVENT_MESSAGE_EVENTTEXT_BUFFER 1024*10 // NOTE: Used by evtdetctl.c
54 #define EVENT_MESSAGE_FILE_BUFFER 1024*10
55 #define EVENT_DLL_SEPARATOR L";"
56 #define EVENT_CATEGORY_MESSAGE_FILE L"CategoryMessageFile"
57 #define EVENT_MESSAGE_FILE L"EventMessageFile"
58 #define EVENT_PARAMETER_MESSAGE_FILE L"ParameterMessageFile"
59
60 #define MAX_LOADSTRING 255
61
62 #define SPLIT_WIDTH 4
63
64 /* Globals */
65 HINSTANCE hInst; /* Current instance */
66 WCHAR szTitle[MAX_LOADSTRING]; /* The title bar text */
67 WCHAR szTitleTemplate[MAX_LOADSTRING]; /* The logged-on title bar text */
68 WCHAR szStatusBarTemplate[MAX_LOADSTRING]; /* The status bar text */
69 WCHAR szLoadingWait[MAX_LOADSTRING]; /* The "Loading, please wait..." text */
70 WCHAR szEmptyList[MAX_LOADSTRING]; /* The "There are no items to show in this view" text */
71 WCHAR szSaveFilter[MAX_LOADSTRING]; /* Filter Mask for the save Dialog */
72
73 INT nVSplitPos; /* Vertical splitter (1) position */
74 INT nHSplitPos; /* Horizontal splitter (2) position */
75 BYTE bSplit = 0; /* Splitter state:
76 * 0: No splitting;
77 * 1: Vertical splitting;
78 * 2: Horizontal splitting.
79 */
80
81 HWND hwndMainWindow = NULL; /* Main window */
82 HWND hwndTreeView; /* TreeView control */
83 HWND hwndListView; /* ListView control */ // NOTE: Used by evtdetctl.c
84 HWND hwndEventDetails; /* Event details pane */
85 HWND hwndStatus; /* Status bar */
86 HWND hwndStatusProgress; /* Progress bar in the status bar */
87 HMENU hMainMenu; /* The application's main menu */
88
89 HTREEITEM htiSystemLogs = NULL, htiAppLogs = NULL, htiUserLogs = NULL;
90
91 LPWSTR lpComputerName = NULL; /* NULL: local user computer (default) */
92 LPWSTR lpszzUserLogsToLoad = NULL; /* The list of user logs to load at startup (multi-string) */
93 SIZE_T cbUserLogsSize = 0;
94
95 HKEY hkMachine = NULL; // Registry handle to the HKEY_LOCAL_MACHINE key of the remote computer registry.
96
97 /* Global event records cache for the current active event log filter */
98 DWORD g_TotalRecords = 0;
99 PEVENTLOGRECORD* g_RecordPtrs = NULL;
100
101 /* Lists of event logs and event log filters */
102 LIST_ENTRY EventLogList;
103 LIST_ENTRY EventLogFilterList;
104 PEVENTLOGFILTER ActiveFilter = NULL;
105
106 HANDLE hEnumEventsThread = NULL;
107 HANDLE hStopEnumEvent = NULL;
108
109 /*
110 * Setting EnumFilter to a valid pointer and raising the hStartEnumEvent event
111 * triggers the event-enumerator thread to perform a new enumeration.
112 */
113 PEVENTLOGFILTER EnumFilter = NULL;
114 HANDLE hStartStopEnumEvent = NULL; // End-of-application event
115 HANDLE hStartEnumEvent = NULL; // Command event
116
117 /* Default Open/Save-As dialog box */
118 OPENFILENAMEW sfn;
119
120
121 /* Event Viewer Application Settings */
122 typedef struct _SETTINGS
123 {
124 BOOL bShowDetailsPane; /* Show (TRUE) or Hide (FALSE) the events details pane */
125 BOOL bShowGrid; /* Show (TRUE) or Hide (FALSE) the events view grid */
126 BOOL bSaveSettings; /* Save (TRUE) or do not save (FALSE) current settings on exit */
127 BOOL bNewestEventsFirst; /* Sort the displayed events the newest ones first (TRUE) or last (FALSE) */
128 INT nVSplitPos; /* Vertical splitter position */
129 INT nHSplitPos; /* Horizontal splitter position */
130 WINDOWPLACEMENT wpPos;
131 } SETTINGS, *PSETTINGS;
132
133 SETTINGS Settings;
134
135
136 /* Forward declarations of functions included in this code module */
137
138 static DWORD WINAPI
139 StartStopEnumEventsThread(IN LPVOID lpParameter);
140
141 VOID OpenUserEventLogFile(IN LPCWSTR lpszFileName);
142
143 VOID BuildLogListAndFilterList(IN LPCWSTR lpComputerName);
144 VOID FreeLogList(VOID);
145 VOID FreeLogFilterList(VOID);
146
147 ATOM MyRegisterClass(HINSTANCE);
148 BOOL InitInstance(HINSTANCE);
149 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
150 INT_PTR EventLogProperties(HINSTANCE, HWND, PEVENTLOGFILTER);
151 INT_PTR CALLBACK EventDetails(HWND, UINT, WPARAM, LPARAM);
152
153
154 /* MAIN FUNCTIONS *************************************************************/
155
156 VOID
157 ShowWin32Error(IN DWORD dwError)
158 {
159 LPWSTR lpMessageBuffer;
160
161 if (dwError == ERROR_SUCCESS)
162 return;
163
164 if (!FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
165 FORMAT_MESSAGE_FROM_SYSTEM |
166 FORMAT_MESSAGE_IGNORE_INSERTS,
167 NULL,
168 dwError,
169 LANG_USER_DEFAULT,
170 (LPWSTR)&lpMessageBuffer,
171 0, NULL))
172 {
173 return;
174 }
175
176 MessageBoxW(hwndMainWindow, lpMessageBuffer, szTitle, MB_OK | MB_ICONERROR);
177 LocalFree(lpMessageBuffer);
178 }
179
180 VOID
181 DisplayUsage(VOID)
182 {
183 LPWSTR lpBuffer;
184 LPCWSTR lpUsage;
185 INT iUsageLen = LoadStringW(hInst, IDS_USAGE, (LPWSTR)&lpUsage, 0);
186
187 if (iUsageLen == 0)
188 return;
189
190 lpBuffer = HeapAlloc(GetProcessHeap(), 0, (iUsageLen + 1) * sizeof(WCHAR));
191 if (!lpBuffer)
192 return;
193
194 StringCchCopyNW(lpBuffer, iUsageLen + 1, lpUsage, iUsageLen);
195 MessageBoxW(NULL, lpBuffer, szTitle, MB_OK | MB_ICONINFORMATION);
196
197 HeapFree(GetProcessHeap(), 0, lpBuffer);
198 }
199
200 BOOL
201 ProcessCmdLine(IN LPWSTR lpCmdLine)
202 {
203 BOOL Success = FALSE;
204 INT i, argc;
205 LPWSTR* argv;
206
207 /* Skip any leading whitespace */
208 if (lpCmdLine)
209 {
210 while (iswspace(*lpCmdLine))
211 ++lpCmdLine;
212 }
213
214 /* No command line means no processing needed */
215 if (!lpCmdLine || !*lpCmdLine)
216 return TRUE;
217
218 /* Build the arguments vector */
219 argv = CommandLineToArgvW(lpCmdLine, &argc);
220 if (!argv)
221 return FALSE;
222
223 /* Parse the command line for options (skip the program name) */
224 for (i = 1; i < argc; ++i)
225 {
226 /* Check for new options */
227 if (argv[i][0] == L'-' || argv[i][0] == L'/')
228 {
229 if (argv[i][1] == L'?' && argv[i][2] == 0)
230 {
231 /* Display help */
232 DisplayUsage();
233 goto Quit;
234 }
235 else
236 if (argv[i][2] == L':')
237 {
238 switch (towupper(argv[i][1]))
239 {
240 case L'L':
241 {
242 LPWSTR lpNewBuffer;
243 LPWSTR lpFileName = argv[i] + 3;
244 SIZE_T cbFileName;
245
246 /* Check for a quoted file name */
247 if (*lpFileName == L'\"')
248 {
249 /* Skip this quote, and the last one too if any */
250 ++lpFileName;
251 cbFileName = wcslen(lpFileName);
252 if (cbFileName > 0 && lpFileName[cbFileName - 1] == L'\"')
253 lpFileName[cbFileName - 1] = UNICODE_NULL;
254 }
255
256 /* Skip this one if we do not actually have a file name */
257 if (!*lpFileName)
258 continue;
259
260 cbFileName = (wcslen(lpFileName) + 1) * sizeof(WCHAR);
261
262 /* Reallocate the list of user logs to load */
263 if (lpszzUserLogsToLoad)
264 {
265 lpNewBuffer = HeapReAlloc(GetProcessHeap(),
266 HEAP_ZERO_MEMORY,
267 lpszzUserLogsToLoad,
268 /* Count the multi-string NULL-terminator */
269 cbUserLogsSize + cbFileName + sizeof(WCHAR));
270 }
271 else
272 {
273 cbUserLogsSize = 0;
274 lpNewBuffer = HeapAlloc(GetProcessHeap(),
275 HEAP_ZERO_MEMORY,
276 /* Count the multi-string NULL-terminator */
277 cbUserLogsSize + cbFileName + sizeof(WCHAR));
278 }
279
280 if (!lpNewBuffer)
281 {
282 ShowWin32Error(ERROR_NOT_ENOUGH_MEMORY);
283 goto Quit;
284 }
285
286 lpszzUserLogsToLoad = lpNewBuffer;
287 lpNewBuffer = (LPWSTR)((ULONG_PTR)lpNewBuffer + cbUserLogsSize);
288 cbUserLogsSize += cbFileName;
289
290 /* Save the file name */
291 StringCbCopyW(lpNewBuffer, cbFileName, lpFileName);
292
293 continue;
294 }
295
296 default:
297 break;
298 }
299 }
300
301 /* Unknown argument: display help and bail out */
302 DisplayUsage();
303 goto Quit;
304 }
305 else
306 {
307 /*
308 * An argument that does not start with the switch character.
309 * If this is the first argument then this corresponds to the
310 * optional computer name. Otherwise this is a wrong argument.
311 */
312 if (i == 1)
313 {
314 /* Store the computer name */
315 LPWSTR lpTemp = argv[i];
316 SIZE_T cbLength;
317
318 /* Strip any leading backslashes */
319 while (*lpTemp == L'\\')
320 ++lpTemp;
321
322 cbLength = (wcslen(lpTemp) + 1) * sizeof(WCHAR);
323 lpComputerName = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbLength);
324 if (lpComputerName)
325 {
326 StringCbCopyW(lpComputerName, cbLength, lpTemp);
327 }
328 /* else, fall back to local computer */
329 }
330 else
331 {
332 /* Invalid syntax: display help and bail out */
333 DisplayUsage();
334 goto Quit;
335 }
336 }
337 }
338
339 Success = TRUE;
340
341 Quit:
342 /* In case of failure, free anything we have allocated */
343 if (!Success)
344 {
345 if (lpszzUserLogsToLoad)
346 {
347 cbUserLogsSize = 0;
348 HeapFree(GetProcessHeap(), 0, lpszzUserLogsToLoad);
349 lpszzUserLogsToLoad = NULL;
350 }
351 if (lpComputerName)
352 {
353 HeapFree(GetProcessHeap(), 0, lpComputerName);
354 lpComputerName = NULL;
355 }
356 }
357
358 /* Free the arguments vector and exit */
359 LocalFree(argv);
360 return Success;
361 }
362
363 BOOL
364 LoadSettings(int nDefCmdShow)
365 {
366 HKEY hKeyEventVwr;
367 LONG Result;
368 DWORD dwSize;
369 DWORD dwType;
370 DWORD Value;
371 UNICODE_STRING ValueU;
372 WCHAR buffer[100];
373
374 /* Load the default values */
375 Settings.bShowDetailsPane = TRUE;
376 Settings.bShowGrid = FALSE;
377 Settings.bSaveSettings = TRUE;
378 Settings.bNewestEventsFirst = TRUE;
379 Settings.nVSplitPos = 250; /* Splitter default positions */
380 Settings.nHSplitPos = 250;
381 ZeroMemory(&Settings.wpPos, sizeof(Settings.wpPos));
382 Settings.wpPos.length = sizeof(Settings.wpPos);
383 Settings.wpPos.rcNormalPosition.left = CW_USEDEFAULT;
384 Settings.wpPos.rcNormalPosition.top = CW_USEDEFAULT;
385 Settings.wpPos.rcNormalPosition.right = CW_USEDEFAULT;
386 Settings.wpPos.rcNormalPosition.bottom = CW_USEDEFAULT;
387
388 /* Try to create/open the Event Viewer user key */
389 if (RegCreateKeyExW(HKEY_CURRENT_USER,
390 EVNTVWR_PARAM_KEY,
391 0,
392 NULL,
393 REG_OPTION_NON_VOLATILE,
394 KEY_READ | KEY_WRITE,
395 NULL,
396 &hKeyEventVwr,
397 NULL) != ERROR_SUCCESS)
398 {
399 return FALSE;
400 }
401
402 // Result = RegQueryValueExW(hKeyEventVwr, L"Filter", NULL, &dwType, (LPBYTE)&szFilter, &dwSize); // REG_SZ
403 // Result = RegQueryValueExW(hKeyEventVwr, L"Find", NULL, &dwType, (LPBYTE)&szFind, &dwSize); // REG_SZ
404 // Result = RegQueryValueExW(hKeyEventVwr, L"Module", NULL, &dwType, (LPBYTE)&szModule, &dwSize); // REG_SZ
405
406 dwSize = sizeof(Value);
407 Result = RegQueryValueExW(hKeyEventVwr, L"DetailsPane", NULL, &dwType, (LPBYTE)&Value, &dwSize);
408 if ((Result == ERROR_SUCCESS) && (dwType == REG_SZ || dwType == REG_DWORD))
409 {
410 if (dwType == REG_SZ)
411 {
412 ValueU.Buffer = (PWSTR)&Value;
413 ValueU.Length = ValueU.MaximumLength = dwSize;
414 RtlUnicodeStringToInteger(&ValueU, 10, &Value);
415 }
416 Settings.bShowDetailsPane = !!Value;
417 }
418
419 dwSize = sizeof(Value);
420 Result = RegQueryValueExW(hKeyEventVwr, L"ShowGrid", NULL, &dwType, (LPBYTE)&Value, &dwSize);
421 if ((Result == ERROR_SUCCESS) && (dwType == REG_SZ || dwType == REG_DWORD))
422 {
423 if (dwType == REG_SZ)
424 {
425 ValueU.Buffer = (PWSTR)&Value;
426 ValueU.Length = ValueU.MaximumLength = dwSize;
427 RtlUnicodeStringToInteger(&ValueU, 10, &Value);
428 }
429 Settings.bShowGrid = !!Value;
430 }
431
432 dwSize = sizeof(Value);
433 Result = RegQueryValueExW(hKeyEventVwr, L"SortOrder", NULL, &dwType, (LPBYTE)&Value, &dwSize);
434 if ((Result == ERROR_SUCCESS) && (dwType == REG_SZ || dwType == REG_DWORD))
435 {
436 if (dwType == REG_SZ)
437 {
438 ValueU.Buffer = (PWSTR)&Value;
439 ValueU.Length = ValueU.MaximumLength = dwSize;
440 RtlUnicodeStringToInteger(&ValueU, 10, &Value);
441 }
442 Settings.bNewestEventsFirst = !!Value;
443 }
444
445 /* Retrieve the splitter positions */
446 dwSize = sizeof(Value);
447 Result = RegQueryValueExW(hKeyEventVwr, L"VSplitPos", NULL, &dwType, (LPBYTE)&Value, &dwSize);
448 if ((Result == ERROR_SUCCESS) && (dwType == REG_SZ || dwType == REG_DWORD))
449 {
450 if (dwType == REG_SZ)
451 {
452 ValueU.Buffer = (PWSTR)&Value;
453 ValueU.Length = ValueU.MaximumLength = dwSize;
454 RtlUnicodeStringToInteger(&ValueU, 10, &Value);
455 }
456 Settings.nVSplitPos = Value;
457 }
458
459 dwSize = sizeof(Value);
460 Result = RegQueryValueExW(hKeyEventVwr, L"HSplitPos", NULL, &dwType, (LPBYTE)&Value, &dwSize);
461 if ((Result == ERROR_SUCCESS) && (dwType == REG_SZ || dwType == REG_DWORD))
462 {
463 if (dwType == REG_SZ)
464 {
465 ValueU.Buffer = (PWSTR)&Value;
466 ValueU.Length = ValueU.MaximumLength = dwSize;
467 RtlUnicodeStringToInteger(&ValueU, 10, &Value);
468 }
469 Settings.nHSplitPos = Value;
470 }
471
472 /* Retrieve the geometry of the main window */
473 dwSize = sizeof(buffer);
474 Result = RegQueryValueExW(hKeyEventVwr, L"Window", NULL, &dwType, (LPBYTE)buffer, &dwSize);
475 if ((Result != ERROR_SUCCESS) || (dwType != REG_SZ))
476 buffer[0] = UNICODE_NULL;
477
478 if (swscanf(buffer, L"%d %d %d %d %d",
479 &Settings.wpPos.rcNormalPosition.left,
480 &Settings.wpPos.rcNormalPosition.top,
481 &Settings.wpPos.rcNormalPosition.right,
482 &Settings.wpPos.rcNormalPosition.bottom,
483 &Settings.wpPos.showCmd) != 5)
484 {
485 /* Parsing failed, use defaults */
486 Settings.wpPos.rcNormalPosition.left = CW_USEDEFAULT;
487 Settings.wpPos.rcNormalPosition.top = CW_USEDEFAULT;
488 Settings.wpPos.rcNormalPosition.right = CW_USEDEFAULT;
489 Settings.wpPos.rcNormalPosition.bottom = CW_USEDEFAULT;
490 Settings.wpPos.showCmd = nDefCmdShow; // SW_SHOWNORMAL;
491 }
492
493 dwSize = sizeof(Value);
494 Result = RegQueryValueExW(hKeyEventVwr, L"SaveSettings", NULL, &dwType, (LPBYTE)&Value, &dwSize);
495 if ((Result == ERROR_SUCCESS) && (dwType == REG_SZ || dwType == REG_DWORD))
496 {
497 if (dwType == REG_SZ)
498 {
499 ValueU.Buffer = (PWSTR)&Value;
500 ValueU.Length = ValueU.MaximumLength = dwSize;
501 RtlUnicodeStringToInteger(&ValueU, 10, &Value);
502 }
503 Settings.bSaveSettings = !!Value;
504 }
505
506 RegCloseKey(hKeyEventVwr);
507 return TRUE;
508 }
509
510 BOOL
511 SaveSettings(VOID)
512 {
513 HKEY hKeyEventVwr;
514 DWORD dwSize;
515 WCHAR buffer[100];
516
517 /* Try to create/open the Event Viewer user key */
518 if (RegCreateKeyExW(HKEY_CURRENT_USER,
519 EVNTVWR_PARAM_KEY,
520 0,
521 NULL,
522 REG_OPTION_NON_VOLATILE,
523 KEY_READ | KEY_WRITE,
524 NULL,
525 &hKeyEventVwr,
526 NULL) != ERROR_SUCCESS)
527 {
528 return FALSE;
529 }
530
531 dwSize = sizeof(Settings.bSaveSettings);
532 RegSetValueExW(hKeyEventVwr, L"SaveSettings", 0, REG_DWORD, (LPBYTE)&Settings.bSaveSettings, dwSize);
533
534 /* Do not save more settings if we are not asked to do so */
535 if (!Settings.bSaveSettings)
536 goto Quit;
537
538 dwSize = sizeof(Settings.bShowDetailsPane);
539 RegSetValueExW(hKeyEventVwr, L"DetailsPane", 0, REG_DWORD, (LPBYTE)&Settings.bShowDetailsPane, dwSize);
540
541 dwSize = sizeof(Settings.bShowGrid);
542 RegSetValueExW(hKeyEventVwr, L"ShowGrid", 0, REG_DWORD, (LPBYTE)&Settings.bShowGrid, dwSize);
543
544 dwSize = sizeof(Settings.bNewestEventsFirst);
545 RegSetValueExW(hKeyEventVwr, L"SortOrder", 0, REG_DWORD, (LPBYTE)&Settings.bNewestEventsFirst, dwSize);
546
547 Settings.nVSplitPos = nVSplitPos;
548 dwSize = sizeof(Settings.nVSplitPos);
549 RegSetValueExW(hKeyEventVwr, L"VSplitPos", 0, REG_DWORD, (LPBYTE)&Settings.nVSplitPos, dwSize);
550
551 Settings.nHSplitPos = nHSplitPos;
552 dwSize = sizeof(Settings.nHSplitPos);
553 RegSetValueExW(hKeyEventVwr, L"HSplitPos", 0, REG_DWORD, (LPBYTE)&Settings.nHSplitPos, dwSize);
554
555 StringCbPrintfW(buffer, sizeof(buffer),
556 L"%d %d %d %d %d",
557 Settings.wpPos.rcNormalPosition.left,
558 Settings.wpPos.rcNormalPosition.top,
559 Settings.wpPos.rcNormalPosition.right,
560 Settings.wpPos.rcNormalPosition.bottom,
561 Settings.wpPos.showCmd);
562
563 dwSize = wcslen(buffer) * sizeof(WCHAR);
564 RegSetValueExW(hKeyEventVwr, L"Window", 0, REG_SZ, (LPBYTE)buffer, dwSize);
565
566 Quit:
567 RegCloseKey(hKeyEventVwr);
568 return TRUE;
569 }
570
571 int APIENTRY
572 wWinMain(HINSTANCE hInstance,
573 HINSTANCE hPrevInstance,
574 LPWSTR lpCmdLine,
575 int nCmdShow)
576 {
577 HANDLE hThread;
578 INITCOMMONCONTROLSEX iccx;
579 HMODULE hRichEdit;
580 HACCEL hAccelTable;
581 MSG msg;
582
583 UNREFERENCED_PARAMETER(hPrevInstance);
584 UNREFERENCED_PARAMETER(lpCmdLine);
585
586 /* Whenever any of the common controls are used in your app,
587 * you must call InitCommonControlsEx() to register the classes
588 * for those controls. */
589 iccx.dwSize = sizeof(iccx);
590 iccx.dwICC = ICC_LISTVIEW_CLASSES;
591 InitCommonControlsEx(&iccx);
592
593 /* Load the RichEdit DLL to add support for RichEdit controls */
594 hRichEdit = LoadLibraryW(L"riched20.dll");
595 if (!hRichEdit)
596 return -1;
597
598 msg.wParam = (WPARAM)-1;
599
600 /* Store the instance handle in the global variable */
601 hInst = hInstance;
602
603 /* Initialize global strings */
604 LoadStringW(hInstance, IDS_APP_TITLE, szTitle, ARRAYSIZE(szTitle));
605 LoadStringW(hInstance, IDS_APP_TITLE_EX, szTitleTemplate, ARRAYSIZE(szTitleTemplate));
606 LoadStringW(hInstance, IDS_STATUS_MSG, szStatusBarTemplate, ARRAYSIZE(szStatusBarTemplate));
607 LoadStringW(hInstance, IDS_LOADING_WAIT, szLoadingWait, ARRAYSIZE(szLoadingWait));
608 LoadStringW(hInstance, IDS_NO_ITEMS, szEmptyList, ARRAYSIZE(szEmptyList));
609
610 /*
611 * Process the command-line arguments. Note that we need the full
612 * command-line, with the program file included, and not just what
613 * WinMain() provides in its lpCmdLine parameter.
614 */
615 if (!ProcessCmdLine(GetCommandLineW()))
616 goto Quit;
617
618 if (!MyRegisterClass(hInstance))
619 goto Quit;
620
621 /* Load the settings */
622 LoadSettings(nCmdShow);
623
624 /* Perform application initialization */
625 if (!InitInstance(hInstance))
626 goto Quit;
627
628 hAccelTable = LoadAcceleratorsW(hInstance, MAKEINTRESOURCEW(IDA_EVENTVWR));
629
630 /* Create the Start/Stop enumerator thread */
631 // Manual-reset event
632 hStartStopEnumEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
633 if (!hStartStopEnumEvent)
634 goto Cleanup;
635
636 // Auto-reset event
637 hStartEnumEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
638 if (!hStartEnumEvent)
639 goto Cleanup;
640
641 hThread = CreateThread(NULL, 0,
642 StartStopEnumEventsThread,
643 NULL, 0, NULL);
644 if (!hThread)
645 goto Cleanup;
646
647 /* Retrieve the available event logs on this computer and create filters for them */
648 InitializeListHead(&EventLogList);
649 InitializeListHead(&EventLogFilterList);
650 BuildLogListAndFilterList(lpComputerName);
651
652 /* Open the user-specified logs if any are present on the command-line */
653 if (lpszzUserLogsToLoad)
654 {
655 LPWSTR lpUserLog;
656 for (lpUserLog = lpszzUserLogsToLoad; *lpUserLog; lpUserLog += wcslen(lpUserLog) + 1)
657 {
658 OpenUserEventLogFile(lpUserLog);
659 }
660
661 /* Now cleanup the list of user logs */
662 cbUserLogsSize = 0;
663 HeapFree(GetProcessHeap(), 0, lpszzUserLogsToLoad);
664 lpszzUserLogsToLoad = NULL;
665 }
666
667 /* Main message loop */
668 while (GetMessageW(&msg, NULL, 0, 0))
669 {
670 if (!TranslateAcceleratorW(hwndMainWindow, hAccelTable, &msg))
671 {
672 TranslateMessage(&msg);
673 DispatchMessageW(&msg);
674 }
675 }
676
677 /* Save the settings */
678 SaveSettings();
679
680 /* Disconnect from computer */
681 if (hkMachine && hkMachine != HKEY_LOCAL_MACHINE)
682 {
683 /* We are connected to some other computer, close the old connection */
684 RegCloseKey(hkMachine);
685 hkMachine = NULL;
686 }
687
688 /* Stop the enumerator thread */
689 SetEvent(hStartStopEnumEvent);
690 WaitForSingleObject(hThread, INFINITE);
691 CloseHandle(hThread);
692
693 /* Free the filters list and the event logs list */
694 FreeLogFilterList();
695 FreeLogList();
696
697 Cleanup:
698 /* Handle cleanup */
699 if (hStartEnumEvent)
700 CloseHandle(hStartEnumEvent);
701 if (hStartStopEnumEvent)
702 CloseHandle(hStartStopEnumEvent);
703
704 Quit:
705 /* Final cleanup */
706 if (lpszzUserLogsToLoad)
707 {
708 cbUserLogsSize = 0;
709 HeapFree(GetProcessHeap(), 0, lpszzUserLogsToLoad);
710 lpszzUserLogsToLoad = NULL;
711 }
712 if (lpComputerName)
713 {
714 HeapFree(GetProcessHeap(), 0, lpComputerName);
715 lpComputerName = NULL;
716 }
717 FreeLibrary(hRichEdit);
718
719 return (int)msg.wParam;
720 }
721
722
723 /* GENERIC HELPER FUNCTIONS ***************************************************/
724
725 VOID
726 EventTimeToSystemTime(IN DWORD EventTime,
727 OUT PSYSTEMTIME pSystemTime)
728 {
729 SYSTEMTIME st1970 = { 1970, 1, 0, 1, 0, 0, 0, 0 };
730 FILETIME ftLocal;
731 union
732 {
733 FILETIME ft;
734 ULONGLONG ll;
735 } u1970, uUCT;
736
737 uUCT.ft.dwHighDateTime = 0;
738 uUCT.ft.dwLowDateTime = EventTime;
739 SystemTimeToFileTime(&st1970, &u1970.ft);
740 uUCT.ll = uUCT.ll * 10000000 + u1970.ll;
741 FileTimeToLocalFileTime(&uUCT.ft, &ftLocal);
742 FileTimeToSystemTime(&ftLocal, pSystemTime);
743 }
744
745 /*
746 * This function takes in entry a path to a single DLL, in which
747 * the message string of ID dwMessageId has to be searched.
748 * The other parameters are similar to those of the FormatMessageW API.
749 */
750 LPWSTR
751 GetMessageStringFromDll(
752 IN LPCWSTR lpMessageDll,
753 IN DWORD dwFlags, // If we always use the same flags, just remove this param...
754 IN DWORD dwMessageId,
755 IN DWORD nSize,
756 IN va_list* Arguments OPTIONAL)
757 {
758 HMODULE hLibrary;
759 DWORD dwLength;
760 LPWSTR lpMsgBuf = NULL;
761
762 hLibrary = LoadLibraryExW(lpMessageDll, NULL,
763 /* LOAD_LIBRARY_AS_IMAGE_RESOURCE | */ LOAD_LIBRARY_AS_DATAFILE);
764 if (hLibrary == NULL)
765 return NULL;
766
767 /* Sanitize dwFlags */
768 dwFlags &= ~FORMAT_MESSAGE_FROM_STRING;
769 dwFlags |= FORMAT_MESSAGE_FROM_HMODULE;
770
771 _SEH2_TRY
772 {
773 /*
774 * Retrieve the message string without appending extra newlines.
775 * Wrap in SEH to protect from invalid string parameters.
776 */
777 _SEH2_TRY
778 {
779 dwLength = FormatMessageW(dwFlags,
780 /* FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
781 FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, */
782 hLibrary,
783 dwMessageId,
784 LANG_USER_DEFAULT,
785 (LPWSTR)&lpMsgBuf,
786 nSize,
787 Arguments);
788 }
789 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
790 {
791 dwLength = 0;
792
793 /*
794 * An exception occurred while calling FormatMessage, this is usually
795 * the sign that a parameter was invalid, either 'lpMsgBuf' was NULL
796 * but we did not pass the flag FORMAT_MESSAGE_ALLOCATE_BUFFER, or the
797 * array pointer 'Arguments' was NULL or did not contain enough elements,
798 * and we did not pass the flag FORMAT_MESSAGE_IGNORE_INSERTS, and the
799 * message string expected too many inserts.
800 * In this last case only, we can call again FormatMessage but ignore
801 * explicitly the inserts. The string that we will return to the user
802 * will not be pre-formatted.
803 */
804 if (((dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) || lpMsgBuf) &&
805 !(dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS))
806 {
807 /* Remove any possible harmful flags and always ignore inserts */
808 dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY;
809 dwFlags |= FORMAT_MESSAGE_IGNORE_INSERTS;
810
811 /* If this call also throws an exception, we are really dead */
812 dwLength = FormatMessageW(dwFlags,
813 hLibrary,
814 dwMessageId,
815 LANG_USER_DEFAULT,
816 (LPWSTR)&lpMsgBuf,
817 nSize,
818 NULL /* Arguments */);
819 }
820 }
821 _SEH2_END;
822 }
823 _SEH2_FINALLY
824 {
825 FreeLibrary(hLibrary);
826 }
827 _SEH2_END;
828
829 if (dwLength == 0)
830 {
831 ASSERT(lpMsgBuf == NULL);
832 lpMsgBuf = NULL;
833 }
834 else
835 {
836 LPWSTR ptr;
837
838 ASSERT(lpMsgBuf);
839
840 /* Trim any trailing whitespace */
841 ptr = lpMsgBuf + dwLength - 1;
842 while (iswspace(*ptr))
843 *ptr-- = UNICODE_NULL;
844 }
845
846 return lpMsgBuf;
847 }
848
849 /*
850 * This function takes in entry a comma-separated list of DLLs, in which
851 * the message string of ID dwMessageId has to be searched.
852 * The other parameters are similar to those of the FormatMessageW API.
853 */
854 LPWSTR
855 GetMessageStringFromDllList(
856 IN LPCWSTR lpMessageDllList,
857 IN DWORD dwFlags, // If we always use the same flags, just remove this param...
858 IN DWORD dwMessageId,
859 IN DWORD nSize,
860 IN va_list* Arguments OPTIONAL)
861 {
862 BOOL Success = FALSE;
863 SIZE_T cbLength;
864 LPWSTR szMessageDllList;
865 LPWSTR szDll;
866 LPWSTR lpMsgBuf = NULL;
867
868 /* Allocate a local buffer for the DLL list that can be tokenized */
869 // TODO: Optimize that!! Maybe we can cleverly use lpMessageDllList in read/write mode
870 // and cleverly temporarily replace the ';' by UNICODE_NULL, do our job, then reverse the change.
871 cbLength = (wcslen(lpMessageDllList) + 1) * sizeof(WCHAR);
872 szMessageDllList = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbLength);
873 if (!szMessageDllList)
874 return NULL;
875 RtlCopyMemory(szMessageDllList, lpMessageDllList, cbLength);
876
877 /* Loop through the list of message DLLs */
878 szDll = wcstok(szMessageDllList, EVENT_DLL_SEPARATOR);
879 while ((szDll != NULL) && !Success)
880 {
881 // Uses LANG_USER_DEFAULT
882 lpMsgBuf = GetMessageStringFromDll(szDll,
883 dwFlags,
884 dwMessageId,
885 nSize,
886 Arguments);
887 if (lpMsgBuf)
888 {
889 /* The ID was found and the message was formatted */
890 Success = TRUE;
891 break;
892 }
893
894 /*
895 * The DLL could not be loaded, or the message could not be found,
896 * try the next DLL, if any.
897 */
898 szDll = wcstok(NULL, EVENT_DLL_SEPARATOR);
899 }
900
901 HeapFree(GetProcessHeap(), 0, szMessageDllList);
902
903 return lpMsgBuf;
904 }
905
906
907 typedef struct
908 {
909 LPWSTR pStartingAddress; // Pointer to the beginning of a parameter string in pMessage
910 LPWSTR pEndingAddress; // Pointer to the end of a parameter string in pMessage
911 DWORD pParameterID; // Parameter identifier found in pMessage
912 LPWSTR pParameter; // Actual parameter string
913 } param_strings_format_data;
914
915 DWORD
916 ApplyParameterStringsToMessage(
917 IN LPCWSTR lpMessageDllList,
918 IN BOOL bMessagePreFormatted,
919 IN CONST LPCWSTR pMessage,
920 OUT LPWSTR* pFinalMessage)
921 {
922 /*
923 * This code is heavily adapted from the MSDN example:
924 * https://msdn.microsoft.com/en-us/library/windows/desktop/bb427356.aspx
925 * with bugs removed.
926 */
927
928 DWORD Status = ERROR_SUCCESS;
929 DWORD dwParamCount = 0; // Number of insertion strings found in pMessage
930 size_t cchBuffer = 0; // Size of the buffer in characters
931 size_t cchParams = 0; // Number of characters in all the parameter strings
932 size_t cch = 0;
933 DWORD i = 0;
934 param_strings_format_data* pParamData = NULL; // Array of pointers holding information about each parameter string in pMessage
935 LPWSTR pTempMessage = (LPWSTR)pMessage;
936 LPWSTR pTempFinalMessage = NULL;
937
938 *pFinalMessage = NULL;
939
940 /* Determine the number of parameter insertion strings in pMessage */
941 if (bMessagePreFormatted)
942 {
943 while ((pTempMessage = wcschr(pTempMessage, L'%')))
944 {
945 pTempMessage++;
946 if (iswdigit(*pTempMessage))
947 {
948 dwParamCount++;
949 while (iswdigit(*++pTempMessage)) ;
950 }
951 }
952 }
953 else
954 {
955 while ((pTempMessage = wcsstr(pTempMessage, L"%%")))
956 {
957 pTempMessage += 2;
958 if (iswdigit(*pTempMessage))
959 {
960 dwParamCount++;
961 while (iswdigit(*++pTempMessage)) ;
962 }
963 }
964 }
965
966 /* If there are no parameter insertion strings in pMessage, just return */
967 if (dwParamCount == 0)
968 {
969 // *pFinalMessage = NULL;
970 goto Cleanup;
971 }
972
973 /* Allocate the array of parameter string format data */
974 pParamData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwParamCount * sizeof(param_strings_format_data));
975 if (!pParamData)
976 {
977 Status = ERROR_OUTOFMEMORY;
978 goto Cleanup;
979 }
980
981 /*
982 * Retrieve each parameter in pMessage and the beginning and end of the
983 * insertion string, as well as the message identifier of the parameter.
984 */
985 pTempMessage = (LPWSTR)pMessage;
986 if (bMessagePreFormatted)
987 {
988 while ((pTempMessage = wcschr(pTempMessage, L'%')) && (i < dwParamCount))
989 {
990 pTempMessage++;
991 if (iswdigit(*pTempMessage))
992 {
993 pParamData[i].pStartingAddress = pTempMessage-1;
994 pParamData[i].pParameterID = (DWORD)_wtol(pTempMessage);
995
996 while (iswdigit(*++pTempMessage)) ;
997
998 pParamData[i].pEndingAddress = pTempMessage;
999 i++;
1000 }
1001 }
1002 }
1003 else
1004 {
1005 while ((pTempMessage = wcsstr(pTempMessage, L"%%")) && (i < dwParamCount))
1006 {
1007 pTempMessage += 2;
1008 if (iswdigit(*pTempMessage))
1009 {
1010 pParamData[i].pStartingAddress = pTempMessage-2;
1011 pParamData[i].pParameterID = (DWORD)_wtol(pTempMessage);
1012
1013 while (iswdigit(*++pTempMessage)) ;
1014
1015 pParamData[i].pEndingAddress = pTempMessage;
1016 i++;
1017 }
1018 }
1019 }
1020
1021 /* Retrieve each parameter string */
1022 for (i = 0; i < dwParamCount; i++)
1023 {
1024 // pParamData[i].pParameter = GetMessageString(pParamData[i].pParameterID, 0, NULL);
1025 pParamData[i].pParameter =
1026 GetMessageStringFromDllList(lpMessageDllList,
1027 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
1028 FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
1029 pParamData[i].pParameterID,
1030 0, NULL);
1031 if (!pParamData[i].pParameter)
1032 {
1033 /* Skip the insertion string */
1034 continue;
1035 }
1036
1037 cchParams += wcslen(pParamData[i].pParameter);
1038 }
1039
1040 /*
1041 * Allocate the final message buffer, the size of which is based on the
1042 * length of the original message and the length of each parameter string.
1043 */
1044 cchBuffer = wcslen(pMessage) + cchParams + 1;
1045 *pFinalMessage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cchBuffer * sizeof(WCHAR));
1046 if (!*pFinalMessage)
1047 {
1048 Status = ERROR_OUTOFMEMORY;
1049 goto Cleanup;
1050 }
1051
1052 pTempFinalMessage = *pFinalMessage;
1053
1054 /* Build the final message string */
1055 pTempMessage = (LPWSTR)pMessage;
1056 for (i = 0; i < dwParamCount; i++)
1057 {
1058 /* Append the segment from pMessage */
1059 cch = pParamData[i].pStartingAddress - pTempMessage;
1060 StringCchCopyNW(pTempFinalMessage, cchBuffer, pTempMessage, cch);
1061 pTempMessage = pParamData[i].pEndingAddress;
1062 cchBuffer -= cch;
1063 pTempFinalMessage += cch;
1064
1065 /* Append the parameter string */
1066 if (pParamData[i].pParameter)
1067 {
1068 StringCchCopyW(pTempFinalMessage, cchBuffer, pParamData[i].pParameter);
1069 cch = wcslen(pParamData[i].pParameter); // pTempFinalMessage
1070 }
1071 else
1072 {
1073 /*
1074 * We failed to retrieve the parameter string before, so just
1075 * place back the original string placeholder.
1076 */
1077 cch = pParamData[i].pEndingAddress /* == pTempMessage */ - pParamData[i].pStartingAddress;
1078 StringCchCopyNW(pTempFinalMessage, cchBuffer, pParamData[i].pStartingAddress, cch);
1079 // cch = wcslen(pTempFinalMessage);
1080 }
1081 cchBuffer -= cch;
1082 pTempFinalMessage += cch;
1083 }
1084
1085 /* Append the last segment from pMessage */
1086 StringCchCopyW(pTempFinalMessage, cchBuffer, pTempMessage);
1087
1088 Cleanup:
1089
1090 // if (Status != ERROR_SUCCESS)
1091 // *pFinalMessage = NULL;
1092
1093 if (pParamData)
1094 {
1095 for (i = 0; i < dwParamCount; i++)
1096 {
1097 if (pParamData[i].pParameter)
1098 LocalFree(pParamData[i].pParameter);
1099 }
1100
1101 HeapFree(GetProcessHeap(), 0, pParamData);
1102 }
1103
1104 return Status;
1105 }
1106
1107
1108 /*
1109 * The following functions were adapted from
1110 * shell32!dialogs/filedefext.cpp:``SH_...'' functions.
1111 */
1112
1113 UINT
1114 FormatInteger(LONGLONG Num, LPWSTR pwszResult, UINT cchResultMax)
1115 {
1116 WCHAR wszNumber[24];
1117 WCHAR wszDecimalSep[8], wszThousandSep[8];
1118 NUMBERFMTW nf;
1119 WCHAR wszGrouping[12];
1120 INT cchGrouping;
1121 INT cchResult;
1122 INT i;
1123
1124 // Print the number in uniform mode
1125 swprintf(wszNumber, L"%I64u", Num);
1126
1127 // Get system strings for decimal and thousand separators.
1128 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, wszDecimalSep, _countof(wszDecimalSep));
1129 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, wszThousandSep, _countof(wszThousandSep));
1130
1131 // Initialize format for printing the number in bytes
1132 ZeroMemory(&nf, sizeof(nf));
1133 nf.lpDecimalSep = wszDecimalSep;
1134 nf.lpThousandSep = wszThousandSep;
1135
1136 // Get system string for groups separator
1137 cchGrouping = GetLocaleInfoW(LOCALE_USER_DEFAULT,
1138 LOCALE_SGROUPING,
1139 wszGrouping,
1140 _countof(wszGrouping));
1141
1142 // Convert grouping specs from string to integer
1143 for (i = 0; i < cchGrouping; i++)
1144 {
1145 WCHAR wch = wszGrouping[i];
1146
1147 if (wch >= L'0' && wch <= L'9')
1148 nf.Grouping = nf.Grouping * 10 + (wch - L'0');
1149 else if (wch != L';')
1150 break;
1151 }
1152
1153 if ((nf.Grouping % 10) == 0)
1154 nf.Grouping /= 10;
1155 else
1156 nf.Grouping *= 10;
1157
1158 // Format the number
1159 cchResult = GetNumberFormatW(LOCALE_USER_DEFAULT,
1160 0,
1161 wszNumber,
1162 &nf,
1163 pwszResult,
1164 cchResultMax);
1165
1166 if (!cchResult)
1167 return 0;
1168
1169 // GetNumberFormatW returns number of characters including UNICODE_NULL
1170 return cchResult - 1;
1171 }
1172
1173 UINT
1174 FormatByteSize(LONGLONG cbSize, LPWSTR pwszResult, UINT cchResultMax)
1175 {
1176 INT cchWritten;
1177 LPWSTR pwszEnd;
1178 size_t cchRemaining;
1179
1180 /* Write formated bytes count */
1181 cchWritten = FormatInteger(cbSize, pwszResult, cchResultMax);
1182 if (!cchWritten)
1183 return 0;
1184
1185 /* Copy " bytes" to buffer */
1186 pwszEnd = pwszResult + cchWritten;
1187 cchRemaining = cchResultMax - cchWritten;
1188 StringCchCopyExW(pwszEnd, cchRemaining, L" ", &pwszEnd, &cchRemaining, 0);
1189 cchWritten = LoadStringW(hInst, IDS_BYTES_FORMAT, pwszEnd, cchRemaining);
1190 cchRemaining -= cchWritten;
1191
1192 return cchResultMax - cchRemaining;
1193 }
1194
1195 LPWSTR
1196 FormatFileSizeWithBytes(const PULARGE_INTEGER lpQwSize, LPWSTR pwszResult, UINT cchResultMax)
1197 {
1198 UINT cchWritten;
1199 LPWSTR pwszEnd;
1200 size_t cchRemaining;
1201
1202 /* Format bytes in KBs, MBs etc */
1203 if (StrFormatByteSizeW(lpQwSize->QuadPart, pwszResult, cchResultMax) == NULL)
1204 return NULL;
1205
1206 /* If there is less bytes than 1KB, we have nothing to do */
1207 if (lpQwSize->QuadPart < 1024)
1208 return pwszResult;
1209
1210 /* Concatenate " (" */
1211 cchWritten = wcslen(pwszResult);
1212 pwszEnd = pwszResult + cchWritten;
1213 cchRemaining = cchResultMax - cchWritten;
1214 StringCchCopyExW(pwszEnd, cchRemaining, L" (", &pwszEnd, &cchRemaining, 0);
1215
1216 /* Write formated bytes count */
1217 cchWritten = FormatByteSize(lpQwSize->QuadPart, pwszEnd, cchRemaining);
1218 pwszEnd += cchWritten;
1219 cchRemaining -= cchWritten;
1220
1221 /* Copy ")" to the buffer */
1222 StringCchCopyW(pwszEnd, cchRemaining, L")");
1223
1224 return pwszResult;
1225 }
1226
1227 /* Adapted from shell32!dialogs/filedefext.cpp:``CFileDefExt::GetFileTimeString'' */
1228 BOOL
1229 GetFileTimeString(LPFILETIME lpFileTime, LPWSTR pwszResult, UINT cchResult)
1230 {
1231 FILETIME ft;
1232 SYSTEMTIME st;
1233 int cchWritten;
1234 size_t cchRemaining = cchResult;
1235 LPWSTR pwszEnd = pwszResult;
1236
1237 if (!FileTimeToLocalFileTime(lpFileTime, &ft) || !FileTimeToSystemTime(&ft, &st))
1238 return FALSE;
1239
1240 cchWritten = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, pwszEnd, cchRemaining);
1241 if (cchWritten)
1242 --cchWritten; // GetDateFormatW returns count with terminating zero
1243 // else
1244 // ERR("GetDateFormatW failed\n");
1245
1246 cchRemaining -= cchWritten;
1247 pwszEnd += cchWritten;
1248
1249 StringCchCopyExW(pwszEnd, cchRemaining, L", ", &pwszEnd, &cchRemaining, 0);
1250
1251 cchWritten = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, pwszEnd, cchRemaining);
1252 if (cchWritten)
1253 --cchWritten; // GetTimeFormatW returns count with terminating zero
1254 // else
1255 // ERR("GetTimeFormatW failed\n");
1256
1257 return TRUE;
1258 }
1259
1260
1261 HTREEITEM
1262 TreeViewAddItem(IN HWND hTreeView,
1263 IN HTREEITEM hParent,
1264 IN LPWSTR lpText,
1265 IN INT Image,
1266 IN INT SelectedImage,
1267 IN LPARAM lParam)
1268 {
1269 TV_INSERTSTRUCTW Insert;
1270
1271 ZeroMemory(&Insert, sizeof(Insert));
1272
1273 Insert.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
1274 Insert.hInsertAfter = TVI_LAST;
1275 Insert.hParent = hParent;
1276 Insert.item.pszText = lpText;
1277 Insert.item.iImage = Image;
1278 Insert.item.iSelectedImage = SelectedImage;
1279 Insert.item.lParam = lParam;
1280
1281 Insert.item.mask |= TVIF_STATE;
1282 Insert.item.stateMask = TVIS_OVERLAYMASK;
1283 Insert.item.state = INDEXTOOVERLAYMASK(1);
1284
1285 return TreeView_InsertItem(hTreeView, &Insert);
1286 }
1287
1288
1289 /* LOG HELPER FUNCTIONS *******************************************************/
1290
1291 PEVENTLOG
1292 AllocEventLog(IN PCWSTR ComputerName OPTIONAL,
1293 IN PCWSTR LogName,
1294 IN BOOL Permanent)
1295 {
1296 PEVENTLOG EventLog;
1297 UINT cchName;
1298
1299 /* Allocate a new event log entry */
1300 EventLog = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*EventLog));
1301 if (!EventLog)
1302 return NULL;
1303
1304 /* Allocate the computer name string (optional) and copy it */
1305 if (ComputerName)
1306 {
1307 cchName = wcslen(ComputerName) + 1;
1308 EventLog->ComputerName = HeapAlloc(GetProcessHeap(), 0, cchName * sizeof(WCHAR));
1309 if (EventLog->ComputerName)
1310 StringCchCopyW(EventLog->ComputerName, cchName, ComputerName);
1311 }
1312
1313 /* Allocate the event log name string and copy it */
1314 cchName = wcslen(LogName) + 1;
1315 EventLog->LogName = HeapAlloc(GetProcessHeap(), 0, cchName * sizeof(WCHAR));
1316 if (!EventLog->LogName)
1317 {
1318 if (EventLog->ComputerName)
1319 HeapFree(GetProcessHeap(), 0, EventLog->ComputerName);
1320 HeapFree(GetProcessHeap(), 0, EventLog);
1321 return NULL;
1322 }
1323 StringCchCopyW(EventLog->LogName, cchName, LogName);
1324
1325 EventLog->Permanent = Permanent;
1326
1327 return EventLog;
1328 }
1329
1330 VOID
1331 EventLog_Free(IN PEVENTLOG EventLog)
1332 {
1333 if (EventLog->LogName)
1334 HeapFree(GetProcessHeap(), 0, EventLog->LogName);
1335
1336 if (EventLog->ComputerName)
1337 HeapFree(GetProcessHeap(), 0, EventLog->ComputerName);
1338
1339 if (EventLog->FileName)
1340 HeapFree(GetProcessHeap(), 0, EventLog->FileName);
1341
1342 HeapFree(GetProcessHeap(), 0, EventLog);
1343 }
1344
1345
1346 PWSTR
1347 AllocAndCopyMultiStr(IN PCWSTR MultiStr OPTIONAL)
1348 {
1349 PWSTR pStr;
1350 ULONG Length;
1351
1352 if (!MultiStr)
1353 return NULL;
1354
1355 pStr = (PWSTR)MultiStr;
1356 while (*pStr) pStr += (wcslen(pStr) + 1);
1357 Length = MultiStr - pStr + 2;
1358
1359 pStr = HeapAlloc(GetProcessHeap(), 0, Length * sizeof(WCHAR));
1360 // NOTE: If we failed allocating the string, then fall back into no filter!
1361 if (pStr)
1362 RtlCopyMemory(pStr, MultiStr, Length * sizeof(WCHAR));
1363
1364 return pStr;
1365 }
1366
1367 PEVENTLOGFILTER
1368 AllocEventLogFilter(// IN PCWSTR FilterName,
1369 IN BOOL Information,
1370 IN BOOL Warning,
1371 IN BOOL Error,
1372 IN BOOL AuditSuccess,
1373 IN BOOL AuditFailure,
1374 IN PCWSTR Sources OPTIONAL,
1375 IN PCWSTR Users OPTIONAL,
1376 IN PCWSTR ComputerNames OPTIONAL,
1377 IN ULONG NumOfEventLogs,
1378 IN PEVENTLOG* EventLogs)
1379 {
1380 PEVENTLOGFILTER EventLogFilter;
1381
1382 /* Allocate a new event log filter entry, big enough to accommodate the list of logs */
1383 EventLogFilter = HeapAlloc(GetProcessHeap(),
1384 HEAP_ZERO_MEMORY,
1385 FIELD_OFFSET(EVENTLOGFILTER, EventLogs[NumOfEventLogs]));
1386 if (!EventLogFilter)
1387 return NULL;
1388
1389 EventLogFilter->Information = Information;
1390 EventLogFilter->Warning = Warning;
1391 EventLogFilter->Error = Error;
1392 EventLogFilter->AuditSuccess = AuditSuccess;
1393 EventLogFilter->AuditFailure = AuditFailure;
1394
1395 /* Allocate and copy the sources, users, and computers multi-strings */
1396 EventLogFilter->Sources = AllocAndCopyMultiStr(Sources);
1397 EventLogFilter->Users = AllocAndCopyMultiStr(Users);
1398 EventLogFilter->ComputerNames = AllocAndCopyMultiStr(ComputerNames);
1399
1400 /* Copy the list of event logs */
1401 EventLogFilter->NumOfEventLogs = NumOfEventLogs;
1402 RtlCopyMemory(EventLogFilter->EventLogs, EventLogs, NumOfEventLogs * sizeof(PEVENTLOG));
1403
1404 /* Initialize the filter reference count */
1405 EventLogFilter->ReferenceCount = 1;
1406
1407 return EventLogFilter;
1408 }
1409
1410 VOID
1411 EventLogFilter_Free(IN PEVENTLOGFILTER EventLogFilter)
1412 {
1413 if (EventLogFilter->Sources)
1414 HeapFree(GetProcessHeap(), 0, EventLogFilter->Sources);
1415
1416 if (EventLogFilter->Users)
1417 HeapFree(GetProcessHeap(), 0, EventLogFilter->Users);
1418
1419 if (EventLogFilter->ComputerNames)
1420 HeapFree(GetProcessHeap(), 0, EventLogFilter->ComputerNames);
1421
1422 HeapFree(GetProcessHeap(), 0, EventLogFilter);
1423 }
1424
1425 LONG EventLogFilter_AddRef(IN PEVENTLOGFILTER EventLogFilter)
1426 {
1427 ASSERT(EventLogFilter);
1428 return InterlockedIncrement(&EventLogFilter->ReferenceCount);
1429 }
1430
1431 LONG EventLogFilter_Release(IN PEVENTLOGFILTER EventLogFilter)
1432 {
1433 LONG RefCount;
1434
1435 ASSERT(EventLogFilter);
1436
1437 /* When the reference count reaches zero, delete the filter */
1438 RefCount = InterlockedDecrement(&EventLogFilter->ReferenceCount);
1439 if (RefCount <= 0)
1440 {
1441 /* Remove the filter from the list */
1442 /** RemoveEntryList(&EventLogFilter->ListEntry); **/
1443 EventLogFilter_Free(EventLogFilter);
1444 }
1445
1446 return RefCount;
1447 }
1448
1449 void
1450 TrimNulls(LPWSTR s)
1451 {
1452 WCHAR *c;
1453
1454 if (s != NULL)
1455 {
1456 c = s + wcslen(s) - 1;
1457 while (c >= s && iswspace(*c))
1458 --c;
1459 *++c = L'\0';
1460 }
1461 }
1462
1463 DWORD
1464 GetExpandedFilePathName(
1465 IN LPCWSTR ComputerName OPTIONAL,
1466 IN LPCWSTR lpFileName,
1467 OUT LPWSTR lpFullFileName OPTIONAL,
1468 IN DWORD nSize)
1469 {
1470 DWORD dwLength;
1471
1472 /* Determine the needed size after expansion of any environment strings */
1473 dwLength = ExpandEnvironmentStringsW(lpFileName, NULL, 0);
1474 if (dwLength == 0)
1475 {
1476 /* We failed, bail out */
1477 return 0;
1478 }
1479
1480 /* If the file path is on a remote computer, estimate its length */
1481 // FIXME: Use WNetGetUniversalName instead?
1482 if (ComputerName && *ComputerName)
1483 {
1484 /* Skip any leading backslashes */
1485 while (*ComputerName == L'\\')
1486 ++ComputerName;
1487
1488 if (*ComputerName)
1489 {
1490 /* Count 2 backslashes plus the computer name and one backslash separator */
1491 dwLength += 2 + wcslen(ComputerName) + 1;
1492 }
1493 }
1494
1495 /* Check whether we have enough space */
1496 if (dwLength > nSize)
1497 {
1498 /* No, return the needed size in characters (includes NULL-terminator) */
1499 return dwLength;
1500 }
1501
1502
1503 /* Now expand the file path */
1504 ASSERT(dwLength <= nSize);
1505
1506 /* Expand any existing environment strings */
1507 if (ExpandEnvironmentStringsW(lpFileName, lpFullFileName, dwLength) == 0)
1508 {
1509 /* We failed, bail out */
1510 return 0;
1511 }
1512
1513 /* If the file path is on a remote computer, retrieve the network share form of the file name */
1514 // FIXME: Use WNetGetUniversalName instead?
1515 if (ComputerName && *ComputerName)
1516 {
1517 /* Note that we previously skipped any potential leading backslashes */
1518
1519 /* Replace ':' by '$' in the drive letter */
1520 if (*lpFullFileName && lpFullFileName[1] == L':')
1521 lpFullFileName[1] = L'$';
1522
1523 /* Prepend the computer name */
1524 RtlMoveMemory(lpFullFileName + 2 + wcslen(ComputerName) + 1,
1525 lpFullFileName, dwLength * sizeof(WCHAR) - (2 + wcslen(ComputerName) + 1) * sizeof(WCHAR));
1526 lpFullFileName[0] = L'\\';
1527 lpFullFileName[1] = L'\\';
1528 wcsncpy(lpFullFileName + 2, ComputerName, wcslen(ComputerName));
1529 lpFullFileName[2 + wcslen(ComputerName)] = L'\\';
1530 }
1531
1532 /* Return the number of stored characters (includes NULL-terminator) */
1533 return dwLength;
1534 }
1535
1536 BOOL
1537 GetEventMessageFileDLL(IN LPCWSTR lpLogName,
1538 IN LPCWSTR SourceName,
1539 IN LPCWSTR EntryName,
1540 OUT PWCHAR lpModuleName) // TODO: Add IN DWORD BufLen
1541 {
1542 BOOL Success = FALSE;
1543 LONG Result;
1544 DWORD dwType, dwSize;
1545 WCHAR szModuleName[MAX_PATH];
1546 WCHAR szKeyName[MAX_PATH];
1547 HKEY hLogKey = NULL;
1548 HKEY hSourceKey = NULL;
1549
1550 StringCbCopyW(szKeyName, sizeof(szKeyName), EVENTLOG_BASE_KEY);
1551 StringCbCatW(szKeyName, sizeof(szKeyName), lpLogName);
1552
1553 Result = RegOpenKeyExW(hkMachine,
1554 szKeyName,
1555 0,
1556 KEY_READ,
1557 &hLogKey);
1558 if (Result != ERROR_SUCCESS)
1559 return FALSE;
1560
1561 Result = RegOpenKeyExW(hLogKey,
1562 SourceName,
1563 0,
1564 KEY_QUERY_VALUE,
1565 &hSourceKey);
1566 if (Result != ERROR_SUCCESS)
1567 {
1568 RegCloseKey(hLogKey);
1569 return FALSE;
1570 }
1571
1572 dwSize = sizeof(szModuleName);
1573 Result = RegQueryValueExW(hSourceKey,
1574 EntryName,
1575 NULL,
1576 &dwType,
1577 (LPBYTE)szModuleName,
1578 &dwSize);
1579 if ((Result != ERROR_SUCCESS) || (dwType != REG_EXPAND_SZ && dwType != REG_SZ))
1580 {
1581 szModuleName[0] = UNICODE_NULL;
1582 }
1583 else
1584 {
1585 /* NULL-terminate the string and expand it */
1586 szModuleName[dwSize / sizeof(WCHAR) - 1] = UNICODE_NULL;
1587 GetExpandedFilePathName(lpComputerName, szModuleName, lpModuleName, ARRAYSIZE(szModuleName));
1588 Success = TRUE;
1589 }
1590
1591 RegCloseKey(hSourceKey);
1592 RegCloseKey(hLogKey);
1593
1594 return Success;
1595 }
1596
1597 BOOL
1598 GetEventCategory(IN LPCWSTR KeyName,
1599 IN LPCWSTR SourceName,
1600 IN PEVENTLOGRECORD pevlr,
1601 OUT PWCHAR CategoryName) // TODO: Add IN DWORD BufLen
1602 {
1603 BOOL Success = FALSE;
1604 WCHAR szMessageDLL[MAX_PATH];
1605 LPWSTR lpMsgBuf = NULL;
1606
1607 if (!GetEventMessageFileDLL(KeyName, SourceName, EVENT_CATEGORY_MESSAGE_FILE, szMessageDLL))
1608 goto Quit;
1609
1610 /* Retrieve the message string without appending extra newlines */
1611 lpMsgBuf =
1612 GetMessageStringFromDllList(szMessageDLL,
1613 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
1614 FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
1615 pevlr->EventCategory,
1616 EVENT_MESSAGE_FILE_BUFFER,
1617 NULL);
1618 if (lpMsgBuf)
1619 {
1620 /* Trim the string */
1621 TrimNulls(lpMsgBuf);
1622
1623 /* Copy the category name */
1624 StringCchCopyW(CategoryName, MAX_PATH, lpMsgBuf);
1625
1626 /* Free the buffer allocated by FormatMessage */
1627 LocalFree(lpMsgBuf);
1628
1629 /* The ID was found and the message was formatted */
1630 Success = TRUE;
1631 }
1632
1633 Quit:
1634 if (!Success)
1635 {
1636 if (pevlr->EventCategory != 0)
1637 {
1638 StringCchPrintfW(CategoryName, MAX_PATH, L"(%lu)", pevlr->EventCategory);
1639 Success = TRUE;
1640 }
1641 }
1642
1643 return Success;
1644 }
1645
1646
1647 BOOL // NOTE: Used by evtdetctl.c
1648 GetEventMessage(IN LPCWSTR KeyName,
1649 IN LPCWSTR SourceName,
1650 IN PEVENTLOGRECORD pevlr,
1651 OUT PWCHAR EventText) // TODO: Add IN DWORD BufLen
1652 {
1653 BOOL Success = FALSE;
1654 DWORD i;
1655 size_t cch;
1656 WCHAR SourceModuleName[1024];
1657 WCHAR ParameterModuleName[1024];
1658 BOOL IsParamModNameCached = FALSE;
1659 LPWSTR lpMsgBuf = NULL;
1660 LPWSTR szStringArray, szMessage;
1661 LPWSTR *szArguments;
1662
1663 /* Get the event string array */
1664 szStringArray = (LPWSTR)((LPBYTE)pevlr + pevlr->StringOffset);
1665
1666 /* NOTE: GetEventMessageFileDLL can return a comma-separated list of DLLs */
1667 if (!GetEventMessageFileDLL(KeyName, SourceName, EVENT_MESSAGE_FILE, SourceModuleName))
1668 goto Quit;
1669
1670 /* Allocate space for insertion strings */
1671 szArguments = HeapAlloc(GetProcessHeap(), 0, pevlr->NumStrings * sizeof(LPVOID));
1672 if (!szArguments)
1673 goto Quit;
1674
1675 if (!IsParamModNameCached)
1676 {
1677 /* Now that the parameter file list is loaded, no need to reload it at the next run! */
1678 IsParamModNameCached = GetEventMessageFileDLL(KeyName, SourceName, EVENT_PARAMETER_MESSAGE_FILE, ParameterModuleName);
1679 // FIXME: If the string loading failed the first time, no need to retry it just after???
1680 }
1681
1682 if (IsParamModNameCached)
1683 {
1684 /* Not yet support for reading messages from parameter message DLL */
1685 }
1686
1687 szMessage = szStringArray;
1688 /*
1689 * HACK:
1690 * We do some hackish preformatting of the cached event strings...
1691 * That's because after we pass the string to FormatMessage
1692 * (via GetMessageStringFromDllList) with the FORMAT_MESSAGE_ARGUMENT_ARRAY
1693 * flag, instead of ignoring the insertion parameters and do the formatting
1694 * by ourselves. Therefore, the resulting string should have the parameter
1695 * string placeholders starting with a single '%' instead of a mix of one
1696 * and two '%'.
1697 */
1698 /* HACK part 1: Compute the full length of the string array */
1699 cch = 0;
1700 for (i = 0; i < pevlr->NumStrings; i++)
1701 {
1702 szMessage += wcslen(szMessage) + 1;
1703 }
1704 cch = szMessage - szStringArray;
1705
1706 /* HACK part 2: Now do the HACK proper! */
1707 szMessage = szStringArray;
1708 for (i = 0; i < pevlr->NumStrings; i++)
1709 {
1710 lpMsgBuf = szMessage;
1711 while ((lpMsgBuf = wcsstr(lpMsgBuf, L"%%")))
1712 {
1713 if (iswdigit(lpMsgBuf[2]))
1714 {
1715 RtlMoveMemory(lpMsgBuf, lpMsgBuf+1, ((szStringArray + cch) - lpMsgBuf - 1) * sizeof(WCHAR));
1716 }
1717 }
1718
1719 szArguments[i] = szMessage;
1720 szMessage += wcslen(szMessage) + 1;
1721 }
1722
1723 /* Retrieve the message string without appending extra newlines */
1724 lpMsgBuf =
1725 GetMessageStringFromDllList(SourceModuleName,
1726 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
1727 FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_MAX_WIDTH_MASK,
1728 pevlr->EventID,
1729 0,
1730 (va_list*)szArguments);
1731 if (lpMsgBuf)
1732 {
1733 /* Trim the string */
1734 TrimNulls(lpMsgBuf);
1735
1736 szMessage = NULL;
1737 Success = (ApplyParameterStringsToMessage(ParameterModuleName,
1738 TRUE,
1739 lpMsgBuf,
1740 &szMessage) == ERROR_SUCCESS);
1741 if (Success && szMessage)
1742 {
1743 /* Free the buffer allocated by FormatMessage */
1744 LocalFree(lpMsgBuf);
1745 lpMsgBuf = szMessage;
1746 }
1747
1748 /* Copy the event text */
1749 StringCchCopyW(EventText, EVENT_MESSAGE_EVENTTEXT_BUFFER, lpMsgBuf);
1750
1751 /* Free the buffer allocated by FormatMessage */
1752 LocalFree(lpMsgBuf);
1753 }
1754
1755 HeapFree(GetProcessHeap(), 0, szArguments);
1756
1757 Quit:
1758 if (!Success)
1759 {
1760 /* Get a read-only pointer to the "event-not-found" string */
1761 lpMsgBuf = HeapAlloc(GetProcessHeap(), 0, EVENT_MESSAGE_EVENTTEXT_BUFFER * sizeof(WCHAR));
1762 LoadStringW(hInst, IDS_EVENTSTRINGIDNOTFOUND, lpMsgBuf, EVENT_MESSAGE_EVENTTEXT_BUFFER);
1763 StringCchPrintfW(EventText, EVENT_MESSAGE_EVENTTEXT_BUFFER, lpMsgBuf, (pevlr->EventID & 0xFFFF), SourceName);
1764
1765 /* Append the strings */
1766 szMessage = szStringArray;
1767 for (i = 0; i < pevlr->NumStrings; i++)
1768 {
1769 StringCchCatW(EventText, EVENT_MESSAGE_EVENTTEXT_BUFFER, szMessage);
1770 StringCchCatW(EventText, EVENT_MESSAGE_EVENTTEXT_BUFFER, L"\n");
1771 szMessage += wcslen(szMessage) + 1;
1772 }
1773 }
1774
1775 return Success;
1776 }
1777
1778 VOID
1779 GetEventType(IN WORD dwEventType,
1780 OUT PWCHAR eventTypeText) // TODO: Add IN DWORD BufLen
1781 {
1782 switch (dwEventType)
1783 {
1784 case EVENTLOG_ERROR_TYPE:
1785 LoadStringW(hInst, IDS_EVENTLOG_ERROR_TYPE, eventTypeText, MAX_LOADSTRING);
1786 break;
1787 case EVENTLOG_WARNING_TYPE:
1788 LoadStringW(hInst, IDS_EVENTLOG_WARNING_TYPE, eventTypeText, MAX_LOADSTRING);
1789 break;
1790 case EVENTLOG_INFORMATION_TYPE:
1791 LoadStringW(hInst, IDS_EVENTLOG_INFORMATION_TYPE, eventTypeText, MAX_LOADSTRING);
1792 break;
1793 case EVENTLOG_SUCCESS:
1794 LoadStringW(hInst, IDS_EVENTLOG_SUCCESS, eventTypeText, MAX_LOADSTRING);
1795 break;
1796 case EVENTLOG_AUDIT_SUCCESS:
1797 LoadStringW(hInst, IDS_EVENTLOG_AUDIT_SUCCESS, eventTypeText, MAX_LOADSTRING);
1798 break;
1799 case EVENTLOG_AUDIT_FAILURE:
1800 LoadStringW(hInst, IDS_EVENTLOG_AUDIT_FAILURE, eventTypeText, MAX_LOADSTRING);
1801 break;
1802 default:
1803 LoadStringW(hInst, IDS_EVENTLOG_UNKNOWN_TYPE, eventTypeText, MAX_LOADSTRING);
1804 break;
1805 }
1806 }
1807
1808 BOOL
1809 GetEventUserName(IN PEVENTLOGRECORD pelr,
1810 IN OUT PSID *pLastSid,
1811 OUT PWCHAR pszUser) // TODO: Add IN DWORD BufLen
1812 {
1813 PSID pCurrentSid;
1814 PWSTR StringSid;
1815 WCHAR szName[1024];
1816 WCHAR szDomain[1024];
1817 SID_NAME_USE peUse;
1818 DWORD cchName = ARRAYSIZE(szName);
1819 DWORD cchDomain = ARRAYSIZE(szDomain);
1820 BOOL Success = FALSE;
1821
1822 /* Point to the SID */
1823 pCurrentSid = (PSID)((LPBYTE)pelr + pelr->UserSidOffset);
1824
1825 if (!IsValidSid(pCurrentSid))
1826 {
1827 *pLastSid = NULL;
1828 return FALSE;
1829 }
1830 else if (*pLastSid && EqualSid(*pLastSid, pCurrentSid))
1831 {
1832 return TRUE;
1833 }
1834
1835 /* User SID */
1836 if (pelr->UserSidLength > 0)
1837 {
1838 /*
1839 * Try to retrieve the user account name and domain name corresponding
1840 * to the SID. If it cannot be retrieved, try to convert the SID to a
1841 * string-form. It should not be bigger than the user-provided buffer
1842 * 'pszUser', otherwise we return an error.
1843 */
1844 if (LookupAccountSidW(lpComputerName,
1845 pCurrentSid,
1846 szName,
1847 &cchName,
1848 szDomain,
1849 &cchDomain,
1850 &peUse))
1851 {
1852 StringCchCopyW(pszUser, MAX_PATH, szName);
1853 Success = TRUE;
1854 }
1855 else if (ConvertSidToStringSidW(pCurrentSid, &StringSid))
1856 {
1857 /* Copy the string only if the user-provided buffer is big enough */
1858 if (wcslen(StringSid) + 1 <= MAX_PATH) // + 1 for NULL-terminator
1859 {
1860 StringCchCopyW(pszUser, MAX_PATH, StringSid);
1861 Success = TRUE;
1862 }
1863 else
1864 {
1865 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1866 Success = FALSE;
1867 }
1868
1869 /* Free the allocated buffer */
1870 LocalFree(StringSid);
1871 }
1872 }
1873
1874 *pLastSid = Success ? pCurrentSid : NULL;
1875
1876 return Success;
1877 }
1878
1879
1880 static VOID FreeRecords(VOID)
1881 {
1882 DWORD iIndex;
1883
1884 if (!g_RecordPtrs)
1885 return;
1886
1887 for (iIndex = 0; iIndex < g_TotalRecords; iIndex++)
1888 {
1889 if (g_RecordPtrs[iIndex])
1890 HeapFree(GetProcessHeap(), 0, g_RecordPtrs[iIndex]);
1891 }
1892 HeapFree(GetProcessHeap(), 0, g_RecordPtrs);
1893 g_RecordPtrs = NULL;
1894 g_TotalRecords = 0;
1895 }
1896
1897 BOOL
1898 FilterByType(IN PEVENTLOGFILTER EventLogFilter,
1899 IN PEVENTLOGRECORD pevlr)
1900 {
1901 if ((pevlr->EventType == EVENTLOG_SUCCESS && !EventLogFilter->Information ) ||
1902 (pevlr->EventType == EVENTLOG_INFORMATION_TYPE && !EventLogFilter->Information ) ||
1903 (pevlr->EventType == EVENTLOG_WARNING_TYPE && !EventLogFilter->Warning ) ||
1904 (pevlr->EventType == EVENTLOG_ERROR_TYPE && !EventLogFilter->Error ) ||
1905 (pevlr->EventType == EVENTLOG_AUDIT_SUCCESS && !EventLogFilter->AuditSuccess) ||
1906 (pevlr->EventType == EVENTLOG_AUDIT_FAILURE && !EventLogFilter->AuditFailure))
1907 {
1908 return FALSE;
1909 }
1910 return TRUE;
1911 }
1912
1913 BOOL
1914 FilterByString(IN PCWSTR FilterString, // This is a multi-string
1915 IN PWSTR String)
1916 {
1917 PCWSTR pStr;
1918
1919 /* The filter string is NULL so it does not filter anything */
1920 if (!FilterString)
1921 return TRUE;
1922
1923 /*
1924 * If the filter string filters for an empty string AND the source string
1925 * is an empty string, we have a match (particular case of the last one).
1926 */
1927 if (!*FilterString && !*String)
1928 return TRUE;
1929
1930 // if (*FilterString || *String)
1931
1932 /*
1933 * If the filter string is empty BUT the source string is not empty,
1934 * OR vice-versa, we cannot have a match.
1935 */
1936 if ( (!*FilterString && *String) || (*FilterString && !*String) )
1937 return FALSE;
1938
1939 /*
1940 * If the filter string filters for at least a non-empty string,
1941 * browse it and search for a string that matches the source string.
1942 */
1943 // else if (*FilterString && *String)
1944 {
1945 pStr = FilterString;
1946 while (*pStr)
1947 {
1948 if (wcsicmp(pStr, String) == 0)
1949 {
1950 /* We have a match, break the loop */
1951 break;
1952 }
1953
1954 pStr += (wcslen(pStr) + 1);
1955 }
1956 if (!*pStr) // && *String
1957 {
1958 /* We do not have a match */
1959 return FALSE;
1960 }
1961 }
1962
1963 /* We have a match */
1964 return TRUE;
1965 }
1966
1967 /*
1968 * The events enumerator thread.
1969 */
1970 static DWORD WINAPI
1971 EnumEventsThread(IN LPVOID lpParameter)
1972 {
1973 PEVENTLOGFILTER EventLogFilter = (PEVENTLOGFILTER)lpParameter;
1974 PEVENTLOG EventLog;
1975
1976 ULONG LogIndex;
1977 HANDLE hEventLog;
1978 PEVENTLOGRECORD pEvlr = NULL;
1979 PBYTE pEvlrEnd;
1980 PBYTE pEvlrBuffer;
1981 DWORD dwWanted, dwRead, dwNeeded, dwStatus = ERROR_SUCCESS;
1982 DWORD dwTotalRecords = 0, dwCurrentRecord = 0;
1983 DWORD dwFlags, dwMaxLength;
1984 size_t cchRemaining;
1985 LPWSTR lpszSourceName;
1986 LPWSTR lpszComputerName;
1987 BOOL bResult = TRUE; /* Read succeeded */
1988 HANDLE hProcessHeap = GetProcessHeap();
1989 PSID pLastSid = NULL;
1990
1991 UINT uStep = 0, uStepAt = 0, uPos = 0;
1992
1993 WCHAR szWindowTitle[MAX_PATH];
1994 WCHAR szStatusText[MAX_PATH];
1995 WCHAR szLocalDate[MAX_PATH];
1996 WCHAR szLocalTime[MAX_PATH];
1997 WCHAR szEventID[MAX_PATH];
1998 WCHAR szEventTypeText[MAX_LOADSTRING];
1999 WCHAR szCategoryID[MAX_PATH];
2000 WCHAR szUsername[MAX_PATH];
2001 WCHAR szNoUsername[MAX_PATH];
2002 WCHAR szCategory[MAX_PATH];
2003 WCHAR szNoCategory[MAX_PATH];
2004 PWCHAR lpTitleTemplateEnd;
2005
2006 SYSTEMTIME time;
2007 LVITEMW lviEventItem;
2008
2009 /* Save the current event log filter globally */
2010 EventLogFilter_AddRef(EventLogFilter);
2011 ActiveFilter = EventLogFilter;
2012
2013
2014 /** HACK!! **/
2015 EventLog = EventLogFilter->EventLogs[0];
2016
2017 // FIXME: Use something else instead of EventLog->LogName !!
2018
2019 /*
2020 * Use a different formatting, whether the event log filter holds
2021 * only one log, or many logs (the latter case is WIP TODO!)
2022 */
2023 if (EventLogFilter->NumOfEventLogs <= 1)
2024 {
2025 StringCchPrintfExW(szWindowTitle,
2026 ARRAYSIZE(szWindowTitle),
2027 &lpTitleTemplateEnd,
2028 &cchRemaining,
2029 0,
2030 szTitleTemplate, szTitle, EventLog->LogName); /* i = number of characters written */
2031 dwMaxLength = (DWORD)cchRemaining;
2032 if (!EventLog->ComputerName)
2033 GetComputerNameW(lpTitleTemplateEnd, &dwMaxLength);
2034 else
2035 StringCchCopyW(lpTitleTemplateEnd, dwMaxLength, EventLog->ComputerName);
2036
2037 StringCbPrintfW(szStatusText,
2038 sizeof(szStatusText),
2039 szStatusBarTemplate,
2040 EventLog->LogName,
2041 0,
2042 0);
2043 }
2044 else
2045 {
2046 // TODO: Use a different title & implement filtering for multi-log filters !!
2047 // (EventLogFilter->NumOfEventLogs > 1)
2048 MessageBoxW(hwndMainWindow,
2049 L"Many-logs filtering is not implemented yet!!",
2050 L"Event Log",
2051 MB_OK | MB_ICONINFORMATION);
2052 }
2053
2054 /* Set the window title */
2055 SetWindowTextW(hwndMainWindow, szWindowTitle);
2056
2057 /* Update the status bar */
2058 StatusBar_SetText(hwndStatus, 0, szStatusText);
2059
2060
2061 /* Disable list view redraw */
2062 SendMessageW(hwndListView, WM_SETREDRAW, FALSE, 0);
2063
2064 /* Clear the list view and free the cached records */
2065 ListView_DeleteAllItems(hwndListView);
2066 FreeRecords();
2067
2068 SendMessageW(hwndListView, LVM_PROGRESS, 0, TRUE);
2069 ProgressBar_SetRange(hwndStatusProgress, 0);
2070 StatusBar_SetText(hwndStatus, 0, NULL);
2071 ShowWindow(hwndStatusProgress, SW_SHOW);
2072
2073 /* Do a loop over the logs enumerated in the filter */
2074 // FIXME: For now we only support 1 event log per filter!
2075 LogIndex = 0;
2076 // for (LogIndex = 0; LogIndex < EventLogFilter->NumOfEventLogs; ++LogIndex)
2077 {
2078
2079 EventLog = EventLogFilter->EventLogs[LogIndex];
2080
2081 /* Open the event log */
2082 if (EventLog->Permanent)
2083 hEventLog = OpenEventLogW(EventLog->ComputerName, EventLog->LogName);
2084 else
2085 hEventLog = OpenBackupEventLogW(EventLog->ComputerName, EventLog->LogName); // FileName
2086
2087 if (hEventLog == NULL)
2088 {
2089 ShowWin32Error(GetLastError());
2090 goto Cleanup;
2091 }
2092
2093 // GetOldestEventLogRecord(hEventLog, &dwThisRecord);
2094
2095 /* Get the total number of event log records */
2096 GetNumberOfEventLogRecords(hEventLog, &dwTotalRecords);
2097
2098 if (dwTotalRecords > 0)
2099 {
2100 EnableMenuItem(hMainMenu, IDM_CLEAR_EVENTS, MF_BYCOMMAND | MF_ENABLED);
2101 EnableMenuItem(hMainMenu, IDM_SAVE_EVENTLOG, MF_BYCOMMAND | MF_ENABLED);
2102 }
2103 else
2104 {
2105 EnableMenuItem(hMainMenu, IDM_CLEAR_EVENTS, MF_BYCOMMAND | MF_GRAYED);
2106 EnableMenuItem(hMainMenu, IDM_SAVE_EVENTLOG, MF_BYCOMMAND | MF_GRAYED);
2107 }
2108
2109 /* Set up the event records cache */
2110 g_RecordPtrs = HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, dwTotalRecords * sizeof(*g_RecordPtrs));
2111 if (!g_RecordPtrs)
2112 {
2113 // ShowWin32Error(GetLastError());
2114 goto Quit;
2115 }
2116 g_TotalRecords = dwTotalRecords;
2117
2118 if (WaitForSingleObject(hStopEnumEvent, 0) == WAIT_OBJECT_0)
2119 goto Quit;
2120
2121 LoadStringW(hInst, IDS_NOT_AVAILABLE, szNoUsername, ARRAYSIZE(szNoUsername));
2122 LoadStringW(hInst, IDS_NONE, szNoCategory, ARRAYSIZE(szNoCategory));
2123
2124 ProgressBar_SetRange(hwndStatusProgress, MAKELPARAM(0, 100));
2125 uStepAt = (dwTotalRecords / 100) + 1;
2126
2127 dwFlags = EVENTLOG_SEQUENTIAL_READ | (Settings.bNewestEventsFirst ? EVENTLOG_FORWARDS_READ : EVENTLOG_BACKWARDS_READ);
2128
2129 /* 0x7ffff is the maximum buffer size ReadEventLog will accept */
2130 dwWanted = 0x7ffff;
2131 pEvlr = HeapAlloc(hProcessHeap, 0, dwWanted);
2132
2133 if (!pEvlr)
2134 goto Quit;
2135
2136 while (dwStatus == ERROR_SUCCESS)
2137 {
2138 bResult = ReadEventLogW(hEventLog, dwFlags, 0, pEvlr, dwWanted, &dwRead, &dwNeeded);
2139 dwStatus = GetLastError();
2140
2141 if (!bResult && dwStatus == ERROR_INSUFFICIENT_BUFFER)
2142 {
2143 pEvlr = HeapReAlloc(hProcessHeap, 0, pEvlr, dwNeeded);
2144 dwWanted = dwNeeded;
2145
2146 if (!pEvlr)
2147 break;
2148
2149 bResult = ReadEventLogW(hEventLog, dwFlags, 0, pEvlr, dwNeeded, &dwRead, &dwNeeded);
2150
2151 if (!bResult)
2152 break;
2153 }
2154 else if (!bResult)
2155 {
2156 /* Exit on other errors (ERROR_HANDLE_EOF) */
2157 break;
2158 }
2159
2160 pEvlrBuffer = (LPBYTE)pEvlr;
2161 pEvlrEnd = pEvlrBuffer + dwRead;
2162
2163 while (pEvlrBuffer < pEvlrEnd)
2164 {
2165 PEVENTLOGRECORD pEvlrTmp = (PEVENTLOGRECORD)pEvlrBuffer;
2166 PWSTR lpszUsername, lpszCategoryName;
2167 g_RecordPtrs[dwCurrentRecord] = NULL;
2168
2169 // ProgressBar_StepIt(hwndStatusProgress);
2170 uStep++;
2171 if (uStep % uStepAt == 0)
2172 {
2173 ++uPos;
2174 ProgressBar_SetPos(hwndStatusProgress, uPos);
2175 }
2176
2177 if (WaitForSingleObject(hStopEnumEvent, 0) == WAIT_OBJECT_0)
2178 goto Quit;
2179
2180 /* Filter by event type */
2181 if (!FilterByType(EventLogFilter, pEvlrTmp))
2182 goto SkipEvent;
2183
2184 /* Get the event source name and filter it */
2185 lpszSourceName = (LPWSTR)(pEvlrBuffer + sizeof(EVENTLOGRECORD));
2186 if (!FilterByString(EventLogFilter->Sources, lpszSourceName))
2187 goto SkipEvent;
2188
2189 /* Get the computer name and filter it */
2190 lpszComputerName = (LPWSTR)(pEvlrBuffer + sizeof(EVENTLOGRECORD) + (wcslen(lpszSourceName) + 1) * sizeof(WCHAR));
2191 if (!FilterByString(EventLogFilter->ComputerNames, lpszComputerName))
2192 goto SkipEvent;
2193
2194 /* Compute the event time */
2195 EventTimeToSystemTime(pEvlrTmp->TimeWritten, &time);
2196 GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time, NULL, szLocalDate, ARRAYSIZE(szLocalDate));
2197 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &time, NULL, szLocalTime, ARRAYSIZE(szLocalTime));
2198
2199 /* Get the username that generated the event, and filter it */
2200 lpszUsername = GetEventUserName(pEvlrTmp, &pLastSid, szUsername) ? szUsername : szNoUsername;
2201
2202 if (!FilterByString(EventLogFilter->Users, lpszUsername))
2203 goto SkipEvent;
2204
2205 // TODO: Filter by event ID and category
2206 GetEventType(pEvlrTmp->EventType, szEventTypeText);
2207
2208 lpszCategoryName = GetEventCategory(EventLog->LogName, lpszSourceName, pEvlrTmp, szCategory) ? szCategory : szNoCategory;
2209
2210 StringCbPrintfW(szEventID, sizeof(szEventID), L"%u", (pEvlrTmp->EventID & 0xFFFF));
2211 StringCbPrintfW(szCategoryID, sizeof(szCategoryID), L"%u", pEvlrTmp->EventCategory);
2212
2213 g_RecordPtrs[dwCurrentRecord] = HeapAlloc(hProcessHeap, 0, pEvlrTmp->Length);
2214 RtlCopyMemory(g_RecordPtrs[dwCurrentRecord], pEvlrTmp, pEvlrTmp->Length);
2215
2216 lviEventItem.mask = LVIF_IMAGE | LVIF_TEXT | LVIF_PARAM;
2217 lviEventItem.iItem = 0;
2218 lviEventItem.iSubItem = 0;
2219 lviEventItem.lParam = (LPARAM)g_RecordPtrs[dwCurrentRecord];
2220 lviEventItem.pszText = szEventTypeText;
2221
2222 switch (pEvlrTmp->EventType)
2223 {
2224 case EVENTLOG_SUCCESS:
2225 case EVENTLOG_INFORMATION_TYPE:
2226 lviEventItem.iImage = 0;
2227 break;
2228
2229 case EVENTLOG_WARNING_TYPE:
2230 lviEventItem.iImage = 1;
2231 break;
2232
2233 case EVENTLOG_ERROR_TYPE:
2234 lviEventItem.iImage = 2;
2235 break;
2236
2237 case EVENTLOG_AUDIT_SUCCESS:
2238 lviEventItem.iImage = 3;
2239 break;
2240
2241 case EVENTLOG_AUDIT_FAILURE:
2242 lviEventItem.iImage = 4;
2243 break;
2244 }
2245
2246 lviEventItem.iItem = ListView_InsertItem(hwndListView, &lviEventItem);
2247
2248 ListView_SetItemText(hwndListView, lviEventItem.iItem, 1, szLocalDate);
2249 ListView_SetItemText(hwndListView, lviEventItem.iItem, 2, szLocalTime);
2250 ListView_SetItemText(hwndListView, lviEventItem.iItem, 3, lpszSourceName);
2251 ListView_SetItemText(hwndListView, lviEventItem.iItem, 4, lpszCategoryName);
2252 ListView_SetItemText(hwndListView, lviEventItem.iItem, 5, szEventID);
2253 ListView_SetItemText(hwndListView, lviEventItem.iItem, 6, lpszUsername);
2254 ListView_SetItemText(hwndListView, lviEventItem.iItem, 7, lpszComputerName);
2255
2256 SkipEvent:
2257 pEvlrBuffer += pEvlrTmp->Length;
2258 dwCurrentRecord++;
2259 }
2260 }
2261
2262 Quit:
2263
2264 if (pEvlr)
2265 HeapFree(hProcessHeap, 0, pEvlr);
2266
2267 /* Close the event log */
2268 CloseEventLog(hEventLog);
2269
2270 } // end-for (LogIndex)
2271
2272 /* All events loaded */
2273
2274 Cleanup:
2275
2276 ShowWindow(hwndStatusProgress, SW_HIDE);
2277 SendMessageW(hwndListView, LVM_PROGRESS, 0, FALSE);
2278
2279 // FIXME: Use something else instead of EventLog->LogName !!
2280
2281 /*
2282 * Use a different formatting, whether the event log filter holds
2283 * only one log, or many logs (the latter case is WIP TODO!)
2284 */
2285 if (EventLogFilter->NumOfEventLogs <= 1)
2286 {
2287 StringCbPrintfW(szStatusText,
2288 sizeof(szStatusText),
2289 szStatusBarTemplate,
2290 EventLog->LogName,
2291 dwTotalRecords,
2292 ListView_GetItemCount(hwndListView));
2293 }
2294 else
2295 {
2296 // TODO: Use a different title & implement filtering for multi-log filters !!
2297 // (EventLogFilter->NumOfEventLogs > 1)
2298 }
2299
2300 /* Update the status bar */
2301 StatusBar_SetText(hwndStatus, 0, szStatusText);
2302
2303 /* Resume list view redraw */
2304 SendMessageW(hwndListView, WM_SETREDRAW, TRUE, 0);
2305
2306 EventLogFilter_Release(EventLogFilter);
2307
2308 CloseHandle(hStopEnumEvent);
2309 InterlockedExchangePointer((PVOID*)&hStopEnumEvent, NULL);
2310
2311 return 0;
2312 }
2313
2314 /*
2315 * The purpose of this thread is to serialize the creation of the events
2316 * enumeration thread, since the Event Log Viewer currently only supports
2317 * one view, one event list, one enumeration.
2318 */
2319 static DWORD WINAPI
2320 StartStopEnumEventsThread(IN LPVOID lpParameter)
2321 {
2322 HANDLE WaitHandles[2];
2323 DWORD WaitResult;
2324
2325 WaitHandles[0] = hStartStopEnumEvent; // End-of-application event
2326 WaitHandles[1] = hStartEnumEvent; // Command event
2327
2328 while (TRUE)
2329 {
2330 WaitResult = WaitForMultipleObjects(ARRAYSIZE(WaitHandles),
2331 WaitHandles,
2332 FALSE, // WaitAny
2333 INFINITE);
2334 switch (WaitResult)
2335 {
2336 case WAIT_OBJECT_0 + 0:
2337 {
2338 /* End-of-application event signaled, quit this thread */
2339
2340 /* Stop the previous enumeration */
2341 if (hEnumEventsThread)
2342 {
2343 if (hStopEnumEvent)
2344 {
2345 SetEvent(hStopEnumEvent);
2346 WaitForSingleObject(hEnumEventsThread, INFINITE);
2347 // NOTE: The following is done by the enumeration thread just before terminating.
2348 // hStopEnumEvent = NULL;
2349 }
2350
2351 CloseHandle(hEnumEventsThread);
2352 hEnumEventsThread = NULL;
2353 }
2354
2355 /* Clear the list view and free the cached records */
2356 ListView_DeleteAllItems(hwndListView);
2357 FreeRecords();
2358
2359 /* Reset the active filter */
2360 ActiveFilter = NULL;
2361
2362 return 0;
2363 }
2364
2365 case WAIT_OBJECT_0 + 1:
2366 {
2367 /* Restart a new enumeration if needed */
2368 PEVENTLOGFILTER EventLogFilter;
2369
2370 /* Stop the previous enumeration */
2371 if (hEnumEventsThread)
2372 {
2373 if (hStopEnumEvent)
2374 {
2375 SetEvent(hStopEnumEvent);
2376 WaitForSingleObject(hEnumEventsThread, INFINITE);
2377 // NOTE: The following is done by the enumeration thread just before terminating.
2378 // hStopEnumEvent = NULL;
2379 }
2380
2381 CloseHandle(hEnumEventsThread);
2382 hEnumEventsThread = NULL;
2383 }
2384
2385 /* Clear the list view and free the cached records */
2386 ListView_DeleteAllItems(hwndListView);
2387 FreeRecords();
2388
2389 /* Reset the active filter */
2390 ActiveFilter = NULL;
2391
2392 EventLogFilter = InterlockedExchangePointer((PVOID*)&EnumFilter, NULL);
2393 if (!EventLogFilter)
2394 break;
2395
2396 // Manual-reset event
2397 hStopEnumEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
2398 if (!hStopEnumEvent)
2399 break;
2400
2401 hEnumEventsThread = CreateThread(NULL,
2402 0,
2403 EnumEventsThread,
2404 (LPVOID)EventLogFilter,
2405 CREATE_SUSPENDED,
2406 NULL);
2407 if (!hEnumEventsThread)
2408 {
2409 CloseHandle(hStopEnumEvent);
2410 hStopEnumEvent = NULL;
2411 break;
2412 }
2413 // CloseHandle(hEnumEventsThread);
2414 ResumeThread(hEnumEventsThread);
2415
2416 break;
2417 }
2418
2419 default:
2420 {
2421 /* Unknown command, must never go there! */
2422 return GetLastError();
2423 }
2424 }
2425 }
2426
2427 return 0;
2428 }
2429
2430 VOID
2431 EnumEvents(IN PEVENTLOGFILTER EventLogFilter)
2432 {
2433 /* Signal the enumerator thread we want to enumerate events */
2434 InterlockedExchangePointer((PVOID*)&EnumFilter, EventLogFilter);
2435 SetEvent(hStartEnumEvent);
2436 return;
2437 }
2438
2439
2440 PEVENTLOGFILTER
2441 GetSelectedFilter(OUT HTREEITEM* phti OPTIONAL)
2442 {
2443 TVITEMEXW tvItemEx;
2444 HTREEITEM hti;
2445
2446 if (phti)
2447 *phti = NULL;
2448
2449 /* Get index of selected item */
2450 hti = TreeView_GetSelection(hwndTreeView);
2451 if (hti == NULL)
2452 return NULL; // No filter
2453
2454 tvItemEx.mask = TVIF_PARAM;
2455 tvItemEx.hItem = hti;
2456
2457 TreeView_GetItem(hwndTreeView, &tvItemEx);
2458
2459 if (phti)
2460 *phti = tvItemEx.hItem;
2461
2462 return (PEVENTLOGFILTER)tvItemEx.lParam;
2463 }
2464
2465
2466 VOID
2467 OpenUserEventLogFile(IN LPCWSTR lpszFileName)
2468 {
2469 WIN32_FIND_DATAW FindData;
2470 HANDLE hFind;
2471 PEVENTLOG EventLog;
2472 PEVENTLOGFILTER EventLogFilter;
2473 SIZE_T cchFileName;
2474 HTREEITEM hItem = NULL;
2475
2476 /* Check whether the file actually exists */
2477 hFind = FindFirstFileW(lpszFileName, &FindData);
2478 if (hFind == INVALID_HANDLE_VALUE)
2479 {
2480 ShowWin32Error(GetLastError());
2481 return;
2482 }
2483 FindClose(hFind);
2484
2485 /* Allocate a new event log entry */
2486 EventLog = AllocEventLog(NULL, lpszFileName, FALSE);
2487 if (EventLog == NULL)
2488 {
2489 ShowWin32Error(ERROR_NOT_ENOUGH_MEMORY);
2490 return;
2491 }
2492
2493 /* Allocate a new event log filter entry for this event log */
2494 EventLogFilter = AllocEventLogFilter(// LogName,
2495 TRUE, TRUE, TRUE, TRUE, TRUE,
2496 NULL, NULL, NULL,
2497 1, &EventLog);
2498 if (EventLogFilter == NULL)
2499 {
2500 ShowWin32Error(ERROR_NOT_ENOUGH_MEMORY);
2501 EventLog_Free(EventLog);
2502 return;
2503 }
2504
2505 /* Add the event log and the filter into their lists */
2506 InsertTailList(&EventLogList, &EventLog->ListEntry);
2507 InsertTailList(&EventLogFilterList, &EventLogFilter->ListEntry);
2508
2509 /* Retrieve and cache the event log file */
2510 cchFileName = wcslen(lpszFileName) + 1;
2511 EventLog->FileName = HeapAlloc(GetProcessHeap(), 0, cchFileName * sizeof(WCHAR));
2512 if (EventLog->FileName)
2513 StringCchCopyW(EventLog->FileName, cchFileName, lpszFileName);
2514
2515 hItem = TreeViewAddItem(hwndTreeView, htiUserLogs,
2516 (LPWSTR)lpszFileName,
2517 2, 3, (LPARAM)EventLogFilter);
2518
2519 /* Select the event log */
2520 if (hItem)
2521 {
2522 // TreeView_Expand(hwndTreeView, htiUserLogs, TVE_EXPAND);
2523 TreeView_SelectItem(hwndTreeView, hItem);
2524 TreeView_EnsureVisible(hwndTreeView, hItem);
2525 }
2526 InvalidateRect(hwndTreeView, NULL, FALSE);
2527 SetFocus(hwndTreeView);
2528 }
2529
2530 VOID
2531 OpenUserEventLog(VOID)
2532 {
2533 WCHAR szFileName[MAX_PATH];
2534
2535 ZeroMemory(szFileName, sizeof(szFileName));
2536
2537 sfn.lpstrFile = szFileName;
2538 sfn.nMaxFile = ARRAYSIZE(szFileName);
2539
2540 if (!GetOpenFileNameW(&sfn))
2541 return;
2542 sfn.lpstrFile[sfn.nMaxFile-1] = UNICODE_NULL;
2543
2544 OpenUserEventLogFile(sfn.lpstrFile);
2545 }
2546
2547 VOID
2548 SaveEventLog(IN PEVENTLOGFILTER EventLogFilter)
2549 {
2550 PEVENTLOG EventLog;
2551 HANDLE hEventLog;
2552 WCHAR szFileName[MAX_PATH];
2553
2554 /* Bail out if there is no available filter */
2555 if (!EventLogFilter)
2556 return;
2557
2558 ZeroMemory(szFileName, sizeof(szFileName));
2559
2560 sfn.lpstrFile = szFileName;
2561 sfn.nMaxFile = ARRAYSIZE(szFileName);
2562
2563 if (!GetSaveFileNameW(&sfn))
2564 return;
2565
2566 EventLogFilter_AddRef(EventLogFilter);
2567
2568 EventLog = EventLogFilter->EventLogs[0];
2569 hEventLog = OpenEventLogW(EventLog->ComputerName, EventLog->LogName);
2570
2571 EventLogFilter_Release(EventLogFilter);
2572
2573 if (!hEventLog)
2574 {
2575 ShowWin32Error(GetLastError());
2576 return;
2577 }
2578
2579 if (!BackupEventLogW(hEventLog, szFileName))
2580 ShowWin32Error(GetLastError());
2581
2582 CloseEventLog(hEventLog);
2583 }
2584
2585 VOID
2586 CloseUserEventLog(IN PEVENTLOGFILTER EventLogFilter, IN HTREEITEM hti)
2587 {
2588 /* Bail out if there is no available filter */
2589 if (!EventLogFilter)
2590 return;
2591
2592 if (InterlockedCompareExchangePointer((PVOID*)&ActiveFilter, NULL, NULL) == EventLogFilter)
2593 {
2594 /* Signal the enumerator thread we want to stop enumerating events */
2595 // EnumEvents(NULL);
2596 InterlockedExchangePointer((PVOID*)&EnumFilter, NULL);
2597 SetEvent(hStartEnumEvent);
2598 }
2599
2600 /*
2601 * The deletion of the item automatically triggers a TVN_SELCHANGED
2602 * notification, that will reset the ActiveFilter (in case the item
2603 * selected is a filter). Otherwise we reset it there.
2604 */
2605 TreeView_DeleteItem(hwndTreeView, hti);
2606
2607 /* Remove the filter from the list */
2608 RemoveEntryList(&EventLogFilter->ListEntry);
2609 EventLogFilter_Release(EventLogFilter);
2610
2611 // /* Select the default event log */
2612 // // TreeView_Expand(hwndTreeView, htiUserLogs, TVE_EXPAND);
2613 // TreeView_SelectItem(hwndTreeView, hItem);
2614 // TreeView_EnsureVisible(hwndTreeView, hItem);
2615 InvalidateRect(hwndTreeView, NULL, FALSE);
2616 SetFocus(hwndTreeView);
2617 }
2618
2619
2620 BOOL
2621 ClearEvents(IN PEVENTLOGFILTER EventLogFilter)
2622 {
2623 BOOL Success;
2624 PEVENTLOG EventLog;
2625 HANDLE hEventLog;
2626 WCHAR szFileName[MAX_PATH];
2627 WCHAR szMessage[MAX_LOADSTRING];
2628
2629 /* Bail out if there is no available filter */
2630 if (!EventLogFilter)
2631 return FALSE;
2632
2633 ZeroMemory(szFileName, sizeof(szFileName));
2634 ZeroMemory(szMessage, sizeof(szMessage));
2635
2636 LoadStringW(hInst, IDS_CLEAREVENTS_MSG, szMessage, ARRAYSIZE(szMessage));
2637
2638 sfn.lpstrFile = szFileName;
2639 sfn.nMaxFile = ARRAYSIZE(szFileName);
2640
2641 switch (MessageBoxW(hwndMainWindow, szMessage, szTitle, MB_YESNOCANCEL | MB_ICONINFORMATION))
2642 {
2643 case IDCANCEL:
2644 return FALSE;
2645
2646 case IDNO:
2647 sfn.lpstrFile = NULL;
2648 break;
2649
2650 case IDYES:
2651 if (!GetSaveFileNameW(&sfn))
2652 return FALSE;
2653 break;
2654 }
2655
2656 EventLogFilter_AddRef(EventLogFilter);
2657
2658 EventLog = EventLogFilter->EventLogs[0];
2659 hEventLog = OpenEventLogW(EventLog->ComputerName, EventLog->LogName);
2660
2661 EventLogFilter_Release(EventLogFilter);
2662
2663 if (!hEventLog)
2664 {
2665 ShowWin32Error(GetLastError());
2666 return FALSE;
2667 }
2668
2669 Success = ClearEventLogW(hEventLog, sfn.lpstrFile);
2670 if (!Success)
2671 ShowWin32Error(GetLastError());
2672
2673 CloseEventLog(hEventLog);
2674 return Success;
2675 }
2676
2677
2678 VOID
2679 Refresh(IN PEVENTLOGFILTER EventLogFilter)
2680 {
2681 /* Bail out if there is no available filter */
2682 if (!EventLogFilter)
2683 return;
2684
2685 /* Reenumerate the events through the filter */
2686 EnumEvents(EventLogFilter);
2687 }
2688
2689
2690 ATOM
2691 MyRegisterClass(HINSTANCE hInstance)
2692 {
2693 WNDCLASSEXW wcex;
2694
2695 wcex.cbSize = sizeof(wcex);
2696 wcex.style = 0;
2697 wcex.lpfnWndProc = WndProc;
2698 wcex.cbClsExtra = 0;
2699 wcex.cbWndExtra = 0;
2700 wcex.hInstance = hInstance;
2701 wcex.hIcon = LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_EVENTVWR));
2702 wcex.hCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_ARROW));
2703 wcex.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); // COLOR_WINDOW + 1
2704 wcex.lpszMenuName = MAKEINTRESOURCEW(IDM_EVENTVWR);
2705 wcex.lpszClassName = EVENTVWR_WNDCLASS;
2706 wcex.hIconSm = (HICON)LoadImageW(hInstance,
2707 MAKEINTRESOURCEW(IDI_EVENTVWR),
2708 IMAGE_ICON,
2709 16,
2710 16,
2711 LR_SHARED);
2712
2713 return RegisterClassExW(&wcex);
2714 }
2715
2716
2717 BOOL
2718 GetDisplayNameFileAndID(IN LPCWSTR lpLogName,
2719 OUT PWCHAR lpModuleName, // TODO: Add IN DWORD BufLen
2720 OUT PDWORD pdwMessageID)
2721 {
2722 BOOL Success = FALSE;
2723 LONG Result;
2724 HKEY hLogKey;
2725 WCHAR *KeyPath;
2726 SIZE_T cbKeyPath;
2727 DWORD dwType, cbData;
2728 DWORD dwMessageID = 0;
2729 WCHAR szModuleName[MAX_PATH];
2730
2731 /* Use a default value for the message ID */
2732 *pdwMessageID = 0;
2733
2734 cbKeyPath = (wcslen(EVENTLOG_BASE_KEY) + wcslen(lpLogName) + 1) * sizeof(WCHAR);
2735 KeyPath = HeapAlloc(GetProcessHeap(), 0, cbKeyPath);
2736 if (!KeyPath)
2737 return FALSE;
2738
2739 StringCbCopyW(KeyPath, cbKeyPath, EVENTLOG_BASE_KEY);
2740 StringCbCatW(KeyPath, cbKeyPath, lpLogName);
2741
2742 Result = RegOpenKeyExW(hkMachine, KeyPath, 0, KEY_QUERY_VALUE, &hLogKey);
2743 HeapFree(GetProcessHeap(), 0, KeyPath);
2744 if (Result != ERROR_SUCCESS)
2745 return FALSE;
2746
2747 cbData = sizeof(szModuleName);
2748 Result = RegQueryValueExW(hLogKey,
2749 L"DisplayNameFile",
2750 NULL,
2751 &dwType,
2752 (LPBYTE)szModuleName,
2753 &cbData);
2754 if ((Result != ERROR_SUCCESS) || (dwType != REG_EXPAND_SZ && dwType != REG_SZ))
2755 {
2756 szModuleName[0] = UNICODE_NULL;
2757 }
2758 else
2759 {
2760 /* NULL-terminate the string and expand it */
2761 szModuleName[cbData / sizeof(WCHAR) - 1] = UNICODE_NULL;
2762 GetExpandedFilePathName(lpComputerName, szModuleName, lpModuleName, ARRAYSIZE(szModuleName));
2763 Success = TRUE;
2764 }
2765
2766 /*
2767 * If we have a 'DisplayNameFile', query for 'DisplayNameID';
2768 * otherwise it's not really useful. 'DisplayNameID' is optional.
2769 */
2770 if (Success)
2771 {
2772 cbData = sizeof(dwMessageID);
2773 Result = RegQueryValueExW(hLogKey,
2774 L"DisplayNameID",
2775 NULL,
2776 &dwType,
2777 (LPBYTE)&dwMessageID,
2778 &cbData);
2779 if ((Result != ERROR_SUCCESS) || (dwType != REG_DWORD))
2780 dwMessageID = 0;
2781
2782 *pdwMessageID = dwMessageID;
2783 }
2784
2785 RegCloseKey(hLogKey);
2786
2787 return Success;
2788 }
2789
2790
2791 VOID
2792 BuildLogListAndFilterList(IN LPCWSTR lpComputerName)
2793 {
2794 LONG Result;
2795 HKEY hEventLogKey, hLogKey;
2796 DWORD dwNumLogs = 0;
2797 DWORD dwIndex, dwMaxKeyLength;
2798 DWORD dwType;
2799 PEVENTLOG EventLog;
2800 PEVENTLOGFILTER EventLogFilter;
2801 LPWSTR LogName = NULL;
2802 WCHAR szModuleName[MAX_PATH];
2803 DWORD lpcName;
2804 DWORD dwMessageID;
2805 LPWSTR lpDisplayName;
2806 HTREEITEM hRootNode = NULL, hItem = NULL, hItemDefault = NULL;
2807
2808 if (hkMachine && hkMachine != HKEY_LOCAL_MACHINE)
2809 {
2810 /* We are connected to some other computer, close the old connection */
2811 RegCloseKey(hkMachine);
2812 hkMachine = NULL;
2813 }
2814 if (!lpComputerName || !*lpComputerName)
2815 {
2816 /* Use the local computer registry */
2817 hkMachine = HKEY_LOCAL_MACHINE;
2818 }
2819 else
2820 {
2821 /* Connect to the remote computer registry */
2822 Result = RegConnectRegistry(lpComputerName, HKEY_LOCAL_MACHINE, &hkMachine);
2823 if (Result != ERROR_SUCCESS)
2824 {
2825 /* Connection failed, display a message and bail out */
2826 hkMachine = NULL;
2827 ShowWin32Error(GetLastError());
2828 return;
2829 }
2830 }
2831
2832 /* Open the EventLog key */
2833 Result = RegOpenKeyExW(hkMachine, EVENTLOG_BASE_KEY, 0, KEY_READ, &hEventLogKey);
2834 if (Result != ERROR_SUCCESS)
2835 {
2836 return;
2837 }
2838
2839 /* Retrieve the number of event logs enumerated as registry keys */
2840 Result = RegQueryInfoKeyW(hEventLogKey, NULL, NULL, NULL, &dwNumLogs, &dwMaxKeyLength,
2841 NULL, NULL, NULL, NULL, NULL, NULL);
2842 if (Result != ERROR_SUCCESS)
2843 {
2844 goto Quit;
2845 }
2846 if (!dwNumLogs)
2847 goto Quit;
2848
2849 /* Take the NULL terminator into account */
2850 ++dwMaxKeyLength;
2851
2852 /* Allocate the temporary buffer */
2853 LogName = HeapAlloc(GetProcessHeap(), 0, dwMaxKeyLength * sizeof(WCHAR));
2854 if (!LogName)
2855 goto Quit;
2856
2857 /* Enumerate and retrieve each event log name */
2858 for (dwIndex = 0; dwIndex < dwNumLogs; dwIndex++)
2859 {
2860 lpcName = dwMaxKeyLength;
2861 Result = RegEnumKeyExW(hEventLogKey, dwIndex, LogName, &lpcName, NULL, NULL, NULL, NULL);
2862 if (Result != ERROR_SUCCESS)
2863 continue;
2864
2865 /* Take the NULL terminator into account */
2866 ++lpcName;
2867
2868 /* Allocate a new event log entry */
2869 EventLog = AllocEventLog(lpComputerName, LogName, TRUE);
2870 if (EventLog == NULL)
2871 continue;
2872
2873 /* Allocate a new event log filter entry for this event log */
2874 EventLogFilter = AllocEventLogFilter(// LogName,
2875 TRUE, TRUE, TRUE, TRUE, TRUE,
2876 NULL, NULL, NULL,
2877 1, &EventLog);
2878 if (EventLogFilter == NULL)
2879 {
2880 EventLog_Free(EventLog);
2881 continue;
2882 }
2883
2884 /* Add the event log and the filter into their lists */
2885 InsertTailList(&EventLogList, &EventLog->ListEntry);
2886 InsertTailList(&EventLogFilterList, &EventLogFilter->ListEntry);
2887
2888 EventLog->FileName = NULL;
2889
2890 /* Retrieve and cache the event log file */
2891 Result = RegOpenKeyExW(hEventLogKey,
2892 LogName,
2893 0,
2894 KEY_QUERY_VALUE,
2895 &hLogKey);
2896 if (Result == ERROR_SUCCESS)
2897 {
2898 lpcName = 0;
2899 Result = RegQueryValueExW(hLogKey,
2900 L"File",
2901 NULL,
2902 &dwType,
2903 NULL,
2904 &lpcName);
2905 if ((Result != ERROR_SUCCESS) || (dwType != REG_EXPAND_SZ && dwType != REG_SZ))
2906 {
2907 // Windows' EventLog uses some kind of default value, we do not.
2908 EventLog->FileName = NULL;
2909 }
2910 else
2911 {
2912 lpcName = ROUND_DOWN(lpcName, sizeof(WCHAR));
2913 EventLog->FileName = HeapAlloc(GetProcessHeap(), 0, lpcName);
2914 if (EventLog->FileName)
2915 {
2916 Result = RegQueryValueExW(hLogKey,
2917 L"File",
2918 NULL,
2919 &dwType,
2920 (LPBYTE)EventLog->FileName,
2921 &lpcName);
2922 if (Result != ERROR_SUCCESS)
2923 {
2924 HeapFree(GetProcessHeap(), 0, EventLog->FileName);
2925 EventLog->FileName = NULL;
2926 }
2927 else
2928 {
2929 EventLog->FileName[lpcName / sizeof(WCHAR) - 1] = UNICODE_NULL;
2930 }
2931 }
2932 }
2933
2934 RegCloseKey(hLogKey);
2935 }
2936
2937 /* Get the display name for the event log */
2938 lpDisplayName = NULL;
2939
2940 ZeroMemory(szModuleName, sizeof(szModuleName));
2941 if (GetDisplayNameFileAndID(LogName, szModuleName, &dwMessageID))
2942 {
2943 /* Retrieve the message string without appending extra newlines */
2944 lpDisplayName =
2945 GetMessageStringFromDll(szModuleName,
2946 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
2947 FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
2948 dwMessageID,
2949 0,
2950 NULL);
2951 }
2952
2953 /*
2954 * Select the correct tree root node, whether the log is a System
2955 * or an Application log. Default to Application log otherwise.
2956 */
2957 hRootNode = htiAppLogs;
2958 for (lpcName = 0; lpcName < ARRAYSIZE(SystemLogs); ++lpcName)
2959 {
2960 /* Check whether the log name is part of the system logs */
2961 if (wcsicmp(LogName, SystemLogs[lpcName]) == 0)
2962 {
2963 hRootNode = htiSystemLogs;
2964 break;
2965 }
2966 }
2967
2968 hItem = TreeViewAddItem(hwndTreeView, hRootNode,
2969 (lpDisplayName ? lpDisplayName : LogName),
2970 2, 3, (LPARAM)EventLogFilter);
2971
2972 /* Try to get the default event log: "Application" */
2973 if ((hItemDefault == NULL) && (wcsicmp(LogName, SystemLogs[0]) == 0))
2974 {
2975 hItemDefault = hItem;
2976 }
2977
2978 /* Free the buffer allocated by FormatMessage */
2979 if (lpDisplayName)
2980 LocalFree(lpDisplayName);
2981 }
2982
2983 HeapFree(GetProcessHeap(), 0, LogName);
2984
2985 Quit:
2986 RegCloseKey(hEventLogKey);
2987
2988 /* Select the default event log */
2989 if (hItemDefault)
2990 {
2991 // TreeView_Expand(hwndTreeView, hRootNode, TVE_EXPAND);
2992 TreeView_SelectItem(hwndTreeView, hItemDefault);
2993 TreeView_EnsureVisible(hwndTreeView, hItemDefault);
2994 }
2995 InvalidateRect(hwndTreeView, NULL, FALSE);
2996 SetFocus(hwndTreeView);
2997
2998 return;
2999 }
3000
3001 VOID
3002 FreeLogList(VOID)
3003 {
3004 PLIST_ENTRY Entry;
3005 PEVENTLOG EventLog;
3006
3007 while (!IsListEmpty(&EventLogList))
3008 {
3009 Entry = RemoveHeadList(&EventLogList);
3010 EventLog = (PEVENTLOG)CONTAINING_RECORD(Entry, EVENTLOG, ListEntry);
3011 EventLog_Free(EventLog);
3012 }
3013
3014 return;
3015 }
3016
3017 VOID
3018 FreeLogFilterList(VOID)
3019 {
3020 PLIST_ENTRY Entry;
3021 PEVENTLOGFILTER EventLogFilter;
3022
3023 while (!IsListEmpty(&EventLogFilterList))
3024 {
3025 Entry = RemoveHeadList(&EventLogFilterList);
3026 EventLogFilter = (PEVENTLOGFILTER)CONTAINING_RECORD(Entry, EVENTLOGFILTER, ListEntry);
3027 EventLogFilter_Free(EventLogFilter);
3028 }
3029
3030 ActiveFilter = NULL;
3031
3032 return;
3033 }
3034
3035 BOOL
3036 InitInstance(HINSTANCE hInstance)
3037 {
3038 RECT rcClient, rs;
3039 LONG StatusHeight;
3040 HIMAGELIST hSmall;
3041 LVCOLUMNW lvc = {0};
3042 WCHAR szTemp[256];
3043
3044 /* Create the main window */
3045 rs = Settings.wpPos.rcNormalPosition;
3046 hwndMainWindow = CreateWindowW(EVENTVWR_WNDCLASS,
3047 szTitle,
3048 WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
3049 rs.left, rs.top,
3050 (rs.right != CW_USEDEFAULT && rs.left != CW_USEDEFAULT) ? rs.right - rs.left : CW_USEDEFAULT,
3051 (rs.bottom != CW_USEDEFAULT && rs.top != CW_USEDEFAULT) ? rs.bottom - rs.top : CW_USEDEFAULT,
3052 NULL,
3053 NULL,
3054 hInstance,
3055 NULL);
3056 if (!hwndMainWindow)
3057 return FALSE;
3058
3059 /* Create the status bar */
3060 hwndStatus = CreateWindowExW(0, // no extended styles
3061 STATUSCLASSNAMEW, // status bar
3062 L"", // no text
3063 WS_CHILD | WS_VISIBLE | CCS_BOTTOM | SBARS_SIZEGRIP, // styles
3064 0, 0, 0, 0, // x, y, cx, cy
3065 hwndMainWindow, // parent window
3066 (HMENU)100, // window ID
3067 hInstance, // instance
3068 NULL); // window data
3069
3070 GetClientRect(hwndMainWindow, &rcClient);
3071 GetWindowRect(hwndStatus, &rs);
3072 StatusHeight = rs.bottom - rs.top;
3073
3074 /* Create a progress bar in the status bar (hidden by default) */
3075 StatusBar_GetItemRect(hwndStatus, 0, &rs);
3076 hwndStatusProgress = CreateWindowExW(0, // no extended styles
3077 PROGRESS_CLASSW, // status bar
3078 NULL, // no text
3079 WS_CHILD | PBS_SMOOTH, // styles
3080 rs.left, rs.top, // x, y
3081 rs.right - rs.left, rs.bottom - rs.top, // cx, cy
3082 hwndStatus, // parent window
3083 NULL, // window ID
3084 hInstance, // instance
3085 NULL); // window data
3086 /* Remove its static edge */
3087 SetWindowLongPtrW(hwndStatusProgress, GWL_EXSTYLE,
3088 GetWindowLongPtrW(hwndStatusProgress, GWL_EXSTYLE) & ~WS_EX_STATICEDGE);
3089 ProgressBar_SetStep(hwndStatusProgress, 1);
3090
3091 /* Initialize the splitter default positions */
3092 nVSplitPos = Settings.nVSplitPos;
3093 nHSplitPos = Settings.nHSplitPos;
3094
3095 /* Create the TreeView */
3096 hwndTreeView = CreateWindowExW(WS_EX_CLIENTEDGE,
3097 WC_TREEVIEWW,
3098 NULL,
3099 // WS_CHILD | WS_VISIBLE | TVS_HASLINES | TVS_SHOWSELALWAYS,
3100 WS_CHILD | WS_VISIBLE | /* WS_TABSTOP | */ TVS_HASLINES | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_EDITLABELS | TVS_SHOWSELALWAYS,
3101 0, 0,
3102 nVSplitPos - SPLIT_WIDTH/2,
3103 (rcClient.bottom - rcClient.top) - StatusHeight,
3104 hwndMainWindow,
3105 NULL,
3106 hInstance,
3107 NULL);
3108
3109 /* Create the ImageList */
3110 hSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
3111 GetSystemMetrics(SM_CYSMICON),
3112 ILC_COLOR32 | ILC_MASK, // ILC_COLOR24
3113 1, 1);
3114
3115 /* Add event type icons to the ImageList: closed/opened folder, event log (normal/viewed) */
3116 ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_CLOSED_CATEGORY)));
3117 ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_OPENED_CATEGORY)));
3118 ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_EVENTLOG)));
3119 ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_EVENTVWR)));
3120
3121 /* Assign the ImageList to the Tree View */
3122 TreeView_SetImageList(hwndTreeView, hSmall, TVSIL_NORMAL);
3123
3124 /* Add the event logs nodes */
3125 // "System Logs"
3126 LoadStringW(hInstance, IDS_EVENTLOG_SYSTEM, szTemp, ARRAYSIZE(szTemp));
3127 htiSystemLogs = TreeViewAddItem(hwndTreeView, NULL, szTemp, 0, 1, (LPARAM)NULL);
3128 // "Application Logs"
3129 LoadStringW(hInstance, IDS_EVENTLOG_APP, szTemp, ARRAYSIZE(szTemp));
3130 htiAppLogs = TreeViewAddItem(hwndTreeView, NULL, szTemp, 0, 1, (LPARAM)NULL);
3131 // "User Logs"
3132 LoadStringW(hInstance, IDS_EVENTLOG_USER, szTemp, ARRAYSIZE(szTemp));
3133 htiUserLogs = TreeViewAddItem(hwndTreeView, NULL, szTemp, 0, 1, (LPARAM)NULL);
3134
3135 /* Create the Event details pane (optional) */
3136 hwndEventDetails = CreateEventDetailsCtrl(hInst, hwndMainWindow, (LPARAM)NULL);
3137 if (hwndEventDetails)
3138 {
3139 SetWindowLongPtrW(hwndEventDetails, GWL_EXSTYLE,
3140 GetWindowLongPtrW(hwndEventDetails, GWL_EXSTYLE) | WS_EX_CLIENTEDGE);
3141 SetWindowPos(hwndEventDetails, NULL,
3142 nVSplitPos + SPLIT_WIDTH/2,
3143 nHSplitPos + SPLIT_WIDTH/2,
3144 (rcClient.right - rcClient.left) - nVSplitPos - SPLIT_WIDTH/2,
3145 (rcClient.bottom - rcClient.top) - nHSplitPos - SPLIT_WIDTH/2 - StatusHeight,
3146 SWP_NOZORDER | SWP_NOACTIVATE | (Settings.bShowDetailsPane ? SWP_SHOWWINDOW : SWP_HIDEWINDOW));
3147 }
3148
3149 /* Create the ListView */
3150 hwndListView = CreateWindowExW(WS_EX_CLIENTEDGE,
3151 WC_LISTVIEWW,
3152 NULL,
3153 WS_CHILD | WS_VISIBLE | LVS_SHOWSELALWAYS | LVS_REPORT,
3154 nVSplitPos + SPLIT_WIDTH/