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