318e5dea5aa0e915012c8bf520089537e5ace744
[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
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 /* Stop the enumerator thread */
446 SetEvent(hStartStopEnumEvent);
447 WaitForSingleObject(hThread, INFINITE);
448 CloseHandle(hThread);
449
450 /* Free the filters list and the event logs list */
451 FreeLogFilterList();
452 FreeLogList();
453
454 Cleanup:
455 /* Handle cleanup */
456 if (hStartEnumEvent)
457 CloseHandle(hStartEnumEvent);
458 if (hStartStopEnumEvent)
459 CloseHandle(hStartStopEnumEvent);
460
461 Quit:
462 /* Final cleanup */
463 if (lpszzUserLogsToLoad)
464 {
465 cbUserLogsSize = 0;
466 HeapFree(GetProcessHeap(), 0, lpszzUserLogsToLoad);
467 lpszzUserLogsToLoad = NULL;
468 }
469 if (lpComputerName)
470 {
471 HeapFree(GetProcessHeap(), 0, lpComputerName);
472 lpComputerName = NULL;
473 }
474 FreeLibrary(hRichEdit);
475
476 return (int)msg.wParam;
477 }
478
479
480 /* GENERIC HELPER FUNCTIONS ***************************************************/
481
482 VOID
483 EventTimeToSystemTime(IN DWORD EventTime,
484 OUT PSYSTEMTIME pSystemTime)
485 {
486 SYSTEMTIME st1970 = { 1970, 1, 0, 1, 0, 0, 0, 0 };
487 FILETIME ftLocal;
488 union
489 {
490 FILETIME ft;
491 ULONGLONG ll;
492 } u1970, uUCT;
493
494 uUCT.ft.dwHighDateTime = 0;
495 uUCT.ft.dwLowDateTime = EventTime;
496 SystemTimeToFileTime(&st1970, &u1970.ft);
497 uUCT.ll = uUCT.ll * 10000000 + u1970.ll;
498 FileTimeToLocalFileTime(&uUCT.ft, &ftLocal);
499 FileTimeToSystemTime(&ftLocal, pSystemTime);
500 }
501
502 /*
503 * This function takes in entry a path to a single DLL, in which
504 * the message string of ID dwMessageId has to be searched.
505 * The other parameters are similar to those of the FormatMessageW API.
506 */
507 LPWSTR
508 GetMessageStringFromDll(
509 IN LPCWSTR lpMessageDll,
510 IN DWORD dwFlags, // If we always use the same flags, just remove this param...
511 IN DWORD dwMessageId,
512 IN DWORD nSize,
513 IN va_list* Arguments OPTIONAL)
514 {
515 HMODULE hLibrary;
516 DWORD dwLength;
517 LPWSTR lpMsgBuf = NULL;
518
519 hLibrary = LoadLibraryExW(lpMessageDll, NULL,
520 /* LOAD_LIBRARY_AS_IMAGE_RESOURCE | */ LOAD_LIBRARY_AS_DATAFILE);
521 if (hLibrary == NULL)
522 return NULL;
523
524 /* Sanitize dwFlags */
525 dwFlags &= ~FORMAT_MESSAGE_FROM_STRING;
526 dwFlags |= FORMAT_MESSAGE_FROM_HMODULE;
527
528 _SEH2_TRY
529 {
530 /*
531 * Retrieve the message string without appending extra newlines.
532 * Wrap in SEH to protect from invalid string parameters.
533 */
534 _SEH2_TRY
535 {
536 dwLength = FormatMessageW(dwFlags,
537 /* FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
538 FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, */
539 hLibrary,
540 dwMessageId,
541 LANG_USER_DEFAULT,
542 (LPWSTR)&lpMsgBuf,
543 nSize,
544 Arguments);
545 }
546 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
547 {
548 dwLength = 0;
549
550 /*
551 * An exception occurred while calling FormatMessage, this is usually
552 * the sign that a parameter was invalid, either 'lpMsgBuf' was NULL
553 * but we did not pass the flag FORMAT_MESSAGE_ALLOCATE_BUFFER, or the
554 * array pointer 'Arguments' was NULL or did not contain enough elements,
555 * and we did not pass the flag FORMAT_MESSAGE_IGNORE_INSERTS, and the
556 * message string expected too many inserts.
557 * In this last case only, we can call again FormatMessage but ignore
558 * explicitly the inserts. The string that we will return to the user
559 * will not be pre-formatted.
560 */
561 if (((dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) || lpMsgBuf) &&
562 !(dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS))
563 {
564 /* Remove any possible harmful flags and always ignore inserts */
565 dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY;
566 dwFlags |= FORMAT_MESSAGE_IGNORE_INSERTS;
567
568 /* If this call also throws an exception, we are really dead */
569 dwLength = FormatMessageW(dwFlags,
570 hLibrary,
571 dwMessageId,
572 LANG_USER_DEFAULT,
573 (LPWSTR)&lpMsgBuf,
574 nSize,
575 NULL /* Arguments */);
576 }
577 }
578 _SEH2_END;
579 }
580 _SEH2_FINALLY
581 {
582 FreeLibrary(hLibrary);
583 }
584 _SEH2_END;
585
586 if (dwLength == 0)
587 {
588 ASSERT(lpMsgBuf == NULL);
589 lpMsgBuf = NULL;
590 }
591 else
592 {
593 ASSERT(lpMsgBuf);
594 }
595
596 return lpMsgBuf;
597 }
598
599 /*
600 * This function takes in entry a comma-separated list of DLLs, in which
601 * the message string of ID dwMessageId has to be searched.
602 * The other parameters are similar to those of the FormatMessageW API.
603 */
604 LPWSTR
605 GetMessageStringFromDllList(
606 IN LPCWSTR lpMessageDllList,
607 IN DWORD dwFlags, // If we always use the same flags, just remove this param...
608 IN DWORD dwMessageId,
609 IN DWORD nSize,
610 IN va_list* Arguments OPTIONAL)
611 {
612 BOOL Success = FALSE;
613 SIZE_T cbLength;
614 LPWSTR szMessageDllList;
615 LPWSTR szDll;
616 LPWSTR lpMsgBuf = NULL;
617
618 /* Allocate a local buffer for the DLL list that can be tokenized */
619 // TODO: Optimize that!! Maybe we can cleverly use lpMessageDllList in read/write mode
620 // and cleverly temporarily replace the ';' by UNICODE_NULL, do our job, then reverse the change.
621 cbLength = (wcslen(lpMessageDllList) + 1) * sizeof(WCHAR);
622 szMessageDllList = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbLength);
623 if (!szMessageDllList)
624 return NULL;
625 RtlCopyMemory(szMessageDllList, lpMessageDllList, cbLength);
626
627 /* Loop through the list of message DLLs */
628 szDll = wcstok(szMessageDllList, EVENT_DLL_SEPARATOR);
629 while ((szDll != NULL) && !Success)
630 {
631 // Uses LANG_USER_DEFAULT
632 lpMsgBuf = GetMessageStringFromDll(szDll,
633 dwFlags,
634 dwMessageId,
635 nSize,
636 Arguments);
637 if (lpMsgBuf)
638 {
639 /* The ID was found and the message was formatted */
640 Success = TRUE;
641 break;
642 }
643
644 /*
645 * The DLL could not be loaded, or the message could not be found,
646 * try the next DLL, if any.
647 */
648 szDll = wcstok(NULL, EVENT_DLL_SEPARATOR);
649 }
650
651 HeapFree(GetProcessHeap(), 0, szMessageDllList);
652
653 return lpMsgBuf;
654 }
655
656
657 typedef struct
658 {
659 LPWSTR pStartingAddress; // Pointer to the beginning of a parameter string in pMessage
660 LPWSTR pEndingAddress; // Pointer to the end of a parameter string in pMessage
661 DWORD pParameterID; // Parameter identifier found in pMessage
662 LPWSTR pParameter; // Actual parameter string
663 } param_strings_format_data;
664
665 DWORD
666 ApplyParameterStringsToMessage(
667 IN LPCWSTR lpMessageDllList,
668 IN BOOL bMessagePreFormatted,
669 IN CONST LPCWSTR pMessage,
670 OUT LPWSTR* pFinalMessage)
671 {
672 /*
673 * This code is heavily adapted from the MSDN example:
674 * https://msdn.microsoft.com/en-us/library/windows/desktop/bb427356.aspx
675 * with bugs removed.
676 */
677
678 DWORD Status = ERROR_SUCCESS;
679 DWORD dwParamCount = 0; // Number of insertion strings found in pMessage
680 size_t cchBuffer = 0; // Size of the buffer in characters
681 size_t cchParams = 0; // Number of characters in all the parameter strings
682 size_t cch = 0;
683 DWORD i = 0;
684 param_strings_format_data* pParamData = NULL; // Array of pointers holding information about each parameter string in pMessage
685 LPWSTR pTempMessage = (LPWSTR)pMessage;
686 LPWSTR pTempFinalMessage = NULL;
687
688 *pFinalMessage = NULL;
689
690 /* Determine the number of parameter insertion strings in pMessage */
691 if (bMessagePreFormatted)
692 {
693 while ((pTempMessage = wcschr(pTempMessage, L'%')))
694 {
695 pTempMessage++;
696 if (iswdigit(*pTempMessage))
697 {
698 dwParamCount++;
699 while (iswdigit(*++pTempMessage)) ;
700 }
701 }
702 }
703 else
704 {
705 while ((pTempMessage = wcsstr(pTempMessage, L"%%")))
706 {
707 pTempMessage += 2;
708 if (iswdigit(*pTempMessage))
709 {
710 dwParamCount++;
711 while (iswdigit(*++pTempMessage)) ;
712 }
713 }
714 }
715
716 /* If there are no parameter insertion strings in pMessage, just return */
717 if (dwParamCount == 0)
718 {
719 // *pFinalMessage = NULL;
720 goto Cleanup;
721 }
722
723 /* Allocate the array of parameter string format data */
724 pParamData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwParamCount * sizeof(param_strings_format_data));
725 if (!pParamData)
726 {
727 Status = ERROR_OUTOFMEMORY;
728 goto Cleanup;
729 }
730
731 /*
732 * Retrieve each parameter in pMessage and the beginning and end of the
733 * insertion string, as well as the message identifier of the parameter.
734 */
735 pTempMessage = (LPWSTR)pMessage;
736 if (bMessagePreFormatted)
737 {
738 while ((pTempMessage = wcschr(pTempMessage, L'%')) && (i < dwParamCount))
739 {
740 pTempMessage++;
741 if (iswdigit(*pTempMessage))
742 {
743 pParamData[i].pStartingAddress = pTempMessage-1;
744 pParamData[i].pParameterID = (DWORD)_wtol(pTempMessage);
745
746 while (iswdigit(*++pTempMessage)) ;
747
748 pParamData[i].pEndingAddress = pTempMessage;
749 i++;
750 }
751 }
752 }
753 else
754 {
755 while ((pTempMessage = wcsstr(pTempMessage, L"%%")) && (i < dwParamCount))
756 {
757 pTempMessage += 2;
758 if (iswdigit(*pTempMessage))
759 {
760 pParamData[i].pStartingAddress = pTempMessage-2;
761 pParamData[i].pParameterID = (DWORD)_wtol(pTempMessage);
762
763 while (iswdigit(*++pTempMessage)) ;
764
765 pParamData[i].pEndingAddress = pTempMessage;
766 i++;
767 }
768 }
769 }
770
771 /* Retrieve each parameter string */
772 for (i = 0; i < dwParamCount; i++)
773 {
774 // pParamData[i].pParameter = GetMessageString(pParamData[i].pParameterID, 0, NULL);
775 pParamData[i].pParameter =
776 GetMessageStringFromDllList(lpMessageDllList,
777 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
778 FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
779 pParamData[i].pParameterID,
780 0, NULL);
781 if (!pParamData[i].pParameter)
782 {
783 /* Skip the insertion string */
784 continue;
785 }
786
787 cchParams += wcslen(pParamData[i].pParameter);
788 }
789
790 /*
791 * Allocate the final message buffer, the size of which is based on the
792 * length of the original message and the length of each parameter string.
793 */
794 cchBuffer = wcslen(pMessage) + cchParams + 1;
795 *pFinalMessage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cchBuffer * sizeof(WCHAR));
796 if (!*pFinalMessage)
797 {
798 Status = ERROR_OUTOFMEMORY;
799 goto Cleanup;
800 }
801
802 pTempFinalMessage = *pFinalMessage;
803
804 /* Build the final message string */
805 pTempMessage = (LPWSTR)pMessage;
806 for (i = 0; i < dwParamCount; i++)
807 {
808 /* Append the segment from pMessage */
809 cch = pParamData[i].pStartingAddress - pTempMessage;
810 StringCchCopyNW(pTempFinalMessage, cchBuffer, pTempMessage, cch);
811 pTempMessage = pParamData[i].pEndingAddress;
812 cchBuffer -= cch;
813 pTempFinalMessage += cch;
814
815 /* Append the parameter string */
816 if (pParamData[i].pParameter)
817 {
818 StringCchCopyW(pTempFinalMessage, cchBuffer, pParamData[i].pParameter);
819 cch = wcslen(pParamData[i].pParameter); // pTempFinalMessage
820 }
821 else
822 {
823 /*
824 * We failed to retrieve the parameter string before, so just
825 * place back the original string placeholder.
826 */
827 cch = pParamData[i].pEndingAddress /* == pTempMessage */ - pParamData[i].pStartingAddress;
828 StringCchCopyNW(pTempFinalMessage, cchBuffer, pParamData[i].pStartingAddress, cch);
829 // cch = wcslen(pTempFinalMessage);
830 }
831 cchBuffer -= cch;
832 pTempFinalMessage += cch;
833 }
834
835 /* Append the last segment from pMessage */
836 StringCchCopyW(pTempFinalMessage, cchBuffer, pTempMessage);
837
838 Cleanup:
839
840 // if (Status != ERROR_SUCCESS)
841 // *pFinalMessage = NULL;
842
843 if (pParamData)
844 {
845 for (i = 0; i < dwParamCount; i++)
846 {
847 if (pParamData[i].pParameter)
848 LocalFree(pParamData[i].pParameter);
849 }
850
851 HeapFree(GetProcessHeap(), 0, pParamData);
852 }
853
854 return Status;
855 }
856
857
858 /*
859 * The following functions were adapted from
860 * shell32!dialogs/filedefext.cpp:``SH_...'' functions.
861 */
862
863 UINT
864 FormatInteger(LONGLONG Num, LPWSTR pwszResult, UINT cchResultMax)
865 {
866 WCHAR wszNumber[24];
867 WCHAR wszDecimalSep[8], wszThousandSep[8];
868 NUMBERFMTW nf;
869 WCHAR wszGrouping[12];
870 INT cchGrouping;
871 INT cchResult;
872 INT i;
873
874 // Print the number in uniform mode
875 swprintf(wszNumber, L"%I64u", Num);
876
877 // Get system strings for decimal and thousand separators.
878 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, wszDecimalSep, _countof(wszDecimalSep));
879 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, wszThousandSep, _countof(wszThousandSep));
880
881 // Initialize format for printing the number in bytes
882 ZeroMemory(&nf, sizeof(nf));
883 nf.lpDecimalSep = wszDecimalSep;
884 nf.lpThousandSep = wszThousandSep;
885
886 // Get system string for groups separator
887 cchGrouping = GetLocaleInfoW(LOCALE_USER_DEFAULT,
888 LOCALE_SGROUPING,
889 wszGrouping,
890 _countof(wszGrouping));
891
892 // Convert grouping specs from string to integer
893 for (i = 0; i < cchGrouping; i++)
894 {
895 WCHAR wch = wszGrouping[i];
896
897 if (wch >= L'0' && wch <= L'9')
898 nf.Grouping = nf.Grouping * 10 + (wch - L'0');
899 else if (wch != L';')
900 break;
901 }
902
903 if ((nf.Grouping % 10) == 0)
904 nf.Grouping /= 10;
905 else
906 nf.Grouping *= 10;
907
908 // Format the number
909 cchResult = GetNumberFormatW(LOCALE_USER_DEFAULT,
910 0,
911 wszNumber,
912 &nf,
913 pwszResult,
914 cchResultMax);
915
916 if (!cchResult)
917 return 0;
918
919 // GetNumberFormatW returns number of characters including UNICODE_NULL
920 return cchResult - 1;
921 }
922
923 UINT
924 FormatByteSize(LONGLONG cbSize, LPWSTR pwszResult, UINT cchResultMax)
925 {
926 INT cchWritten;
927 LPWSTR pwszEnd;
928 size_t cchRemaining;
929
930 /* Write formated bytes count */
931 cchWritten = FormatInteger(cbSize, pwszResult, cchResultMax);
932 if (!cchWritten)
933 return 0;
934
935 /* Copy " bytes" to buffer */
936 pwszEnd = pwszResult + cchWritten;
937 cchRemaining = cchResultMax - cchWritten;
938 StringCchCopyExW(pwszEnd, cchRemaining, L" ", &pwszEnd, &cchRemaining, 0);
939 cchWritten = LoadStringW(hInst, IDS_BYTES_FORMAT, pwszEnd, cchRemaining);
940 cchRemaining -= cchWritten;
941
942 return cchResultMax - cchRemaining;
943 }
944
945 LPWSTR
946 FormatFileSizeWithBytes(const PULARGE_INTEGER lpQwSize, LPWSTR pwszResult, UINT cchResultMax)
947 {
948 UINT cchWritten;
949 LPWSTR pwszEnd;
950 size_t cchRemaining;
951
952 /* Format bytes in KBs, MBs etc */
953 if (StrFormatByteSizeW(lpQwSize->QuadPart, pwszResult, cchResultMax) == NULL)
954 return NULL;
955
956 /* If there is less bytes than 1KB, we have nothing to do */
957 if (lpQwSize->QuadPart < 1024)
958 return pwszResult;
959
960 /* Concatenate " (" */
961 cchWritten = wcslen(pwszResult);
962 pwszEnd = pwszResult + cchWritten;
963 cchRemaining = cchResultMax - cchWritten;
964 StringCchCopyExW(pwszEnd, cchRemaining, L" (", &pwszEnd, &cchRemaining, 0);
965
966 /* Write formated bytes count */
967 cchWritten = FormatByteSize(lpQwSize->QuadPart, pwszEnd, cchRemaining);
968 pwszEnd += cchWritten;
969 cchRemaining -= cchWritten;
970
971 /* Copy ")" to the buffer */
972 StringCchCopyW(pwszEnd, cchRemaining, L")");
973
974 return pwszResult;
975 }
976
977 /* Adapted from shell32!dialogs/filedefext.cpp:``CFileDefExt::GetFileTimeString'' */
978 BOOL
979 GetFileTimeString(LPFILETIME lpFileTime, LPWSTR pwszResult, UINT cchResult)
980 {
981 FILETIME ft;
982 SYSTEMTIME st;
983 int cchWritten;
984 size_t cchRemaining = cchResult;
985 LPWSTR pwszEnd = pwszResult;
986
987 if (!FileTimeToLocalFileTime(lpFileTime, &ft) || !FileTimeToSystemTime(&ft, &st))
988 return FALSE;
989
990 cchWritten = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, pwszEnd, cchRemaining);
991 if (cchWritten)
992 --cchWritten; // GetDateFormatW returns count with terminating zero
993 // else
994 // ERR("GetDateFormatW failed\n");
995
996 cchRemaining -= cchWritten;
997 pwszEnd += cchWritten;
998
999 StringCchCopyExW(pwszEnd, cchRemaining, L", ", &pwszEnd, &cchRemaining, 0);
1000
1001 cchWritten = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, pwszEnd, cchRemaining);
1002 if (cchWritten)
1003 --cchWritten; // GetTimeFormatW returns count with terminating zero
1004 // else
1005 // ERR("GetTimeFormatW failed\n");
1006
1007 return TRUE;
1008 }
1009
1010
1011 HTREEITEM
1012 TreeViewAddItem(IN HWND hTreeView,
1013 IN HTREEITEM hParent,
1014 IN LPWSTR lpText,
1015 IN INT Image,
1016 IN INT SelectedImage,
1017 IN LPARAM lParam)
1018 {
1019 TV_INSERTSTRUCTW Insert;
1020
1021 ZeroMemory(&Insert, sizeof(Insert));
1022
1023 Insert.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
1024 Insert.hInsertAfter = TVI_LAST;
1025 Insert.hParent = hParent;
1026 Insert.item.pszText = lpText;
1027 Insert.item.iImage = Image;
1028 Insert.item.iSelectedImage = SelectedImage;
1029 Insert.item.lParam = lParam;
1030
1031 Insert.item.mask |= TVIF_STATE;
1032 Insert.item.stateMask = TVIS_OVERLAYMASK;
1033 Insert.item.state = INDEXTOOVERLAYMASK(1);
1034
1035 return TreeView_InsertItem(hTreeView, &Insert);
1036 }
1037
1038
1039 /* LOG HELPER FUNCTIONS *******************************************************/
1040
1041 PEVENTLOG
1042 AllocEventLog(IN PCWSTR ComputerName OPTIONAL,
1043 IN PCWSTR LogName,
1044 IN BOOL Permanent)
1045 {
1046 PEVENTLOG EventLog;
1047 UINT cchName;
1048
1049 /* Allocate a new event log entry */
1050 EventLog = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*EventLog));
1051 if (!EventLog)
1052 return NULL;
1053
1054 /* Allocate the computer name string (optional) and copy it */
1055 if (ComputerName)
1056 {
1057 cchName = wcslen(ComputerName) + 1;
1058 EventLog->ComputerName = HeapAlloc(GetProcessHeap(), 0, cchName * sizeof(WCHAR));
1059 if (EventLog->ComputerName)
1060 StringCchCopyW(EventLog->ComputerName, cchName, ComputerName);
1061 }
1062
1063 /* Allocate the event log name string and copy it */
1064 cchName = wcslen(LogName) + 1;
1065 EventLog->LogName = HeapAlloc(GetProcessHeap(), 0, cchName * sizeof(WCHAR));
1066 if (!EventLog->LogName)
1067 {
1068 if (EventLog->ComputerName)
1069 HeapFree(GetProcessHeap(), 0, EventLog->ComputerName);
1070 HeapFree(GetProcessHeap(), 0, EventLog);
1071 return NULL;
1072 }
1073 StringCchCopyW(EventLog->LogName, cchName, LogName);
1074
1075 EventLog->Permanent = Permanent;
1076
1077 return EventLog;
1078 }
1079
1080 VOID
1081 EventLog_Free(IN PEVENTLOG EventLog)
1082 {
1083 if (EventLog->LogName)
1084 HeapFree(GetProcessHeap(), 0, EventLog->LogName);
1085
1086 if (EventLog->ComputerName)
1087 HeapFree(GetProcessHeap(), 0, EventLog->ComputerName);
1088
1089 if (EventLog->FileName)
1090 HeapFree(GetProcessHeap(), 0, EventLog->FileName);
1091
1092 HeapFree(GetProcessHeap(), 0, EventLog);
1093 }
1094
1095
1096 PWSTR
1097 AllocAndCopyMultiStr(IN PCWSTR MultiStr OPTIONAL)
1098 {
1099 PWSTR pStr;
1100 ULONG Length;
1101
1102 if (!MultiStr)
1103 return NULL;
1104
1105 pStr = (PWSTR)MultiStr;
1106 while (*pStr) pStr += (wcslen(pStr) + 1);
1107 Length = MultiStr - pStr + 2;
1108
1109 pStr = HeapAlloc(GetProcessHeap(), 0, Length * sizeof(WCHAR));
1110 // NOTE: If we failed allocating the string, then fall back into no filter!
1111 if (pStr)
1112 RtlCopyMemory(pStr, MultiStr, Length * sizeof(WCHAR));
1113
1114 return pStr;
1115 }
1116
1117 PEVENTLOGFILTER
1118 AllocEventLogFilter(// IN PCWSTR FilterName,
1119 IN BOOL Information,
1120 IN BOOL Warning,
1121 IN BOOL Error,
1122 IN BOOL AuditSuccess,
1123 IN BOOL AuditFailure,
1124 IN PCWSTR Sources OPTIONAL,
1125 IN PCWSTR Users OPTIONAL,
1126 IN PCWSTR ComputerNames OPTIONAL,
1127 IN ULONG NumOfEventLogs,
1128 IN PEVENTLOG* EventLogs)
1129 {
1130 PEVENTLOGFILTER EventLogFilter;
1131
1132 /* Allocate a new event log filter entry, big enough to accommodate the list of logs */
1133 EventLogFilter = HeapAlloc(GetProcessHeap(),
1134 HEAP_ZERO_MEMORY,
1135 FIELD_OFFSET(EVENTLOGFILTER, EventLogs[NumOfEventLogs]));
1136 if (!EventLogFilter)
1137 return NULL;
1138
1139 EventLogFilter->Information = Information;
1140 EventLogFilter->Warning = Warning;
1141 EventLogFilter->Error = Error;
1142 EventLogFilter->AuditSuccess = AuditSuccess;
1143 EventLogFilter->AuditFailure = AuditFailure;
1144
1145 /* Allocate and copy the sources, users, and computers multi-strings */
1146 EventLogFilter->Sources = AllocAndCopyMultiStr(Sources);
1147 EventLogFilter->Users = AllocAndCopyMultiStr(Users);
1148 EventLogFilter->ComputerNames = AllocAndCopyMultiStr(ComputerNames);
1149
1150 /* Copy the list of event logs */
1151 EventLogFilter->NumOfEventLogs = NumOfEventLogs;
1152 RtlCopyMemory(EventLogFilter->EventLogs, EventLogs, NumOfEventLogs * sizeof(PEVENTLOG));
1153
1154 /* Initialize the filter reference count */
1155 EventLogFilter->ReferenceCount = 1;
1156
1157 return EventLogFilter;
1158 }
1159
1160 VOID
1161 EventLogFilter_Free(IN PEVENTLOGFILTER EventLogFilter)
1162 {
1163 if (EventLogFilter->Sources)
1164 HeapFree(GetProcessHeap(), 0, EventLogFilter->Sources);
1165
1166 if (EventLogFilter->Users)
1167 HeapFree(GetProcessHeap(), 0, EventLogFilter->Users);
1168
1169 if (EventLogFilter->ComputerNames)
1170 HeapFree(GetProcessHeap(), 0, EventLogFilter->ComputerNames);
1171
1172 HeapFree(GetProcessHeap(), 0, EventLogFilter);
1173 }
1174
1175 LONG EventLogFilter_AddRef(IN PEVENTLOGFILTER EventLogFilter)
1176 {
1177 ASSERT(EventLogFilter);
1178 return InterlockedIncrement(&EventLogFilter->ReferenceCount);
1179 }
1180
1181 LONG EventLogFilter_Release(IN PEVENTLOGFILTER EventLogFilter)
1182 {
1183 LONG RefCount;
1184
1185 ASSERT(EventLogFilter);
1186
1187 /* When the reference count reaches zero, delete the filter */
1188 RefCount = InterlockedDecrement(&EventLogFilter->ReferenceCount);
1189 if (RefCount <= 0)
1190 {
1191 /* Remove the filter from the list */
1192 /** RemoveEntryList(&EventLogFilter->ListEntry); **/
1193 EventLogFilter_Free(EventLogFilter);
1194 }
1195
1196 return RefCount;
1197 }
1198
1199 void
1200 TrimNulls(LPWSTR s)
1201 {
1202 WCHAR *c;
1203
1204 if (s != NULL)
1205 {
1206 c = s + wcslen(s) - 1;
1207 while (c >= s && iswspace(*c))
1208 --c;
1209 *++c = L'\0';
1210 }
1211 }
1212
1213 BOOL
1214 GetEventMessageFileDLL(IN LPCWSTR lpLogName,
1215 IN LPCWSTR SourceName,
1216 IN LPCWSTR EntryName,
1217 OUT PWCHAR lpModuleName) // TODO: Add IN DWORD BufLen
1218 {
1219 BOOL Success = FALSE;
1220 LONG Result;
1221 DWORD dwType, dwSize;
1222 WCHAR szModuleName[MAX_PATH];
1223 WCHAR szKeyName[MAX_PATH];
1224 HKEY hLogKey = NULL;
1225 HKEY hSourceKey = NULL;
1226
1227 StringCbCopyW(szKeyName, sizeof(szKeyName), EVENTLOG_BASE_KEY);
1228 StringCbCatW(szKeyName, sizeof(szKeyName), lpLogName);
1229
1230 Result = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
1231 szKeyName,
1232 0,
1233 KEY_READ,
1234 &hLogKey);
1235 if (Result != ERROR_SUCCESS)
1236 return FALSE;
1237
1238 Result = RegOpenKeyExW(hLogKey,
1239 SourceName,
1240 0,
1241 KEY_QUERY_VALUE,
1242 &hSourceKey);
1243 if (Result != ERROR_SUCCESS)
1244 {
1245 RegCloseKey(hLogKey);
1246 return FALSE;
1247 }
1248
1249 dwSize = sizeof(szModuleName);
1250 Result = RegQueryValueExW(hSourceKey,
1251 EntryName,
1252 NULL,
1253 &dwType,
1254 (LPBYTE)szModuleName,
1255 &dwSize);
1256 if ((Result != ERROR_SUCCESS) || (dwType != REG_EXPAND_SZ && dwType != REG_SZ))
1257 {
1258 szModuleName[0] = UNICODE_NULL;
1259 }
1260 else
1261 {
1262 /* NULL-terminate the string and expand it */
1263 szModuleName[dwSize / sizeof(WCHAR) - 1] = UNICODE_NULL;
1264 ExpandEnvironmentStringsW(szModuleName, lpModuleName, ARRAYSIZE(szModuleName));
1265 Success = TRUE;
1266 }
1267
1268 RegCloseKey(hSourceKey);
1269 RegCloseKey(hLogKey);
1270
1271 return Success;
1272 }
1273
1274 BOOL
1275 GetEventCategory(IN LPCWSTR KeyName,
1276 IN LPCWSTR SourceName,
1277 IN PEVENTLOGRECORD pevlr,
1278 OUT PWCHAR CategoryName) // TODO: Add IN DWORD BufLen
1279 {
1280 BOOL Success = FALSE;
1281 WCHAR szMessageDLL[MAX_PATH];
1282 LPWSTR lpMsgBuf = NULL;
1283
1284 if (!GetEventMessageFileDLL(KeyName, SourceName, EVENT_CATEGORY_MESSAGE_FILE, szMessageDLL))
1285 goto Quit;
1286
1287 /* Retrieve the message string without appending extra newlines */
1288 lpMsgBuf =
1289 GetMessageStringFromDllList(szMessageDLL,
1290 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
1291 FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
1292 pevlr->EventCategory,
1293 EVENT_MESSAGE_FILE_BUFFER,
1294 NULL);
1295 if (lpMsgBuf)
1296 {
1297 /* Trim the string */
1298 TrimNulls(lpMsgBuf);
1299
1300 /* Copy the category name */
1301 StringCchCopyW(CategoryName, MAX_PATH, lpMsgBuf);
1302
1303 /* Free the buffer allocated by FormatMessage */
1304 LocalFree(lpMsgBuf);
1305
1306 /* The ID was found and the message was formatted */
1307 Success = TRUE;
1308 }
1309
1310 Quit:
1311 if (!Success)
1312 {
1313 if (pevlr->EventCategory != 0)
1314 {
1315 StringCchPrintfW(CategoryName, MAX_PATH, L"(%lu)", pevlr->EventCategory);
1316 Success = TRUE;
1317 }
1318 }
1319
1320 return Success;
1321 }
1322
1323
1324 BOOL // NOTE: Used by evtdetctl.c
1325 GetEventMessage(IN LPCWSTR KeyName,
1326 IN LPCWSTR SourceName,
1327 IN PEVENTLOGRECORD pevlr,
1328 OUT PWCHAR EventText) // TODO: Add IN DWORD BufLen
1329 {
1330 BOOL Success = FALSE;
1331 DWORD i;
1332 size_t cch;
1333 WCHAR SourceModuleName[1024];
1334 WCHAR ParameterModuleName[1024];
1335 BOOL IsParamModNameCached = FALSE;
1336 LPWSTR lpMsgBuf = NULL;
1337 LPWSTR szStringArray, szMessage;
1338 LPWSTR *szArguments;
1339
1340 /* Get the event string array */
1341 szStringArray = (LPWSTR)((LPBYTE)pevlr + pevlr->StringOffset);
1342
1343 /* NOTE: GetEventMessageFileDLL can return a comma-separated list of DLLs */
1344 if (!GetEventMessageFileDLL(KeyName, SourceName, EVENT_MESSAGE_FILE, SourceModuleName))
1345 goto Quit;
1346
1347 /* Allocate space for insertion strings */
1348 szArguments = HeapAlloc(GetProcessHeap(), 0, pevlr->NumStrings * sizeof(LPVOID));
1349 if (!szArguments)
1350 goto Quit;
1351
1352 if (!IsParamModNameCached)
1353 {
1354 /* Now that the parameter file list is loaded, no need to reload it at the next run! */
1355 IsParamModNameCached = GetEventMessageFileDLL(KeyName, SourceName, EVENT_PARAMETER_MESSAGE_FILE, ParameterModuleName);
1356 // FIXME: If the string loading failed the first time, no need to retry it just after???
1357 }
1358
1359 if (IsParamModNameCached)
1360 {
1361 /* Not yet support for reading messages from parameter message DLL */
1362 }
1363
1364 szMessage = szStringArray;
1365 /*
1366 * HACK:
1367 * We do some hackish preformatting of the cached event strings...
1368 * That's because after we pass the string to FormatMessage
1369 * (via GetMessageStringFromDllList) with the FORMAT_MESSAGE_ARGUMENT_ARRAY
1370 * flag, instead of ignoring the insertion parameters and do the formatting
1371 * by ourselves. Therefore, the resulting string should have the parameter
1372 * string placeholders starting with a single '%' instead of a mix of one
1373 * and two '%'.
1374 */
1375 /* HACK part 1: Compute the full length of the string array */
1376 cch = 0;
1377 for (i = 0; i < pevlr->NumStrings; i++)
1378 {
1379 szMessage += wcslen(szMessage) + 1;
1380 }
1381 cch = szMessage - szStringArray;
1382
1383 /* HACK part 2: Now do the HACK proper! */
1384 szMessage = szStringArray;
1385 for (i = 0; i < pevlr->NumStrings; i++)
1386 {
1387 lpMsgBuf = szMessage;
1388 while ((lpMsgBuf = wcsstr(lpMsgBuf, L"%%")))
1389 {
1390 if (iswdigit(lpMsgBuf[2]))
1391 {
1392 RtlMoveMemory(lpMsgBuf, lpMsgBuf+1, ((szStringArray + cch) - lpMsgBuf - 1) * sizeof(WCHAR));
1393 }
1394 }
1395
1396 szArguments[i] = szMessage;
1397 szMessage += wcslen(szMessage) + 1;
1398 }
1399
1400 /* Retrieve the message string without appending extra newlines */
1401 lpMsgBuf =
1402 GetMessageStringFromDllList(SourceModuleName,
1403 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
1404 FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_MAX_WIDTH_MASK,
1405 pevlr->EventID,
1406 0,
1407 (va_list*)szArguments);
1408 if (lpMsgBuf)
1409 {
1410 /* Trim the string */
1411 TrimNulls(lpMsgBuf);
1412
1413 szMessage = NULL;
1414 Success = (ApplyParameterStringsToMessage(ParameterModuleName,
1415 TRUE,
1416 lpMsgBuf,
1417 &szMessage) == ERROR_SUCCESS);
1418 if (Success && szMessage)
1419 {
1420 /* Free the buffer allocated by FormatMessage */
1421 LocalFree(lpMsgBuf);
1422 lpMsgBuf = szMessage;
1423 }
1424
1425 /* Copy the event text */
1426 StringCchCopyW(EventText, EVENT_MESSAGE_EVENTTEXT_BUFFER, lpMsgBuf);
1427
1428 /* Free the buffer allocated by FormatMessage */
1429 LocalFree(lpMsgBuf);
1430 }
1431
1432 HeapFree(GetProcessHeap(), 0, szArguments);
1433
1434 Quit:
1435 if (!Success)
1436 {
1437 /* Get a read-only pointer to the "event-not-found" string */
1438 lpMsgBuf = HeapAlloc(GetProcessHeap(), 0, EVENT_MESSAGE_EVENTTEXT_BUFFER * sizeof(WCHAR));
1439 LoadStringW(hInst, IDS_EVENTSTRINGIDNOTFOUND, lpMsgBuf, EVENT_MESSAGE_EVENTTEXT_BUFFER);
1440 StringCchPrintfW(EventText, EVENT_MESSAGE_EVENTTEXT_BUFFER, lpMsgBuf, (pevlr->EventID & 0xFFFF), SourceName);
1441
1442 /* Append the strings */
1443 szMessage = szStringArray;
1444 for (i = 0; i < pevlr->NumStrings; i++)
1445 {
1446 StringCchCatW(EventText, EVENT_MESSAGE_EVENTTEXT_BUFFER, szMessage);
1447 StringCchCatW(EventText, EVENT_MESSAGE_EVENTTEXT_BUFFER, L"\n");
1448 szMessage += wcslen(szMessage) + 1;
1449 }
1450 }
1451
1452 return Success;
1453 }
1454
1455 VOID
1456 GetEventType(IN WORD dwEventType,
1457 OUT PWCHAR eventTypeText) // TODO: Add IN DWORD BufLen
1458 {
1459 switch (dwEventType)
1460 {
1461 case EVENTLOG_ERROR_TYPE:
1462 LoadStringW(hInst, IDS_EVENTLOG_ERROR_TYPE, eventTypeText, MAX_LOADSTRING);
1463 break;
1464 case EVENTLOG_WARNING_TYPE:
1465 LoadStringW(hInst, IDS_EVENTLOG_WARNING_TYPE, eventTypeText, MAX_LOADSTRING);
1466 break;
1467 case EVENTLOG_INFORMATION_TYPE:
1468 LoadStringW(hInst, IDS_EVENTLOG_INFORMATION_TYPE, eventTypeText, MAX_LOADSTRING);
1469 break;
1470 case EVENTLOG_SUCCESS:
1471 LoadStringW(hInst, IDS_EVENTLOG_SUCCESS, eventTypeText, MAX_LOADSTRING);
1472 break;
1473 case EVENTLOG_AUDIT_SUCCESS:
1474 LoadStringW(hInst, IDS_EVENTLOG_AUDIT_SUCCESS, eventTypeText, MAX_LOADSTRING);
1475 break;
1476 case EVENTLOG_AUDIT_FAILURE:
1477 LoadStringW(hInst, IDS_EVENTLOG_AUDIT_FAILURE, eventTypeText, MAX_LOADSTRING);
1478 break;
1479 default:
1480 LoadStringW(hInst, IDS_EVENTLOG_UNKNOWN_TYPE, eventTypeText, MAX_LOADSTRING);
1481 break;
1482 }
1483 }
1484
1485 BOOL
1486 GetEventUserName(IN PEVENTLOGRECORD pelr,
1487 IN OUT PSID *pLastSid,
1488 OUT PWCHAR pszUser) // TODO: Add IN DWORD BufLen
1489 {
1490 PSID pCurrentSid;
1491 PWSTR StringSid;
1492 WCHAR szName[1024];
1493 WCHAR szDomain[1024];
1494 SID_NAME_USE peUse;
1495 DWORD cchName = ARRAYSIZE(szName);
1496 DWORD cchDomain = ARRAYSIZE(szDomain);
1497 BOOL Success = FALSE;
1498
1499 /* Point to the SID */
1500 pCurrentSid = (PSID)((LPBYTE)pelr + pelr->UserSidOffset);
1501
1502 if (!IsValidSid(pCurrentSid))
1503 {
1504 *pLastSid = NULL;
1505 return FALSE;
1506 }
1507 else if (*pLastSid && EqualSid(*pLastSid, pCurrentSid))
1508 {
1509 return TRUE;
1510 }
1511
1512 /* User SID */
1513 if (pelr->UserSidLength > 0)
1514 {
1515 /*
1516 * Try to retrieve the user account name and domain name corresponding
1517 * to the SID. If it cannot be retrieved, try to convert the SID to a
1518 * string-form. It should not be bigger than the user-provided buffer
1519 * 'pszUser', otherwise we return an error.
1520 */
1521 if (LookupAccountSidW(NULL, // FIXME: Use computer name? From the particular event?
1522 pCurrentSid,
1523 szName,
1524 &cchName,
1525 szDomain,
1526 &cchDomain,
1527 &peUse))
1528 {
1529 StringCchCopyW(pszUser, MAX_PATH, szName);
1530 Success = TRUE;
1531 }
1532 else if (ConvertSidToStringSidW(pCurrentSid, &StringSid))
1533 {
1534 /* Copy the string only if the user-provided buffer is big enough */
1535 if (wcslen(StringSid) + 1 <= MAX_PATH) // + 1 for NULL-terminator
1536 {
1537 StringCchCopyW(pszUser, MAX_PATH, StringSid);
1538 Success = TRUE;
1539 }
1540 else
1541 {
1542 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1543 Success = FALSE;
1544 }
1545
1546 /* Free the allocated buffer */
1547 LocalFree(StringSid);
1548 }
1549 }
1550
1551 *pLastSid = Success ? pCurrentSid : NULL;
1552
1553 return Success;
1554 }
1555
1556
1557 static VOID FreeRecords(VOID)
1558 {
1559 DWORD iIndex;
1560
1561 if (!g_RecordPtrs)
1562 return;
1563
1564 for (iIndex = 0; iIndex < g_TotalRecords; iIndex++)
1565 {
1566 if (g_RecordPtrs[iIndex])
1567 HeapFree(GetProcessHeap(), 0, g_RecordPtrs[iIndex]);
1568 }
1569 HeapFree(GetProcessHeap(), 0, g_RecordPtrs);
1570 g_RecordPtrs = NULL;
1571 g_TotalRecords = 0;
1572 }
1573
1574 BOOL
1575 FilterByType(IN PEVENTLOGFILTER EventLogFilter,
1576 IN PEVENTLOGRECORD pevlr)
1577 {
1578 if ((pevlr->EventType == EVENTLOG_SUCCESS && !EventLogFilter->Information ) ||
1579 (pevlr->EventType == EVENTLOG_INFORMATION_TYPE && !EventLogFilter->Information ) ||
1580 (pevlr->EventType == EVENTLOG_WARNING_TYPE && !EventLogFilter->Warning ) ||
1581 (pevlr->EventType == EVENTLOG_ERROR_TYPE && !EventLogFilter->Error ) ||
1582 (pevlr->EventType == EVENTLOG_AUDIT_SUCCESS && !EventLogFilter->AuditSuccess) ||
1583 (pevlr->EventType == EVENTLOG_AUDIT_FAILURE && !EventLogFilter->AuditFailure))
1584 {
1585 return FALSE;
1586 }
1587 return TRUE;
1588 }
1589
1590 BOOL
1591 FilterByString(IN PCWSTR FilterString, // This is a multi-string
1592 IN PWSTR String)
1593 {
1594 PCWSTR pStr;
1595
1596 /* The filter string is NULL so it does not filter anything */
1597 if (!FilterString)
1598 return TRUE;
1599
1600 /*
1601 * If the filter string filters for an empty string AND the source string
1602 * is an empty string, we have a match (particular case of the last one).
1603 */
1604 if (!*FilterString && !*String)
1605 return TRUE;
1606
1607 // if (*FilterString || *String)
1608
1609 /*
1610 * If the filter string is empty BUT the source string is not empty,
1611 * OR vice-versa, we cannot have a match.
1612 */
1613 if ( (!*FilterString && *String) || (*FilterString && !*String) )
1614 return FALSE;
1615
1616 /*
1617 * If the filter string filters for at least a non-empty string,
1618 * browse it and search for a string that matches the source string.
1619 */
1620 // else if (*FilterString && *String)
1621 {
1622 pStr = FilterString;
1623 while (*pStr)
1624 {
1625 if (wcsicmp(pStr, String) == 0)
1626 {
1627 /* We have a match, break the loop */
1628 break;
1629 }
1630
1631 pStr += (wcslen(pStr) + 1);
1632 }
1633 if (!*pStr) // && *String
1634 {
1635 /* We do not have a match */
1636 return FALSE;
1637 }
1638 }
1639
1640 /* We have a match */
1641 return TRUE;
1642 }
1643
1644 /*
1645 * The events enumerator thread.
1646 */
1647 static DWORD WINAPI
1648 EnumEventsThread(IN LPVOID lpParameter)
1649 {
1650 PEVENTLOGFILTER EventLogFilter = (PEVENTLOGFILTER)lpParameter;
1651 PEVENTLOG EventLog;
1652
1653 ULONG LogIndex;
1654 HANDLE hEventLog;
1655 PEVENTLOGRECORD pEvlr = NULL;
1656 PBYTE pEvlrEnd;
1657 PBYTE pEvlrBuffer;
1658 DWORD dwWanted, dwRead, dwNeeded, dwStatus = ERROR_SUCCESS;
1659 DWORD dwTotalRecords = 0, dwCurrentRecord = 0;
1660 DWORD dwFlags, dwMaxLength;
1661 size_t cchRemaining;
1662 LPWSTR lpszSourceName;
1663 LPWSTR lpszComputerName;
1664 BOOL bResult = TRUE; /* Read succeeded */
1665 HANDLE hProcessHeap = GetProcessHeap();
1666 PSID pLastSid = NULL;
1667
1668 UINT uStep = 0, uStepAt = 0, uPos = 0;
1669
1670 WCHAR szWindowTitle[MAX_PATH];
1671 WCHAR szStatusText[MAX_PATH];
1672 WCHAR szLocalDate[MAX_PATH];
1673 WCHAR szLocalTime[MAX_PATH];
1674 WCHAR szEventID[MAX_PATH];
1675 WCHAR szEventTypeText[MAX_LOADSTRING];
1676 WCHAR szCategoryID[MAX_PATH];
1677 WCHAR szUsername[MAX_PATH];
1678 WCHAR szNoUsername[MAX_PATH];
1679 WCHAR szCategory[MAX_PATH];
1680 WCHAR szNoCategory[MAX_PATH];
1681 PWCHAR lpTitleTemplateEnd;
1682
1683 SYSTEMTIME time;
1684 LVITEMW lviEventItem;
1685
1686 /* Save the current event log filter globally */
1687 EventLogFilter_AddRef(EventLogFilter);
1688 ActiveFilter = EventLogFilter;
1689
1690
1691 /** HACK!! **/
1692 EventLog = EventLogFilter->EventLogs[0];
1693
1694 // FIXME: Use something else instead of EventLog->LogName !!
1695
1696 /*
1697 * Use a different formatting, whether the event log filter holds
1698 * only one log, or many logs (the latter case is WIP TODO!)
1699 */
1700 if (EventLogFilter->NumOfEventLogs <= 1)
1701 {
1702 StringCchPrintfExW(szWindowTitle,
1703 ARRAYSIZE(szWindowTitle),
1704 &lpTitleTemplateEnd,
1705 &cchRemaining,
1706 0,
1707 szTitleTemplate, szTitle, EventLog->LogName); /* i = number of characters written */
1708 dwMaxLength = (DWORD)cchRemaining;
1709 if (!EventLog->ComputerName)
1710 GetComputerNameW(lpTitleTemplateEnd, &dwMaxLength);
1711 else
1712 StringCchCopyW(lpTitleTemplateEnd, dwMaxLength, EventLog->ComputerName);
1713
1714 StringCbPrintfW(szStatusText,
1715 sizeof(szStatusText),
1716 szStatusBarTemplate,
1717 EventLog->LogName,
1718 0,
1719 0);
1720 }
1721 else
1722 {
1723 // TODO: Use a different title & implement filtering for multi-log filters !!
1724 // (EventLogFilter->NumOfEventLogs > 1)
1725 MessageBoxW(hwndMainWindow,
1726 L"Many-logs filtering is not implemented yet!!",
1727 L"Event Log",
1728 MB_OK | MB_ICONINFORMATION);
1729 }
1730
1731 /* Set the window title */
1732 SetWindowTextW(hwndMainWindow, szWindowTitle);
1733
1734 /* Update the status bar */
1735 StatusBar_SetText(hwndStatus, 0, szStatusText);
1736
1737
1738 /* Disable list view redraw */
1739 SendMessageW(hwndListView, WM_SETREDRAW, FALSE, 0);
1740
1741 /* Clear the list view and free the cached records */
1742 ListView_DeleteAllItems(hwndListView);
1743 FreeRecords();
1744
1745 SendMessageW(hwndListView, LVM_PROGRESS, 0, TRUE);
1746 ProgressBar_SetRange(hwndStatusProgress, 0);
1747 StatusBar_SetText(hwndStatus, 0, NULL);
1748 ShowWindow(hwndStatusProgress, SW_SHOW);
1749
1750 /* Do a loop over the logs enumerated in the filter */
1751 // FIXME: For now we only support 1 event log per filter!
1752 LogIndex = 0;
1753 // for (LogIndex = 0; LogIndex < EventLogFilter->NumOfEventLogs; ++LogIndex)
1754 {
1755
1756 EventLog = EventLogFilter->EventLogs[LogIndex];
1757
1758 /* Open the event log */
1759 if (EventLog->Permanent)
1760 hEventLog = OpenEventLogW(EventLog->ComputerName, EventLog->LogName);
1761 else
1762 hEventLog = OpenBackupEventLogW(EventLog->ComputerName, EventLog->LogName); // FileName
1763
1764 if (hEventLog == NULL)
1765 {
1766 ShowWin32Error(GetLastError());
1767 goto Cleanup;
1768 }
1769
1770 // GetOldestEventLogRecord(hEventLog, &dwThisRecord);
1771
1772 /* Get the total number of event log records */
1773 GetNumberOfEventLogRecords(hEventLog, &dwTotalRecords);
1774
1775 if (dwTotalRecords > 0)
1776 {
1777 EnableMenuItem(hMainMenu, IDM_CLEAR_EVENTS, MF_BYCOMMAND | MF_ENABLED);
1778 EnableMenuItem(hMainMenu, IDM_SAVE_EVENTLOG, MF_BYCOMMAND | MF_ENABLED);
1779 }
1780 else
1781 {
1782 EnableMenuItem(hMainMenu, IDM_CLEAR_EVENTS, MF_BYCOMMAND | MF_GRAYED);
1783 EnableMenuItem(hMainMenu, IDM_SAVE_EVENTLOG, MF_BYCOMMAND | MF_GRAYED);
1784 }
1785
1786 /* Set up the event records cache */
1787 g_RecordPtrs = HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, dwTotalRecords * sizeof(*g_RecordPtrs));
1788 if (!g_RecordPtrs)
1789 {
1790 // ShowWin32Error(GetLastError());
1791 goto Quit;
1792 }
1793 g_TotalRecords = dwTotalRecords;
1794
1795 if (WaitForSingleObject(hStopEnumEvent, 0) == WAIT_OBJECT_0)
1796 goto Quit;
1797
1798 LoadStringW(hInst, IDS_NOT_AVAILABLE, szNoUsername, ARRAYSIZE(szNoUsername));
1799 LoadStringW(hInst, IDS_NONE, szNoCategory, ARRAYSIZE(szNoCategory));
1800
1801 ProgressBar_SetRange(hwndStatusProgress, MAKELPARAM(0, 100));
1802 uStepAt = (dwTotalRecords / 100) + 1;
1803
1804 dwFlags = EVENTLOG_SEQUENTIAL_READ | (NewestEventsFirst ? EVENTLOG_FORWARDS_READ : EVENTLOG_BACKWARDS_READ);
1805
1806 /* 0x7ffff is the maximum buffer size ReadEventLog will accept */
1807 dwWanted = 0x7ffff;
1808 pEvlr = HeapAlloc(hProcessHeap, 0, dwWanted);
1809
1810 if (!pEvlr)
1811 goto Quit;
1812
1813 while (dwStatus == ERROR_SUCCESS)
1814 {
1815 bResult = ReadEventLogW(hEventLog, dwFlags, 0, pEvlr, dwWanted, &dwRead, &dwNeeded);
1816 dwStatus = GetLastError();
1817
1818 if (!bResult && dwStatus == ERROR_INSUFFICIENT_BUFFER)
1819 {
1820 pEvlr = HeapReAlloc(hProcessHeap, 0, pEvlr, dwNeeded);
1821 dwWanted = dwNeeded;
1822
1823 if (!pEvlr)
1824 break;
1825
1826 bResult = ReadEventLogW(hEventLog, dwFlags, 0, pEvlr, dwNeeded, &dwRead, &dwNeeded);
1827
1828 if (!bResult)
1829 break;
1830 }
1831 else if (!bResult)
1832 {
1833 /* Exit on other errors (ERROR_HANDLE_EOF) */
1834 break;
1835 }
1836
1837 pEvlrBuffer = (LPBYTE)pEvlr;
1838 pEvlrEnd = pEvlrBuffer + dwRead;
1839
1840 while (pEvlrBuffer < pEvlrEnd)
1841 {
1842 PEVENTLOGRECORD pEvlrTmp = (PEVENTLOGRECORD)pEvlrBuffer;
1843 PWSTR lpszUsername, lpszCategoryName;
1844 g_RecordPtrs[dwCurrentRecord] = NULL;
1845
1846 // ProgressBar_StepIt(hwndStatusProgress);
1847 uStep++;
1848 if (uStep % uStepAt == 0)
1849 {
1850 ++uPos;
1851 ProgressBar_SetPos(hwndStatusProgress, uPos);
1852 }
1853
1854 if (WaitForSingleObject(hStopEnumEvent, 0) == WAIT_OBJECT_0)
1855 goto Quit;
1856
1857 /* Filter by event type */
1858 if (!FilterByType(EventLogFilter, pEvlrTmp))
1859 goto SkipEvent;
1860
1861 /* Get the event source name and filter it */
1862 lpszSourceName = (LPWSTR)(pEvlrBuffer + sizeof(EVENTLOGRECORD));
1863 if (!FilterByString(EventLogFilter->Sources, lpszSourceName))
1864 goto SkipEvent;
1865
1866 /* Get the computer name and filter it */
1867 lpszComputerName = (LPWSTR)(pEvlrBuffer + sizeof(EVENTLOGRECORD) + (wcslen(lpszSourceName) + 1) * sizeof(WCHAR));
1868 if (!FilterByString(EventLogFilter->ComputerNames, lpszComputerName))
1869 goto SkipEvent;
1870
1871 /* Compute the event time */
1872 EventTimeToSystemTime(pEvlrTmp->TimeWritten, &time);
1873 GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time, NULL, szLocalDate, ARRAYSIZE(szLocalDate));
1874 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &time, NULL, szLocalTime, ARRAYSIZE(szLocalTime));
1875
1876 /* Get the username that generated the event, and filter it */
1877 lpszUsername = GetEventUserName(pEvlrTmp, &pLastSid, szUsername) ? szUsername : szNoUsername;
1878
1879 if (!FilterByString(EventLogFilter->Users, lpszUsername))
1880 goto SkipEvent;
1881
1882 // TODO: Filter by event ID and category
1883 GetEventType(pEvlrTmp->EventType, szEventTypeText);
1884
1885 lpszCategoryName = GetEventCategory(EventLog->LogName, lpszSourceName, pEvlrTmp, szCategory) ? szCategory : szNoCategory;
1886
1887 StringCbPrintfW(szEventID, sizeof(szEventID), L"%u", (pEvlrTmp->EventID & 0xFFFF));
1888 StringCbPrintfW(szCategoryID, sizeof(szCategoryID), L"%u", pEvlrTmp->EventCategory);
1889
1890 g_RecordPtrs[dwCurrentRecord] = HeapAlloc(hProcessHeap, 0, pEvlrTmp->Length);
1891 RtlCopyMemory(g_RecordPtrs[dwCurrentRecord], pEvlrTmp, pEvlrTmp->Length);
1892
1893 lviEventItem.mask = LVIF_IMAGE | LVIF_TEXT | LVIF_PARAM;
1894 lviEventItem.iItem = 0;
1895 lviEventItem.iSubItem = 0;
1896 lviEventItem.lParam = (LPARAM)g_RecordPtrs[dwCurrentRecord];
1897 lviEventItem.pszText = szEventTypeText;
1898
1899 switch (pEvlrTmp->EventType)
1900 {
1901 case EVENTLOG_SUCCESS:
1902 case EVENTLOG_INFORMATION_TYPE:
1903 lviEventItem.iImage = 0;
1904 break;
1905
1906 case EVENTLOG_WARNING_TYPE:
1907 lviEventItem.iImage = 1;
1908 break;
1909
1910 case EVENTLOG_ERROR_TYPE:
1911 lviEventItem.iImage = 2;
1912 break;
1913
1914 case EVENTLOG_AUDIT_SUCCESS:
1915 lviEventItem.iImage = 3;
1916 break;
1917
1918 case EVENTLOG_AUDIT_FAILURE:
1919 lviEventItem.iImage = 4;
1920 break;
1921 }
1922
1923 lviEventItem.iItem = ListView_InsertItem(hwndListView, &lviEventItem);
1924
1925 ListView_SetItemText(hwndListView, lviEventItem.iItem, 1, szLocalDate);
1926 ListView_SetItemText(hwndListView, lviEventItem.iItem, 2, szLocalTime);
1927 ListView_SetItemText(hwndListView, lviEventItem.iItem, 3, lpszSourceName);
1928 ListView_SetItemText(hwndListView, lviEventItem.iItem, 4, lpszCategoryName);
1929 ListView_SetItemText(hwndListView, lviEventItem.iItem, 5, szEventID);
1930 ListView_SetItemText(hwndListView, lviEventItem.iItem, 6, lpszUsername);
1931 ListView_SetItemText(hwndListView, lviEventItem.iItem, 7, lpszComputerName);
1932
1933 SkipEvent:
1934 pEvlrBuffer += pEvlrTmp->Length;
1935 dwCurrentRecord++;
1936 }
1937 }
1938
1939 Quit:
1940
1941 if (pEvlr)
1942 HeapFree(hProcessHeap, 0, pEvlr);
1943
1944 /* Close the event log */
1945 CloseEventLog(hEventLog);
1946
1947 } // end-for (LogIndex)
1948
1949 /* All events loaded */
1950
1951 Cleanup:
1952
1953 ShowWindow(hwndStatusProgress, SW_HIDE);
1954 SendMessageW(hwndListView, LVM_PROGRESS, 0, FALSE);
1955
1956 // FIXME: Use something else instead of EventLog->LogName !!
1957
1958 /*
1959 * Use a different formatting, whether the event log filter holds
1960 * only one log, or many logs (the latter case is WIP TODO!)
1961 */
1962 if (EventLogFilter->NumOfEventLogs <= 1)
1963 {
1964 StringCbPrintfW(szStatusText,
1965 sizeof(szStatusText),
1966 szStatusBarTemplate,
1967 EventLog->LogName,
1968 dwTotalRecords,
1969 ListView_GetItemCount(hwndListView));
1970 }
1971 else
1972 {
1973 // TODO: Use a different title & implement filtering for multi-log filters !!
1974 // (EventLogFilter->NumOfEventLogs > 1)
1975 }
1976
1977 /* Update the status bar */
1978 StatusBar_SetText(hwndStatus, 0, szStatusText);
1979
1980 /* Resume list view redraw */
1981 SendMessageW(hwndListView, WM_SETREDRAW, TRUE, 0);
1982
1983 EventLogFilter_Release(EventLogFilter);
1984
1985 CloseHandle(hStopEnumEvent);
1986 InterlockedExchangePointer((PVOID*)&hStopEnumEvent, NULL);
1987
1988 return 0;
1989 }
1990
1991 /*
1992 * The purpose of this thread is to serialize the creation of the events
1993 * enumeration thread, since the Event Log Viewer currently only supports
1994 * one view, one event list, one enumeration.
1995 */
1996 static DWORD WINAPI
1997 StartStopEnumEventsThread(IN LPVOID lpParameter)
1998 {
1999 HANDLE WaitHandles[2];
2000 DWORD WaitResult;
2001
2002 WaitHandles[0] = hStartStopEnumEvent; // End-of-application event
2003 WaitHandles[1] = hStartEnumEvent; // Command event
2004
2005 while (TRUE)
2006 {
2007 WaitResult = WaitForMultipleObjects(ARRAYSIZE(WaitHandles),
2008 WaitHandles,
2009 FALSE, // WaitAny
2010 INFINITE);
2011 switch (WaitResult)
2012 {
2013 case WAIT_OBJECT_0 + 0:
2014 {
2015 /* End-of-application event signaled, quit this thread */
2016
2017 /* Stop the previous enumeration */
2018 if (hEnumEventsThread)
2019 {
2020 if (hStopEnumEvent)
2021 {
2022 SetEvent(hStopEnumEvent);
2023 WaitForSingleObject(hEnumEventsThread, INFINITE);
2024 // NOTE: The following is done by the enumeration thread just before terminating.
2025 // hStopEnumEvent = NULL;
2026 }
2027
2028 CloseHandle(hEnumEventsThread);
2029 hEnumEventsThread = NULL;
2030 }
2031
2032 /* Clear the list view and free the cached records */
2033 ListView_DeleteAllItems(hwndListView);
2034 FreeRecords();
2035
2036 /* Reset the active filter */
2037 ActiveFilter = NULL;
2038
2039 return 0;
2040 }
2041
2042 case WAIT_OBJECT_0 + 1:
2043 {
2044 /* Restart a new enumeration if needed */
2045 PEVENTLOGFILTER EventLogFilter;
2046
2047 /* Stop the previous enumeration */
2048 if (hEnumEventsThread)
2049 {
2050 if (hStopEnumEvent)
2051 {
2052 SetEvent(hStopEnumEvent);
2053 WaitForSingleObject(hEnumEventsThread, INFINITE);
2054 // NOTE: The following is done by the enumeration thread just before terminating.
2055 // hStopEnumEvent = NULL;
2056 }
2057
2058 CloseHandle(hEnumEventsThread);
2059 hEnumEventsThread = NULL;
2060 }
2061
2062 /* Clear the list view and free the cached records */
2063 ListView_DeleteAllItems(hwndListView);
2064 FreeRecords();
2065
2066 /* Reset the active filter */
2067 ActiveFilter = NULL;
2068
2069 EventLogFilter = InterlockedExchangePointer((PVOID*)&EnumFilter, NULL);
2070 if (!EventLogFilter)
2071 break;
2072
2073 // Manual-reset event
2074 hStopEnumEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
2075 if (!hStopEnumEvent)
2076 break;
2077
2078 hEnumEventsThread = CreateThread(NULL,
2079 0,
2080 EnumEventsThread,
2081 (LPVOID)EventLogFilter,
2082 CREATE_SUSPENDED,
2083 NULL);
2084 if (!hEnumEventsThread)
2085 {
2086 CloseHandle(hStopEnumEvent);
2087 hStopEnumEvent = NULL;
2088 break;
2089 }
2090 // CloseHandle(hEnumEventsThread);
2091 ResumeThread(hEnumEventsThread);
2092
2093 break;
2094 }
2095
2096 default:
2097 {
2098 /* Unknown command, must never go there! */
2099 return GetLastError();
2100 }
2101 }
2102 }
2103
2104 return 0;
2105 }
2106
2107 VOID
2108 EnumEvents(IN PEVENTLOGFILTER EventLogFilter)
2109 {
2110 /* Signal the enumerator thread we want to enumerate events */
2111 InterlockedExchangePointer((PVOID*)&EnumFilter, EventLogFilter);
2112 SetEvent(hStartEnumEvent);
2113 return;
2114 }
2115
2116
2117 PEVENTLOGFILTER
2118 GetSelectedFilter(OUT HTREEITEM* phti OPTIONAL)
2119 {
2120 TVITEMEXW tvItemEx;
2121 HTREEITEM hti;
2122
2123 if (phti)
2124 *phti = NULL;
2125
2126 /* Get index of selected item */
2127 hti = TreeView_GetSelection(hwndTreeView);
2128 if (hti == NULL)
2129 return NULL; // No filter
2130
2131 tvItemEx.mask = TVIF_PARAM;
2132 tvItemEx.hItem = hti;
2133
2134 TreeView_GetItem(hwndTreeView, &tvItemEx);
2135
2136 if (phti)
2137 *phti = tvItemEx.hItem;
2138
2139 return (PEVENTLOGFILTER)tvItemEx.lParam;
2140 }
2141
2142
2143 VOID
2144 OpenUserEventLogFile(IN LPCWSTR lpszFileName)
2145 {
2146 WIN32_FIND_DATAW FindData;
2147 HANDLE hFind;
2148 PEVENTLOG EventLog;
2149 PEVENTLOGFILTER EventLogFilter;
2150 SIZE_T cchFileName;
2151 HTREEITEM hItem = NULL;
2152
2153 /* Check whether the file actually exists */
2154 hFind = FindFirstFileW(lpszFileName, &FindData);
2155 if (hFind == INVALID_HANDLE_VALUE)
2156 {
2157 ShowWin32Error(GetLastError());
2158 return;
2159 }
2160 FindClose(hFind);
2161
2162 /* Allocate a new event log entry */
2163 EventLog = AllocEventLog(NULL, lpszFileName, FALSE);
2164 if (EventLog == NULL)
2165 {
2166 ShowWin32Error(ERROR_NOT_ENOUGH_MEMORY);
2167 return;
2168 }
2169
2170 /* Allocate a new event log filter entry for this event log */
2171 EventLogFilter = AllocEventLogFilter(// LogName,
2172 TRUE, TRUE, TRUE, TRUE, TRUE,
2173 NULL, NULL, NULL,
2174 1, &EventLog);
2175 if (EventLogFilter == NULL)
2176 {
2177 ShowWin32Error(ERROR_NOT_ENOUGH_MEMORY);
2178 EventLog_Free(EventLog);
2179 return;
2180 }
2181
2182 /* Add the event log and the filter into their lists */
2183 InsertTailList(&EventLogList, &EventLog->ListEntry);
2184 InsertTailList(&EventLogFilterList, &EventLogFilter->ListEntry);
2185
2186 /* Retrieve and cache the event log file */
2187 cchFileName = wcslen(lpszFileName) + 1;
2188 EventLog->FileName = HeapAlloc(GetProcessHeap(), 0, cchFileName * sizeof(WCHAR));
2189 if (EventLog->FileName)
2190 StringCchCopyW(EventLog->FileName, cchFileName, lpszFileName);
2191
2192 hItem = TreeViewAddItem(hwndTreeView, htiUserLogs,
2193 (LPWSTR)lpszFileName,
2194 2, 3, (LPARAM)EventLogFilter);
2195
2196 /* Select the event log */
2197 if (hItem)
2198 {
2199 // TreeView_Expand(hwndTreeView, htiUserLogs, TVE_EXPAND);
2200 TreeView_SelectItem(hwndTreeView, hItem);
2201 TreeView_EnsureVisible(hwndTreeView, hItem);
2202 }
2203 InvalidateRect(hwndTreeView, NULL, FALSE);
2204 SetFocus(hwndTreeView);
2205 }
2206
2207 VOID
2208 OpenUserEventLog(VOID)
2209 {
2210 WCHAR szFileName[MAX_PATH];
2211
2212 ZeroMemory(szFileName, sizeof(szFileName));
2213
2214 sfn.lpstrFile = szFileName;
2215 sfn.nMaxFile = ARRAYSIZE(szFileName);
2216
2217 if (!GetOpenFileNameW(&sfn))
2218 return;
2219 sfn.lpstrFile[sfn.nMaxFile-1] = UNICODE_NULL;
2220
2221 OpenUserEventLogFile(sfn.lpstrFile);
2222 }
2223
2224 VOID
2225 SaveEventLog(IN PEVENTLOGFILTER EventLogFilter)
2226 {
2227 PEVENTLOG EventLog;
2228 HANDLE hEventLog;
2229 WCHAR szFileName[MAX_PATH];
2230
2231 /* Bail out if there is no available filter */
2232 if (!EventLogFilter)
2233 return;
2234
2235 ZeroMemory(szFileName, sizeof(szFileName));
2236
2237 sfn.lpstrFile = szFileName;
2238 sfn.nMaxFile = ARRAYSIZE(szFileName);
2239
2240 if (!GetSaveFileNameW(&sfn))
2241 return;
2242
2243 EventLogFilter_AddRef(EventLogFilter);
2244
2245 EventLog = EventLogFilter->EventLogs[0];
2246 hEventLog = OpenEventLogW(EventLog->ComputerName, EventLog->LogName);
2247
2248 EventLogFilter_Release(EventLogFilter);
2249
2250 if (!hEventLog)
2251 {
2252 ShowWin32Error(GetLastError());
2253 return;
2254 }
2255
2256 if (!BackupEventLogW(hEventLog, szFileName))
2257 ShowWin32Error(GetLastError());
2258
2259 CloseEventLog(hEventLog);
2260 }
2261
2262 VOID
2263 CloseUserEventLog(IN PEVENTLOGFILTER EventLogFilter, IN HTREEITEM hti)
2264 {
2265 /* Bail out if there is no available filter */
2266 if (!EventLogFilter)
2267 return;
2268
2269 if (InterlockedCompareExchangePointer((PVOID*)&ActiveFilter, NULL, NULL) == EventLogFilter)
2270 {
2271 /* Signal the enumerator thread we want to stop enumerating events */
2272 // EnumEvents(NULL);
2273 InterlockedExchangePointer((PVOID*)&EnumFilter, NULL);
2274 SetEvent(hStartEnumEvent);
2275 }
2276
2277 /*
2278 * The deletion of the item automatically triggers a TVN_SELCHANGED
2279 * notification, that will reset the ActiveFilter (in case the item
2280 * selected is a filter). Otherwise we reset it there.
2281 */
2282 TreeView_DeleteItem(hwndTreeView, hti);
2283
2284 /* Remove the filter from the list */
2285 RemoveEntryList(&EventLogFilter->ListEntry);
2286 EventLogFilter_Release(EventLogFilter);
2287
2288 // /* Select the default event log */
2289 // // TreeView_Expand(hwndTreeView, htiUserLogs, TVE_EXPAND);
2290 // TreeView_SelectItem(hwndTreeView, hItem);
2291 // TreeView_EnsureVisible(hwndTreeView, hItem);
2292 InvalidateRect(hwndTreeView, NULL, FALSE);
2293 SetFocus(hwndTreeView);
2294 }
2295
2296
2297 BOOL
2298 ClearEvents(IN PEVENTLOGFILTER EventLogFilter)
2299 {
2300 BOOL Success;
2301 PEVENTLOG EventLog;
2302 HANDLE hEventLog;
2303 WCHAR szFileName[MAX_PATH];
2304 WCHAR szMessage[MAX_LOADSTRING];
2305
2306 /* Bail out if there is no available filter */
2307 if (!EventLogFilter)
2308 return FALSE;
2309
2310 ZeroMemory(szFileName, sizeof(szFileName));
2311 ZeroMemory(szMessage, sizeof(szMessage));
2312
2313 LoadStringW(hInst, IDS_CLEAREVENTS_MSG, szMessage, ARRAYSIZE(szMessage));
2314
2315 sfn.lpstrFile = szFileName;
2316 sfn.nMaxFile = ARRAYSIZE(szFileName);
2317
2318 switch (MessageBoxW(hwndMainWindow, szMessage, szTitle, MB_YESNOCANCEL | MB_ICONINFORMATION))
2319 {
2320 case IDCANCEL:
2321 return FALSE;
2322
2323 case IDNO:
2324 sfn.lpstrFile = NULL;
2325 break;
2326
2327 case IDYES:
2328 if (!GetSaveFileNameW(&sfn))
2329 return FALSE;
2330 break;
2331 }
2332
2333 EventLogFilter_AddRef(EventLogFilter);
2334
2335 EventLog = EventLogFilter->EventLogs[0];
2336 hEventLog = OpenEventLogW(EventLog->ComputerName, EventLog->LogName);
2337
2338 EventLogFilter_Release(EventLogFilter);
2339
2340 if (!hEventLog)
2341 {
2342 ShowWin32Error(GetLastError());
2343 return FALSE;
2344 }
2345
2346 Success = ClearEventLogW(hEventLog, sfn.lpstrFile);
2347 if (!Success)
2348 ShowWin32Error(GetLastError());
2349
2350 CloseEventLog(hEventLog);
2351 return Success;
2352 }
2353
2354
2355 VOID
2356 Refresh(IN PEVENTLOGFILTER EventLogFilter)
2357 {
2358 /* Bail out if there is no available filter */
2359 if (!EventLogFilter)
2360 return;
2361
2362 /* Reenumerate the events through the filter */
2363 EnumEvents(EventLogFilter);
2364 }
2365
2366
2367 ATOM
2368 MyRegisterClass(HINSTANCE hInstance)
2369 {
2370 WNDCLASSEXW wcex;
2371
2372 wcex.cbSize = sizeof(wcex);
2373 wcex.style = 0;
2374 wcex.lpfnWndProc = WndProc;
2375 wcex.cbClsExtra = 0;
2376 wcex.cbWndExtra = 0;
2377 wcex.hInstance = hInstance;
2378 wcex.hIcon = LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_EVENTVWR));
2379 wcex.hCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_ARROW));
2380 wcex.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); // COLOR_WINDOW + 1
2381 wcex.lpszMenuName = MAKEINTRESOURCEW(IDM_EVENTVWR);
2382 wcex.lpszClassName = EVENTVWR_WNDCLASS;
2383 wcex.hIconSm = (HICON)LoadImageW(hInstance,
2384 MAKEINTRESOURCEW(IDI_EVENTVWR),
2385 IMAGE_ICON,
2386 16,
2387 16,
2388 LR_SHARED);
2389
2390 return RegisterClassExW(&wcex);
2391 }
2392
2393
2394 BOOL
2395 GetDisplayNameFileAndID(IN LPCWSTR lpLogName,
2396 OUT PWCHAR lpModuleName, // TODO: Add IN DWORD BufLen
2397 OUT PDWORD pdwMessageID)
2398 {
2399 BOOL Success = FALSE;
2400 LONG Result;
2401 HKEY hLogKey;
2402 WCHAR *KeyPath;
2403 SIZE_T cbKeyPath;
2404 DWORD dwType, cbData;
2405 DWORD dwMessageID = 0;
2406 WCHAR szModuleName[MAX_PATH];
2407
2408 /* Use a default value for the message ID */
2409 *pdwMessageID = 0;
2410
2411 cbKeyPath = (wcslen(EVENTLOG_BASE_KEY) + wcslen(lpLogName) + 1) * sizeof(WCHAR);
2412 KeyPath = HeapAlloc(GetProcessHeap(), 0, cbKeyPath);
2413 if (!KeyPath)
2414 return FALSE;
2415
2416 StringCbCopyW(KeyPath, cbKeyPath, EVENTLOG_BASE_KEY);
2417 StringCbCatW(KeyPath, cbKeyPath, lpLogName);
2418
2419 Result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, KeyPath, 0, KEY_QUERY_VALUE, &hLogKey);
2420 HeapFree(GetProcessHeap(), 0, KeyPath);
2421 if (Result != ERROR_SUCCESS)
2422 return FALSE;
2423
2424 cbData = sizeof(szModuleName);
2425 Result = RegQueryValueExW(hLogKey,
2426 L"DisplayNameFile",
2427 NULL,
2428 &dwType,
2429 (LPBYTE)szModuleName,
2430 &cbData);
2431 if ((Result != ERROR_SUCCESS) || (dwType != REG_EXPAND_SZ && dwType != REG_SZ))
2432 {
2433 szModuleName[0] = UNICODE_NULL;
2434 }
2435 else
2436 {
2437 /* NULL-terminate the string and expand it */
2438 szModuleName[cbData / sizeof(WCHAR) - 1] = UNICODE_NULL;
2439 ExpandEnvironmentStringsW(szModuleName, lpModuleName, ARRAYSIZE(szModuleName));
2440 Success = TRUE;
2441 }
2442
2443 /*
2444 * If we have a 'DisplayNameFile', query for 'DisplayNameID';
2445 * otherwise it's not really useful. 'DisplayNameID' is optional.
2446 */
2447 if (Success)
2448 {
2449 cbData = sizeof(dwMessageID);
2450 Result = RegQueryValueExW(hLogKey,
2451 L"DisplayNameID",
2452 NULL,
2453 &dwType,
2454 (LPBYTE)&dwMessageID,
2455 &cbData);
2456 if ((Result != ERROR_SUCCESS) || (dwType != REG_DWORD))
2457 dwMessageID = 0;
2458
2459 *pdwMessageID = dwMessageID;
2460 }
2461
2462 RegCloseKey(hLogKey);
2463
2464 return Success;
2465 }
2466
2467
2468 VOID
2469 BuildLogListAndFilterList(IN LPCWSTR lpComputerName)
2470 {
2471 LONG Result;
2472 HKEY hEventLogKey, hLogKey;
2473 DWORD dwNumLogs = 0;
2474 DWORD dwIndex, dwMaxKeyLength;
2475 DWORD dwType;
2476 PEVENTLOG EventLog;
2477 PEVENTLOGFILTER EventLogFilter;
2478 LPWSTR LogName = NULL;
2479 WCHAR szModuleName[MAX_PATH];
2480 DWORD lpcName;
2481 DWORD dwMessageID;
2482 LPWSTR lpDisplayName;
2483 HTREEITEM hRootNode = NULL, hItem = NULL, hItemDefault = NULL;
2484
2485 /* Open the EventLog key */
2486 // TODO: Implement connection to remote computer...
2487 // At the moment we only support the user local computer.
2488 // FIXME: Use local or remote computer
2489 Result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, EVENTLOG_BASE_KEY, 0, KEY_READ, &hEventLogKey);
2490 if (Result != ERROR_SUCCESS)
2491 {
2492 return;
2493 }
2494
2495 /* Retrieve the number of event logs enumerated as registry keys */
2496 Result = RegQueryInfoKeyW(hEventLogKey, NULL, NULL, NULL, &dwNumLogs, &dwMaxKeyLength,
2497 NULL, NULL, NULL, NULL, NULL, NULL);
2498 if (Result != ERROR_SUCCESS)
2499 {
2500 goto Quit;
2501 }
2502 if (!dwNumLogs)
2503 goto Quit;
2504
2505 /* Take the NULL terminator into account */
2506 ++dwMaxKeyLength;
2507
2508 /* Allocate the temporary buffer */
2509 LogName = HeapAlloc(GetProcessHeap(), 0, dwMaxKeyLength * sizeof(WCHAR));
2510 if (!LogName)
2511 goto Quit;
2512
2513 /* Enumerate and retrieve each event log name */
2514 for (dwIndex = 0; dwIndex < dwNumLogs; dwIndex++)
2515 {
2516 lpcName = dwMaxKeyLength;
2517 Result = RegEnumKeyExW(hEventLogKey, dwIndex, LogName, &lpcName, NULL, NULL, NULL, NULL);
2518 if (Result != ERROR_SUCCESS)
2519 continue;
2520
2521 /* Take the NULL terminator into account */
2522 ++lpcName;
2523
2524 /* Allocate a new event log entry */
2525 EventLog = AllocEventLog(lpComputerName, LogName, TRUE);
2526 if (EventLog == NULL)
2527 continue;
2528
2529 /* Allocate a new event log filter entry for this event log */
2530 EventLogFilter = AllocEventLogFilter(// LogName,
2531 TRUE, TRUE, TRUE, TRUE, TRUE,
2532 NULL, NULL, NULL,
2533 1, &EventLog);
2534 if (EventLogFilter == NULL)
2535 {
2536 EventLog_Free(EventLog);
2537 continue;
2538 }
2539
2540 /* Add the event log and the filter into their lists */
2541 InsertTailList(&EventLogList, &EventLog->ListEntry);
2542 InsertTailList(&EventLogFilterList, &EventLogFilter->ListEntry);
2543
2544 EventLog->FileName = NULL;
2545
2546 /* Retrieve and cache the event log file */
2547 Result = RegOpenKeyExW(hEventLogKey,
2548 LogName,
2549 0,
2550 KEY_QUERY_VALUE,
2551 &hLogKey);
2552 if (Result == ERROR_SUCCESS)
2553 {
2554 lpcName = 0;
2555 Result = RegQueryValueExW(hLogKey,
2556 L"File",
2557 NULL,
2558 &dwType,
2559 NULL,
2560 &lpcName);
2561 if ((Result != ERROR_SUCCESS) || (dwType != REG_EXPAND_SZ && dwType != REG_SZ))
2562 {
2563 // Windows' EventLog uses some kind of default value, we do not.
2564 EventLog->FileName = NULL;
2565 }
2566 else
2567 {
2568 lpcName = ROUND_DOWN(lpcName, sizeof(WCHAR));
2569 EventLog->FileName = HeapAlloc(GetProcessHeap(), 0, lpcName);
2570 if (EventLog->FileName)
2571 {
2572 Result = RegQueryValueExW(hLogKey,
2573 L"File",
2574 NULL,
2575 &dwType,
2576 (LPBYTE)EventLog->FileName,
2577 &lpcName);
2578 if (Result != ERROR_SUCCESS)
2579 {
2580 HeapFree(GetProcessHeap(), 0, EventLog->FileName);
2581 EventLog->FileName = NULL;
2582 }
2583 else
2584 {
2585 EventLog->FileName[lpcName / sizeof(WCHAR) - 1] = UNICODE_NULL;
2586 }
2587 }
2588 }
2589
2590 RegCloseKey(hLogKey);
2591 }
2592
2593 /* Get the display name for the event log */
2594 lpDisplayName = NULL;
2595
2596 ZeroMemory(szModuleName, sizeof(szModuleName));
2597 if (GetDisplayNameFileAndID(LogName, szModuleName, &dwMessageID))
2598 {
2599 /* Retrieve the message string without appending extra newlines */
2600 lpDisplayName =
2601 GetMessageStringFromDll(szModuleName,
2602 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
2603 FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
2604 dwMessageID,
2605 0,
2606 NULL);
2607 }
2608
2609 /*
2610 * Select the correct tree root node, whether the log is a System
2611 * or an Application log. Default to Application log otherwise.
2612 */
2613 hRootNode = htiAppLogs;
2614 for (lpcName = 0; lpcName < ARRAYSIZE(SystemLogs); ++lpcName)
2615 {
2616 /* Check whether the log name is part of the system logs */
2617 if (wcsicmp(LogName, SystemLogs[lpcName]) == 0)
2618 {
2619 hRootNode = htiSystemLogs;
2620 break;
2621 }
2622 }
2623
2624 hItem = TreeViewAddItem(hwndTreeView, hRootNode,
2625 (lpDisplayName ? lpDisplayName : LogName),
2626 2, 3, (LPARAM)EventLogFilter);
2627
2628 /* Try to get the default event log: "Application" */
2629 if ((hItemDefault == NULL) && (wcsicmp(LogName, SystemLogs[0]) == 0))
2630 {
2631 hItemDefault = hItem;
2632 }
2633
2634 /* Free the buffer allocated by FormatMessage */
2635 if (lpDisplayName)
2636 LocalFree(lpDisplayName);
2637 }
2638
2639 HeapFree(GetProcessHeap(), 0, LogName);
2640
2641 Quit:
2642 RegCloseKey(hEventLogKey);
2643
2644 /* Select the default event log */
2645 if (hItemDefault)
2646 {
2647 // TreeView_Expand(hwndTreeView, hRootNode, TVE_EXPAND);
2648 TreeView_SelectItem(hwndTreeView, hItemDefault);
2649 TreeView_EnsureVisible(hwndTreeView, hItemDefault);
2650 }
2651 InvalidateRect(hwndTreeView, NULL, FALSE);
2652 SetFocus(hwndTreeView);
2653
2654 return;
2655 }
2656
2657 VOID
2658 FreeLogList(VOID)
2659 {
2660 PLIST_ENTRY Entry;
2661 PEVENTLOG EventLog;
2662
2663 while (!IsListEmpty(&EventLogList))
2664 {
2665 Entry = RemoveHeadList(&EventLogList);
2666 EventLog = (PEVENTLOG)CONTAINING_RECORD(Entry, EVENTLOG, ListEntry);
2667 EventLog_Free(EventLog);
2668 }
2669
2670 return;
2671 }
2672
2673 VOID
2674 FreeLogFilterList(VOID)
2675 {
2676 PLIST_ENTRY Entry;
2677 PEVENTLOGFILTER EventLogFilter;
2678
2679 while (!IsListEmpty(&EventLogFilterList))
2680 {
2681 Entry = RemoveHeadList(&EventLogFilterList);
2682 EventLogFilter = (PEVENTLOGFILTER)CONTAINING_RECORD(Entry, EVENTLOGFILTER, ListEntry);
2683 EventLogFilter_Free(EventLogFilter);
2684 }
2685
2686 ActiveFilter = NULL;
2687
2688 return;
2689 }
2690
2691 BOOL
2692 InitInstance(HINSTANCE hInstance,
2693 int nCmdShow)
2694 {
2695 RECT rcClient, rs;
2696 LONG StatusHeight;
2697 HIMAGELIST hSmall;
2698 LVCOLUMNW lvc = {0};
2699 WCHAR szTemp[256];
2700
2701 /* Create the main window */
2702 hwndMainWindow = CreateWindowW(EVENTVWR_WNDCLASS,
2703 szTitle,
2704 WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
2705 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
2706 NULL,
2707 NULL,
2708 hInstance,
2709 NULL);
2710 if (!hwndMainWindow)
2711 return FALSE;
2712
2713 /* Create the status bar */
2714 hwndStatus = CreateWindowExW(0, // no extended styles
2715 STATUSCLASSNAMEW, // status bar
2716 L"", // no text
2717 WS_CHILD | WS_VISIBLE | CCS_BOTTOM | SBARS_SIZEGRIP, // styles
2718 0, 0, 0, 0, // x, y, cx, cy
2719 hwndMainWindow, // parent window
2720 (HMENU)100, // window ID
2721 hInstance, // instance
2722 NULL); // window data
2723
2724 GetClientRect(hwndMainWindow, &rcClient);
2725 GetWindowRect(hwndStatus, &rs);
2726 StatusHeight = rs.bottom - rs.top;
2727
2728 /* Create a progress bar in the status bar (hidden by default) */
2729 StatusBar_GetItemRect(hwndStatus, 0, &rs);
2730 hwndStatusProgress = CreateWindowExW(0, // no extended styles
2731 PROGRESS_CLASSW, // status bar
2732 NULL, // no text
2733 WS_CHILD | PBS_SMOOTH, // styles
2734 rs.left, rs.top, // x, y
2735 rs.right - rs.left, rs.bottom - rs.top, // cx, cy
2736 hwndStatus, // parent window
2737 NULL, // window ID
2738 hInstance, // instance
2739 NULL); // window data
2740 ProgressBar_SetStep(hwndStatusProgress, 1);
2741
2742 /* Initialize the splitter default positions */
2743 nVSplitPos = 250;
2744 nHSplitPos = 250;
2745
2746 /* Create the TreeView */
2747 hwndTreeView = CreateWindowExW(WS_EX_CLIENTEDGE,
2748 WC_TREEVIEWW,
2749 NULL,
2750 // WS_CHILD | WS_VISIBLE | TVS_HASLINES | TVS_SHOWSELALWAYS,
2751 WS_CHILD | WS_VISIBLE | /* WS_TABSTOP | */ TVS_HASLINES | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_EDITLABELS | TVS_SHOWSELALWAYS,
2752 0, 0,
2753 nVSplitPos - SPLIT_WIDTH/2,
2754 (rcClient.bottom - rcClient.top) - StatusHeight,
2755 hwndMainWindow,
2756 NULL,
2757 hInstance,
2758 NULL);
2759
2760 /* Create the ImageList */
2761 hSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
2762 GetSystemMetrics(SM_CYSMICON),
2763 ILC_COLOR32 | ILC_MASK, // ILC_COLOR24
2764 1, 1);
2765
2766 /* Add event type icons to the ImageList: closed/opened folder, event log (normal/viewed) */
2767 ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_CLOSED_CATEGORY)));
2768 ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_OPENED_CATEGORY)));
2769 ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_EVENTLOG)));
2770 ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_EVENTVWR)));
2771
2772 /* Assign the ImageList to the Tree View */
2773 TreeView_SetImageList(hwndTreeView, hSmall, TVSIL_NORMAL);
2774
2775 /* Add the event logs nodes */
2776 // "System Logs"
2777 LoadStringW(hInstance, IDS_EVENTLOG_SYSTEM, szTemp, ARRAYSIZE(szTemp));
2778 htiSystemLogs = TreeViewAddItem(hwndTreeView, NULL, szTemp, 0, 1, (LPARAM)NULL);
2779 // "Application Logs"
2780 LoadStringW(hInstance, IDS_EVENTLOG_APP, szTemp, ARRAYSIZE(szTemp));
2781 htiAppLogs = TreeViewAddItem(hwndTreeView, NULL, szTemp, 0, 1, (LPARAM)NULL);
2782 // "User Logs"
2783 LoadStringW(hInstance, IDS_EVENTLOG_USER, szTemp, ARRAYSIZE(szTemp));
2784 htiUserLogs = TreeViewAddItem(hwndTreeView, NULL, szTemp, 0, 1, (LPARAM)NULL);
2785
2786 /* Create the Event details pane (optional) */
2787 hwndEventDetails = CreateEventDetailsCtrl(hInst, hwndMainWindow, (LPARAM)NULL);
2788 // hwndEventDetails = NULL;
2789 if (hwndEventDetails)
2790 {
2791 // SetWindowLongPtrW(hwndEventDetails, GWL_STYLE,
2792 // GetWindowLongPtrW(hwndEventDetails, GWL_STYLE) | WS_BORDER);
2793 SetWindowLongPtrW(hwndEventDetails, GWL_EXSTYLE,
2794 GetWindowLongPtrW(hwndEventDetails, GWL_EXSTYLE) | WS_EX_CLIENTEDGE);
2795 SetWindowPos(hwndEventDetails, NULL,
2796 nVSplitPos + SPLIT_WIDTH/2,
2797 nHSplitPos + SPLIT_WIDTH/2,
2798 (rcClient.right - rcClient.left) - nVSplitPos - SPLIT_WIDTH/2,
2799 (rcClient.bottom - rcClient.top) - nHSplitPos - SPLIT_WIDTH/2 - StatusHeight,
2800 SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
2801 }
2802
2803 /* Create the ListView */
2804 hwndListView = CreateWindowExW(WS_EX_CLIENTEDGE,
2805 WC_LISTVIEWW,
2806 NULL,
2807 WS_CHILD | WS_VISIBLE | LVS_SHOWSELALWAYS | LVS_REPORT,
2808 nVSplitPos + SPLIT_WIDTH/2,
2809 0,
2810 (rcClient.right - rcClient.left) - nVSplitPos - SPLIT_WIDTH/2,
2811 hwndEventDetails ? nHSplitPos - SPLIT_WIDTH/2
2812 : (rcClient.bottom - rcClient.top) - StatusHeight,
2813 hwndMainWindow,
2814 NULL,
2815 hInstance,
2816 NULL);
2817
2818 /* Add the extended ListView styles */
2819 ListView_SetExtendedListViewStyle(hwndListView, LVS_EX_HEADERDRAGDROP | LVS_EX_FULLROWSELECT |LVS_EX_LABELTIP);
2820
2821 /* Create the ImageList */
2822 hSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
2823 GetSystemMetrics(SM_CYSMICON),
2824 ILC_COLOR32 | ILC_MASK, // ILC_COLOR24
2825 1, 1);
2826
2827 /* Add event type icons to the ImageList */
2828 ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_INFORMATIONICON)));
2829 ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_WARNINGICON)));
2830 ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_ERRORICON)));
2831 ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_AUDITSUCCESSICON)));
2832 ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_AUDITFAILUREICON)));
2833
2834 /* Assign the ImageList to the List View */
2835 ListView_SetImageList(hwndListView, hSmall, LVSIL_SMALL);
2836
2837 /* Now set up the listview with its columns */
2838 lvc.mask = LVCF_TEXT | LVCF_WIDTH;
2839 lvc.cx = 90;
2840 LoadStringW(hInstance,
2841 IDS_COLUMNTYPE,
2842 szTemp,
2843 ARRAYSIZE(szTemp));
2844 lvc.pszText = szTemp;
2845 ListView_InsertColumn(hwndListView, 0, &lvc);
2846
2847 lvc.cx = 70;
2848 LoadStringW(hInstance,
2849 IDS_COLUMNDATE,
2850 szTemp,
2851 ARRAYSIZE(szTemp));
2852 lvc.pszText = szTemp;
2853 ListView_InsertColumn(hwndListView, 1, &lvc);
2854
2855 lvc.cx = 70;
2856 LoadStringW(hInstance,
2857 IDS_COLUMNTIME,
2858 szTemp,
2859 ARRAYSIZE(szTemp));
2860 lvc.pszText = szTemp;
2861 ListView_InsertColumn(hwndListView, 2, &lvc);
2862
2863 lvc.cx = 150;
2864 LoadStringW(hInstance,
2865 IDS_COLUMNSOURCE,
2866 szTemp,
2867 ARRAYSIZE(szTemp));
2868 lvc.pszText = szTemp;
2869 ListView_InsertColumn(hwndListView, 3, &lvc);
2870
2871 lvc.cx = 100;
2872 LoadStringW(hInstance,
2873 IDS_COLUMNCATEGORY,
2874 szTemp,
2875 ARRAYSIZE(szTemp));
2876 lvc.pszText = szTemp;
2877 ListView_InsertColumn(hwndListView, 4, &lvc);
2878
2879 lvc.cx = 60;
2880 LoadStringW(hInstance,
2881 IDS_COLUMNEVENT,
2882 szTemp,
2883 ARRAYSIZE(szTemp));
2884 lvc.pszText = szTemp;
2885 ListView_InsertColumn(hwndListView, 5, &lvc);
2886
2887 lvc.cx = 120;
2888 LoadStringW(hInstance,
2889 IDS_COLUMNUSER,
2890 szTemp,
2891 ARRAYSIZE(szTemp));
2892 lvc.pszText = szTemp;
2893 ListView_InsertColumn(hwndListView, 6, &lvc);
2894
2895 lvc.cx = 100;
2896 LoadStringW(hInstance,
2897 IDS_COLUMNCOMPUTER,
2898 szTemp,
2899 ARRAYSIZE(szTemp));
2900 lvc.pszText = szTemp;
2901 ListView_InsertColumn(hwndListView, 7, &lvc);
2902
2903 /* Initialize the save Dialog */
2904 ZeroMemory(&sfn, sizeof(sfn));
2905 ZeroMemory(szSaveFilter, sizeof(szSaveFilter));
2906
2907 LoadStringW(hInst, IDS_SAVE_FILTER, szSaveFilter, ARRAYSIZE(szSaveFilter));
2908
2909 sfn.lStructSize = sizeof(sfn);
2910 sfn.hwndOwner = hwndMainWindow;
2911 sfn.hInstance = hInstance;
2912 sfn.lpstrFilter = szSaveFilter;
2913 sfn.lpstrInitialDir = NULL;
2914 sfn.Flags = OFN_HIDEREADONLY | OFN_SHAREAWARE;
2915 sfn.lpstrDefExt = NULL;
2916
2917 ShowWindow(hwndMainWindow, nCmdShow);
2918 UpdateWindow(hwndMainWindow);
2919
2920 return TRUE;
2921 }
2922
2923 VOID ResizeWnd(INT cx, INT cy)
2924 {
2925 HDWP hdwp;
2926 RECT rs;
2927 LONG StatusHeight;
2928
2929 /* Resize the status bar -- now done in WM_SIZE */
2930 // SendMessageW(hwndStatus, WM_SIZE, 0, 0);
2931 GetWindowRect(hwndStatus, &rs);
2932 StatusHeight = rs.bottom - rs.top;
2933
2934 /* Move the progress bar */
2935 StatusBar_GetItemRect(hwndStatus, 0, &rs);
2936 MoveWindow(hwndStatusProgress,
2937 rs.left, rs.top, rs.right-rs.left, rs.bottom-rs.top,
2938 IsWindowVisible(hwndStatusProgress) ? TRUE : FALSE);
2939
2940 /*
2941 * TODO: Adjust the splitter positions:
2942 * - Vertical splitter (1) : fixed position from the left window side.
2943 * - Horizontal splitter (2): fixed position from the bottom window side.
2944 */
2945 nVSplitPos = min(max(nVSplitPos, SPLIT_WIDTH/2), cx - SPLIT_WIDTH/2); // OK
2946 nHSplitPos = min(max(nHSplitPos, SPLIT_WIDTH/2), cy - SPLIT_WIDTH/2 - StatusHeight); // FIXME!
2947
2948 hdwp = BeginDeferWindowPos(3);
2949
2950 if (hdwp)
2951 hdwp = DeferWindowPos(hdwp,
2952 hwndTreeView,
2953 HWND_TOP,
2954 0, 0,
2955 nVSplitPos - SPLIT_WIDTH/2,
2956 cy - StatusHeight,
2957 SWP_NOZORDER | SWP_NOACTIVATE);
2958
2959 if (hdwp)
2960 hdwp = DeferWindowPos(hdwp,
2961 hwndListView,
2962 HWND_TOP,
2963 nVSplitPos + SPLIT_WIDTH/2, 0,
2964 cx - nVSplitPos - SPLIT_WIDTH/2,
2965 hwndEventDetails ? nHSplitPos - SPLIT_WIDTH/2
2966 : cy - StatusHeight,
2967 SWP_NOZORDER | SWP_NOACTIVATE);
2968
2969 if (hwndEventDetails && hdwp)
2970 hdwp = DeferWindowPos(hdwp,
2971 hwndEventDetails,
2972 HWND_TOP,
2973 nVSplitPos + SPLIT_WIDTH/2,
2974 nHSplitPos + SPLIT_WIDTH/2,
2975 cx - nVSplitPos - SPLIT_WIDTH/2,
2976 cy - nHSplitPos - SPLIT_WIDTH/2 - StatusHeight,
2977 SWP_NOZORDER | SWP_NOACTIVATE);
2978
2979 if (hdwp)
2980 EndDeferWindowPos(hdwp);
2981 }
2982
2983
2984 LRESULT CALLBACK
2985 WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
2986 {
2987 RECT rect;
2988
2989 switch (uMsg)
2990 {
2991 case WM_CREATE:
2992 hMainMenu = GetMenu(hWnd);
2993 break;
2994
2995 case WM_DESTROY:
2996 PostQuitMessage(0);
2997 break;
2998
2999 case WM_NOTIFY:
3000 {
3001 LPNMHDR hdr = (LPNMHDR)lParam;
3002
3003 if (hdr->hwndFrom == hwndListView)
3004 {
3005 switch (hdr->code)
3006 {
3007 case LVN_ITEMCHANGED:
3008 {
3009 LPNMLISTVIEW pnmv = (LPNMLISTVIEW)lParam;
3010
3011 if ( (pnmv->uChanged & LVIF_STATE) && /* The state has changed */
3012 (pnmv->uNewState & LVIS_SELECTED) /* The item has been (de)selected */ )
3013 {
3014 if (hwndEventDetails)
3015 SendMessageW(hwndEventDetails, EVT_DISPLAY, 0, 0);
3016 }
3017 break;
3018 }
3019
3020 case NM_DBLCLK:
3021 case NM_RETURN:
3022 SendMessageW(hWnd, WM_COMMAND, IDM_EVENT_DETAILS, 0);
3023 break;
3024 }
3025 }
3026 else if (hdr->hwndFrom == hwndTreeView)
3027 {
3028 switch (hdr->code)
3029 {
3030 case TVN_BEGINLABELEDIT:
3031 {
3032 HTREEITEM hItem = ((LPNMTVDISPINFO)lParam)->item.hItem;
3033
3034 /* Disable label editing for root nodes */
3035 return ((hItem == htiSystemLogs) ||
3036 (hItem == htiAppLogs) ||
3037 (hItem == htiUserLogs));
3038 }
3039
3040 case TVN_ENDLABELEDIT:
3041 {
3042 TVITEMW item = ((LPNMTVDISPINFO)lParam)->item;
3043 HTREEITEM hItem = item.hItem;
3044
3045 /* Disable label editing for root nodes */
3046 if ((hItem == htiSystemLogs) ||
3047 (hItem == htiAppLogs) ||
3048 (hItem == htiUserLogs))
3049 {
3050 return FALSE;
3051 }
3052
3053 if (item.pszText)
3054 {
3055 LPWSTR pszText = item.pszText;
3056
3057 /* Trim leading whitespace */
3058 while (*pszText && iswspace(*pszText))
3059 ++pszText;
3060
3061 if (!*pszText)
3062 return FALSE;
3063
3064 return TRUE;
3065 }
3066 else
3067 {
3068 return FALSE;
3069 }
3070 }
3071
3072 case TVN_SELCHANGED:
3073 {
3074 PEVENTLOGFILTER EventLogFilter =
3075 (PEVENTLOGFILTER)((LPNMTREEVIEW)lParam)->itemNew.lParam;
3076
3077 // FIXME: It might be nice to reference here the filter,
3078 // so that we don't have to reference/dereference it many times
3079 // in the other functions???
3080
3081 // FIXME: This is a hack!!
3082 if (hwndEventDetails && EventLogFilter)
3083 {
3084 SendMessageW(hwndEventDetails, EVT_SETFILTER, 0, (LPARAM)EventLogFilter);
3085 }
3086
3087 if (EventLogFilter)
3088 {
3089 /*
3090 * If we have selected a filter, enable the menu commands;
3091 * they will possibly be updated after events enumeration.
3092 */
3093 EnableMenuItem(hMainMenu, IDM_SAVE_EVENTLOG, MF_BYCOMMAND | MF_ENABLED);
3094 EnableMenuItem(hMainMenu, IDM_CLOSE_EVENTLOG, MF_BYCOMMAND | MF_ENABLED);
3095 EnableMenuItem(hMainMenu, IDM_CLEAR_EVENTS, MF_BYCOMMAND | MF_ENABLED);
3096 EnableMenuItem(hMainMenu, IDM_RENAME_EVENTLOG, MF_BYCOMMAND | MF_ENABLED);
3097 EnableMenuItem(hMainMenu, IDM_EVENTLOG_SETTINGS, MF_BYCOMMAND | MF_ENABLED);
3098 }
3099 else
3100 {
3101 EnableMenuItem(hMainMenu, IDM_SAVE_EVENTLOG, MF_BYCOMMAND | MF_GRAYED);
3102 EnableMenuItem(hMainMenu, IDM_CLOSE_EVENTLOG, MF_BYCOMMAND | MF_GRAYED);
3103 EnableMenuItem(hMainMenu, IDM_CLEAR_EVENTS, MF_BYCOMMAND | MF_GRAYED);
3104 EnableMenuItem(hMainMenu, IDM_RENAME_EVENTLOG, MF_BYCOMMAND | MF_GRAYED);
3105 EnableMenuItem(hMainMenu, IDM_EVENTLOG_SETTINGS, MF_BYCOMMAND | MF_GRAYED);
3106 }
3107
3108 /*
3109 * The enumeration thread that is triggered by EnumEvents
3110 * will set a new value for the 'ActiveFilter'.
3111 */
3112 if (EventLogFilter)
3113 EnumEvents(EventLogFilter);
3114
3115 break;
3116 }
3117 }
3118 }
3119 break;
3120 }
3121
3122 case WM_COMMAND:
3123 {
3124 /* Parse the menu selections */
3125 switch (LOWORD(wParam))
3126 {
3127 case IDM_OPEN_EVENTLOG:
3128 OpenUserEventLog();
3129 break;
3130
3131 case IDM_SAVE_EVENTLOG:
3132 SaveEventLog(GetSelectedFilter(NULL));
3133 break;
3134
3135 case IDM_CLOSE_EVENTLOG:
3136 {
3137 HTREEITEM hti;
3138 PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(&hti);
3139 CloseUserEventLog(EventLogFilter, hti);
3140 break;
3141 }
3142
3143 case IDM_CLEAR_EVENTS:
3144 {
3145 PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(NULL);
3146 if (EventLogFilter && ClearEvents(EventLogFilter))
3147 Refresh(EventLogFilter);
3148 break;
3149 }
3150
3151 case IDM_RENAME_EVENTLOG:
3152 if (GetFocus() == hwndTreeView)
3153 TreeView_EditLabel(hwndTreeView, TreeView_GetSelection(hwndTreeView));
3154 break;
3155
3156 case IDM_EVENTLOG_SETTINGS:
3157 {
3158 PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(NULL);
3159 // TODO: Check the returned value?
3160 if (EventLogFilter)
3161 EventLogProperties(hInst, hWnd, EventLogFilter);
3162 break;
3163 }
3164
3165 case IDM_LIST_NEWEST:
3166 {
3167 CheckMenuRadioItem(hMainMenu, IDM_LIST_NEWEST, IDM_LIST_OLDEST, IDM_LIST_NEWEST, MF_BYCOMMAND);
3168 if (!NewestEventsFirst)
3169 {
3170 NewestEventsFirst = TRUE;
3171 Refresh(GetSelectedFilter(NULL));
3172 }
3173 break;
3174 }
3175
3176 case IDM_LIST_OLDEST:
3177 {
3178 CheckMenuRadioItem(hMainMenu, IDM_LIST_NEWEST, IDM_LIST_OLDEST, IDM_LIST_OLDEST, MF_BYCOMMAND);
3179 if (NewestEventsFirst)
3180 {
3181 NewestEventsFirst = FALSE;
3182 Refresh(GetSelectedFilter(NULL));
3183 }
3184 break;
3185 }
3186
3187 case IDM_EVENT_DETAILS:
3188 {
3189 // LPNMITEMACTIVATE lpnmitem = (LPNMITEMACTIVATE)lParam;
3190 PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(NULL);
3191 if (/*lpnmitem->iItem != -1 &&*/ EventLogFilter)
3192 {
3193 EventLogFilter_AddRef(EventLogFilter);
3194 DialogBoxParamW(hInst,
3195 MAKEINTRESOURCEW(IDD_EVENTDETAILS_DLG),
3196 hWnd,
3197 EventDetails,
3198 (LPARAM)EventLogFilter);
3199 EventLogFilter_Release(EventLogFilter);
3200 }
3201 break;
3202 }
3203
3204 case IDM_REFRESH:
3205 Refresh(GetSelectedFilter(NULL));
3206 break;
3207
3208 case IDM_ABOUT:
3209 {
3210 HICON hIcon;
3211 WCHAR szCopyright[MAX_LOADSTRING];
3212
3213 hIcon = LoadIconW(hInst, MAKEINTRESOURCEW(IDI_EVENTVWR));
3214 LoadStringW(hInst, IDS_COPYRIGHT, szCopyright, ARRAYSIZE(szCopyright));
3215 ShellAboutW(hWnd, szTitle, szCopyright, hIcon);
3216 DeleteObject(hIcon);
3217 break;
3218 }
3219
3220 case IDM_HELP:
3221 MessageBoxW(hwndMainWindow,
3222 L"Help not implemented yet!",
3223 L"Event Log",
3224 MB_OK | MB_ICONINFORMATION);
3225 break;
3226
3227 case IDM_EXIT:
3228 DestroyWindow(hWnd);
3229 break;
3230
3231 default:
3232 return DefWindowProcW(hWnd, uMsg, wParam, lParam);
3233 }
3234 break;
3235 }
3236
3237 case WM_SETCURSOR:
3238 {
3239 POINT pt;
3240
3241 if (LOWORD(lParam) != HTCLIENT)
3242 goto Default;
3243
3244 GetCursorPos(&pt);
3245 ScreenToClient(hWnd, &pt);
3246
3247 /* Set the cursor for the vertical splitter */
3248 if (pt.x >= nVSplitPos - SPLIT_WIDTH/2 && pt.x < nVSplitPos + SPLIT_WIDTH/2 + 1)
3249 {
3250 RECT rs;
3251 GetClientRect(hWnd, &rect);
3252 GetWindowRect(hwndStatus, &rs);
3253 if (pt.y >= rect.top && pt.y < rect.bottom - (rs.bottom - rs.top))
3254 {
3255 SetCursor(LoadCursorW(NULL, IDC_SIZEWE));
3256 return TRUE;
3257 }
3258 }
3259 else
3260 /* Set the cursor for the horizontal splitter, if the Event details pane is displayed */
3261 if (hwndEventDetails &&
3262 (pt.y >= nHSplitPos - SPLIT_WIDTH/2 && pt.y < nHSplitPos + SPLIT_WIDTH/2 + 1))
3263 {
3264 // RECT rs;
3265 GetClientRect(hWnd, &rect);
3266 // GetWindowRect(hwndStatus, &rs);
3267 if (pt.x >= nVSplitPos + SPLIT_WIDTH/2 + 1 /* rect.left + (rs.bottom - rs.top) */ &&
3268 pt.x < rect.right)
3269 {
3270 SetCursor(LoadCursorW(NULL, IDC_SIZENS));
3271 return TRUE;
3272 }
3273 }
3274 goto Default;
3275 }
3276
3277 case WM_LBUTTONDOWN:
3278 {
3279 INT x = GET_X_LPARAM(lParam);
3280 INT y = GET_Y_LPARAM(lParam);
3281
3282 /* Reset the splitter state */
3283 bSplit = 0;
3284
3285 /* Capture the cursor for the vertical splitter */
3286 if (x >= nVSplitPos - SPLIT_WIDTH/2 && x < nVSplitPos + SPLIT_WIDTH/2 + 1)
3287 {
3288 bSplit = 1;
3289 SetCapture(hWnd);
3290 }
3291 else
3292 /* Capture the cursor for the horizontal splitter, if the Event details pane is displayed */
3293 if (hwndEventDetails &&
3294 (y >= nHSplitPos - SPLIT_WIDTH/2 && y < nHSplitPos + SPLIT_WIDTH/2 + 1))
3295 {
3296 bSplit = 2;
3297 SetCapture(hWnd);
3298 }
3299 break;
3300 }
3301
3302 case WM_LBUTTONUP:
3303 case WM_RBUTTONDOWN:
3304 {
3305 if (GetCapture() != hWnd)
3306 break;
3307
3308 /* Adjust the correct splitter position */
3309 if (bSplit == 1)
3310 nVSplitPos = GET_X_LPARAM(lParam);
3311 else if (bSplit == 2)
3312 nHSplitPos = GET_Y_LPARAM(lParam);
3313
3314 /* If we are splitting, resize the windows */
3315 if (bSplit != 0)
3316 {
3317 GetClientRect(hWnd, &rect);
3318 ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
3319 }
3320
3321 /* Reset the splitter state */
3322 bSplit = 0;
3323
3324 ReleaseCapture();
3325 break;
3326 }
3327
3328 case WM_MOUSEMOVE:
3329 {
3330 if (GetCapture() != hWnd)
3331 break;
3332
3333 /* Move the correct splitter */
3334 if (bSplit == 1)
3335 {
3336 INT x = GET_X_LPARAM(lParam);
3337
3338 GetClientRect(hWnd, &rect);
3339
3340 x = min(max(x, SPLIT_WIDTH/2), rect.right - rect.left - SPLIT_WIDTH/2);
3341 if (nVSplitPos != x)
3342 {
3343 nVSplitPos = x;
3344 ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
3345 }
3346 }
3347 else if (bSplit == 2)
3348 {
3349 RECT rs;
3350 INT y = GET_Y_LPARAM(lParam);
3351
3352 GetClientRect(hWnd, &rect);
3353 GetWindowRect(hwndStatus, &rs);
3354
3355 y = min(max(y, SPLIT_WIDTH/2), rect.bottom - rect.top - SPLIT_WIDTH/2 - (rs.bottom - rs.top));
3356 if (nHSplitPos != y)
3357 {
3358 nHSplitPos = y;
3359 ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
3360 }
3361 }
3362 break;
3363 }
3364
3365 case WM_SIZE:
3366 {
3367 if (wParam != SIZE_MINIMIZED)
3368 {
3369 SendMessageW(hwndStatus, WM_SIZE, 0, 0);
3370 ResizeWnd(LOWORD(lParam), HIWORD(lParam));
3371 break;
3372 }
3373 /* Fall through the default case */
3374 }
3375
3376 default: Default:
3377 return DefWindowProcW(hWnd, uMsg, wParam, lParam);
3378 }
3379
3380 return 0;
3381 }
3382
3383
3384 static
3385 VOID
3386 InitPropertiesDlg(HWND hDlg, PEVENTLOG EventLog)
3387 {
3388 LPWSTR lpLogName = EventLog->LogName;
3389
3390 DWORD Result, dwType;
3391 DWORD dwMaxSize = 0, dwRetention = 0;
3392 BOOL Success;
3393 WIN32_FIND_DATAW FileInfo; // WIN32_FILE_ATTRIBUTE_DATA
3394 ULARGE_INTEGER FileSize;
3395 WCHAR wszBuf[MAX_PATH];
3396 WCHAR szTemp[MAX_LOADSTRING];
3397 LPWSTR FileName;
3398
3399 HKEY hLogKey;
3400 WCHAR *KeyPath;
3401 DWORD cbData;
3402 SIZE_T cbKeyPath;
3403
3404 if (EventLog->Permanent)
3405 {
3406
3407 cbKeyPath = (wcslen(EVENTLOG_BASE_KEY) + wcslen(lpLogName) + 1) * sizeof(WCHAR);
3408 KeyPath = HeapAlloc(GetProcessHeap(), 0, cbKeyPath);
3409 if (!KeyPath)
3410 {
3411 goto Quit;
3412 }
3413
3414 StringCbCopyW(KeyPath, cbKeyPath, EVENTLOG_BASE_KEY);
3415 StringCbCatW(KeyPath, cbKeyPath, lpLogName);
3416
3417 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, KeyPath, 0, KEY_QUERY_VALUE, &hLogKey) != ERROR_SUCCESS)
3418 {
3419 HeapFree(GetProcessHeap(), 0, KeyPath);
3420 goto Quit;
3421 }
3422 HeapFree(GetProcessHeap(), 0, KeyPath);
3423
3424
3425 cbData = sizeof(dwMaxSize);
3426 Result = RegQueryValueExW(hLogKey,
3427 L"MaxSize",
3428 NULL,
3429 &dwType,
3430 (LPBYTE)&dwMaxSize,
3431 &cbData);
3432 if ((Result != ERROR_SUCCESS) || (dwType != REG_DWORD))
3433 {
3434 // dwMaxSize = 512 * 1024; /* 512 kBytes */
3435 dwMaxSize = 0;
3436 }
3437 /* Convert in KB */
3438 dwMaxSize /= 1024;
3439
3440 cbData = sizeof(dwRetention);
3441 Result = RegQueryValueExW(hLogKey,
3442 L"Retention",
3443 NULL,
3444 &dwType,
3445 (LPBYTE)&dwRetention,
3446 &cbData);
3447 if ((Result != ERROR_SUCCESS) || (dwType != REG_DWORD))
3448 {
3449 /* On Windows 2003 it is 604800 (secs) == 7 days */
3450 dwRetention = 0;
3451 }
3452 /* Convert in days, rounded up */ // ROUND_UP
3453 // dwRetention = ROUND_UP(dwRetention, 24*3600) / (24*3600);
3454 dwRetention = (dwRetention + 24*3600 - 1) / (24*3600);
3455
3456
3457 RegCloseKey(hLogKey);
3458
3459 }
3460
3461
3462 Quit:
3463
3464 SetDlgItemTextW(hDlg, IDC_DISPLAYNAME, lpLogName); // FIXME!
3465 SetDlgItemTextW(hDlg, IDC_LOGNAME, lpLogName);
3466
3467 FileName = EventLog->FileName;
3468 if (FileName && *FileName)
3469 {
3470 ExpandEnvironmentStringsW(FileName, wszBuf, ARRAYSIZE(wszBuf));
3471 FileName = wszBuf;
3472 }
3473 else
3474 {
3475 FileName = L"";
3476 }
3477 SetDlgItemTextW(hDlg, IDC_LOGFILE, FileName);
3478
3479 if (FileName && *FileName)
3480 {
3481 /*
3482 * The general problem here (and in the shell as well) is that
3483 * GetFileAttributesEx fails for files that are opened without
3484 * shared access. To retrieve file information for those we need
3485 * to use something else: FindFirstFile, on the full file name.
3486 */
3487 Success = GetFileAttributesExW(FileName,
3488 GetFileExInfoStandard,
3489 (LPWIN32_FILE_ATTRIBUTE_DATA)&FileInfo);
3490 if (!Success)
3491 {
3492 HANDLE hFind = FindFirstFileW(FileName, &FileInfo);
3493 Success = (hFind != INVALID_HANDLE_VALUE);
3494 if (Success)
3495 FindClose(hFind);
3496 }
3497 }
3498 else
3499 {
3500 Success = FALSE;
3501 }
3502
3503 /* Starting there, FileName becomes invalid because we are reusing wszBuf */
3504
3505 if (Success)
3506 {
3507 FileSize.u.LowPart = FileInfo.nFileSizeLow;
3508 FileSize.u.HighPart = FileInfo.nFileSizeHigh;
3509 if (FormatFileSizeWithBytes(&FileSize, wszBuf, ARRAYSIZE(wszBuf)))
3510 SetDlgItemTextW(hDlg, IDC_SIZE_LABEL, wszBuf);
3511
3512 LoadStringW(hInst, IDS_NOT_AVAILABLE, szTemp, ARRAYSIZE(szTemp));
3513
3514 if (GetFileTimeString(&FileInfo.ftCreationTime, wszBuf, ARRAYSIZE(wszBuf)))
3515 SetDlgItemTextW(hDlg, IDC_CREATED_LABEL, wszBuf);
3516 else
3517 SetDlgItemTextW(hDlg, IDC_CREATED_LABEL, szTemp);
3518
3519 if (GetFileTimeString(&FileInfo.ftLastWriteTime, wszBuf, ARRAYSIZE(wszBuf)))
3520 SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, wszBuf);
3521 else
3522 SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, szTemp);
3523
3524 if (GetFileTimeString(&FileInfo.ftLastAccessTime, wszBuf, ARRAYSIZE(wszBuf)))
3525 SetDlgItemTextW(hDlg, IDC_ACCESSED_LABEL, wszBuf);
3526 else
3527 SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, szTemp);
3528 }
3529 else
3530 {
3531 LoadStringW(hInst, IDS_NOT_AVAILABLE, szTemp, ARRAYSIZE(szTemp));
3532
3533 SetDlgItemTextW(hDlg, IDC_SIZE_LABEL, szTemp);
3534 SetDlgItemTextW(hDlg, IDC_CREATED_LABEL, szTemp);
3535 SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, szTemp);
3536 SetDlgItemTextW(hDlg, IDC_ACCESSED_LABEL, szTemp);
3537 }
3538
3539 if (EventLog->Permanent)
3540 {
3541 SendDlgItemMessageW(hDlg, IDC_UPDOWN_MAXLOGSIZE, UDM_SETRANGE32, (WPARAM)1, (LPARAM)0x3FFFC0);
3542 SendDlgItemMessageW(hDlg, IDC_UPDOWN_EVENTS_AGE, UDM_SETRANGE, 0, (LPARAM)MAKELONG(365, 1));
3543
3544 SetDlgItemInt(hDlg, IDC_EDIT_MAXLOGSIZE, dwMaxSize, FALSE);
3545 SetDlgItemInt(hDlg, IDC_EDIT_EVENTS_AGE, dwRetention, FALSE);
3546
3547 if (dwRetention == 0)
3548 {
3549 CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_OVERWRITE_AS_NEEDED);
3550 EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, FALSE);
3551 EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, FALSE);
3552 }
3553 else if (dwRetention == INFINITE)
3554 {
3555 CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_NO_OVERWRITE);
3556 EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, FALSE);
3557 EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, FALSE);
3558 }
3559 else
3560 {
3561 CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_OVERWRITE_OLDER_THAN);
3562 EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, TRUE);
3563 EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, TRUE);
3564 }
3565 }
3566 else
3567 {
3568 // TODO: Hide the unused controls! Or, just use another type of property sheet!
3569 }
3570 }
3571
3572 /* Message handler for EventLog Properties dialog */
3573 INT_PTR CALLBACK
3574 EventLogPropProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
3575 {
3576 PEVENTLOG EventLog;
3577
3578 EventLog = (PEVENTLOG)GetWindowLongPtrW(hDlg, DWLP_USER);
3579
3580 switch (uMsg)
3581 {
3582 case WM_INITDIALOG:
3583 {
3584 EventLog = (PEVENTLOG)((LPPROPSHEETPAGE)lParam)->lParam;
3585 SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)EventLog);
3586
3587 InitPropertiesDlg(hDlg, EventLog);
3588
3589 PropSheet_UnChanged(GetParent(hDlg), hDlg);
3590 return (INT_PTR)TRUE;
3591 }
3592
3593 case WM_DESTROY:
3594 return (INT_PTR)TRUE;
3595
3596 case WM_COMMAND:
3597 switch (LOWORD(wParam))
3598 {
3599 case IDOK:
3600 case IDCANCEL:
3601 EndDialog(hDlg, LOWORD(wParam));
3602 return (INT_PTR)TRUE;
3603
3604 case IDC_OVERWRITE_AS_NEEDED:
3605 {
3606 CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_OVERWRITE_AS_NEEDED);
3607 EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, FALSE);
3608 EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, FALSE);
3609 break;
3610 }
3611
3612 case IDC_OVERWRITE_OLDER_THAN:
3613 {
3614 CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_OVERWRITE_OLDER_THAN);
3615 EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, TRUE);
3616 EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, TRUE);
3617 break;
3618 }
3619
3620 case IDC_NO_OVERWRITE:
3621 {
3622 CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_NO_OVERWRITE);
3623 EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, FALSE);
3624 EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, FALSE);
3625 break;
3626 }
3627
3628 case IDHELP:
3629 MessageBoxW(hDlg,
3630 L"Help not implemented yet!",
3631 L"Event Log",
3632 MB_OK | MB_ICONINFORMATION);
3633 return (INT_PTR)TRUE;
3634
3635 default:
3636 break;
3637 }
3638 break;
3639 }
3640
3641 return (INT_PTR)FALSE;
3642 }
3643
3644 INT_PTR
3645 EventLogProperties(HINSTANCE hInstance, HWND hWndParent, PEVENTLOGFILTER EventLogFilter)
3646 {
3647 INT_PTR ret = 0;
3648 PROPSHEETHEADERW psh;
3649 PROPSHEETPAGEW psp[1]; // 2
3650
3651 /*
3652 * Bail out if there is no available filter, or if the filter
3653 * contains more than one log.
3654 */
3655 if (!EventLogFilter)
3656 return 0;
3657
3658 EventLogFilter_AddRef(EventLogFilter);
3659
3660 if (EventLogFilter->NumOfEventLogs > 1 ||
3661 EventLogFilter->EventLogs[0] == NULL)
3662 {
3663 goto Quit;
3664 }
3665
3666 /* Header */
3667 psh.dwSize = sizeof(psh);
3668 psh.dwFlags = PSH_PROPSHEETPAGE /*| PSH_USEICONID */ | PSH_PROPTITLE | PSH_HASHELP /*| PSH_NOCONTEXTHELP */ /*| PSH_USECALLBACK */;
3669 psh.hInstance = hInstance;
3670 psh.hwndParent = hWndParent;
3671 // psh.pszIcon = MAKEINTRESOURCEW(IDI_APPICON); // Disabled because it only sets the small icon; the big icon is a stretched version of the small one.
3672 psh.pszCaption = EventLogFilter->EventLogs[0]->LogName;
3673 psh.nStartPage = 0;
3674 psh.ppsp = psp;
3675 psh.nPages = ARRAYSIZE(psp);
3676 // psh.pfnCallback = PropSheetCallback;
3677
3678 /* Log properties page */
3679 psp[0].dwSize = sizeof(psp[0]);
3680 psp[0].dwFlags = PSP_HASHELP;
3681 psp[0].hInstance = hInstance;
3682 psp[0].pszTemplate = MAKEINTRESOURCEW(IDD_LOGPROPERTIES_GENERAL);
3683 psp[0].pfnDlgProc = EventLogPropProc;
3684 psp[0].lParam = (LPARAM)EventLogFilter->EventLogs[0];
3685
3686 #if 0
3687 /* TODO: Log sources page */
3688 psp[1].dwSize = sizeof(psp[1]);
3689 psp[1].dwFlags = PSP_HASHELP;
3690 psp[1].hInstance = hInstance;
3691 psp[1].pszTemplate = MAKEINTRESOURCEW(IDD_GENERAL_PAGE);
3692 psp[1].pfnDlgProc = GeneralPageWndProc;
3693 psp[0].lParam = (LPARAM)EventLogFilter->EventLogs[0];
3694 #endif
3695
3696 /* Create the property sheet */
3697 ret = PropertySheetW(&psh);
3698
3699 Quit:
3700 EventLogFilter_Release(EventLogFilter);
3701 return ret;
3702 }
3703
3704 /* Message handler for Event Details dialog */
3705 static HWND hWndDetailsCtrl = NULL; // May go into the DWLP_USER
3706 static HWND hWndGrip = NULL;
3707 static INT cxMin, cyMin; // In window coordinates
3708 static INT cxOld, cyOld; // In client coordinates
3709
3710 INT_PTR CALLBACK
3711 EventDetails(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
3712 {
3713 switch (uMsg)
3714 {
3715 case WM_INITDIALOG:
3716 {
3717 LONG_PTR dwStyle;
3718 INT sbVXSize, sbHYSize;
3719 RECT rcWnd, rect;
3720
3721 hWndDetailsCtrl = CreateEventDetailsCtrl(hInst, hDlg, lParam);
3722 if (!hWndDetailsCtrl)
3723 {
3724 EndDialog(hDlg, 0);
3725 return (INT_PTR)TRUE;
3726 }
3727
3728 /* Create a size grip if the dialog has a sizing border */
3729 GetClientRect(hDlg, &rcWnd);
3730 dwStyle = GetWindowLongPtrW(hDlg, GWL_STYLE);
3731 sbVXSize = GetSystemMetrics(SM_CXVSCROLL);
3732 sbHYSize = GetSystemMetrics(SM_CYHSCROLL);
3733 if (dwStyle & WS_THICKFRAME /* == WS_SIZEBOX */)
3734 {
3735 hWndGrip = CreateWindowW(WC_SCROLLBARW,
3736 NULL,
3737 WS_CHILD | WS_VISIBLE | /**/ WS_CLIPSIBLINGS | /**/ SBS_SIZEGRIP | SBS_SIZEBOXBOTTOMRIGHTALIGN,
3738 rcWnd.right - sbVXSize,
3739 rcWnd.bottom - sbHYSize,
3740 sbVXSize, sbHYSize,
3741 hDlg,
3742 NULL,
3743 hInst,
3744 NULL);
3745 }
3746
3747 // SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)hWndDetailsCtrl);
3748
3749 /*
3750 * Compute the minimum window size (in window coordinates) by
3751 * adding the widths/heights of the "Help" and "Close" buttons,
3752 * together with the margins, and add some minimal spacing
3753 * between the buttons.
3754 */
3755 GetWindowRect(hDlg, &rcWnd);
3756 cxMin = cyMin = 0;
3757
3758 GetWindowRect(GetDlgItem(hDlg, IDHELP), &rect);
3759 cxMin += (rect.right - rect.left) + (rect.left - rcWnd.left); // == (rect.right - rcWnd.left);
3760 cyMin += (rect.bottom - rect.top) + (rcWnd.bottom - rect.bottom); // == (rcWnd.bottom - rect.top);
3761
3762 GetWindowRect(GetDlgItem(hDlg, IDOK), &rect);
3763 cxMin += (rect.right - rect.left) + (rcWnd.right - rect.right); // == (rcWnd.right - rect.left);
3764 cyMin += (rect.bottom - rect.top) + (rcWnd.bottom - rect.bottom); // == (rcWnd.bottom - rect.top);
3765
3766 /*
3767 * Convert the window rect from window to client coordinates
3768 * in order to retrieve the sizes of the left and top margins,
3769 * and add some extra space.
3770 */
3771 MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rcWnd, sizeof(RECT)/sizeof(POINT));
3772
3773 cxMin += -2*rcWnd.left; // Minimal spacing between the buttons == 2 * left margin
3774 cyMin += -rcWnd.top + 12; // Add some space on top
3775
3776 GetClientRect(hDlg, &rcWnd);
3777 cxOld = rcWnd.right - rcWnd.left;
3778 cyOld = rcWnd.bottom - rcWnd.top;
3779
3780 /* Show event info on dialog control */
3781 SendMessageW(hWndDetailsCtrl, EVT_DISPLAY, 0, 0);
3782
3783 // SetWindowPos(hWndDetailsCtrl, NULL,
3784 // 0, 0,
3785 // (rcWnd.right - rcWnd.left),
3786 // (rcWnd.bottom - rcWnd.top),
3787 // SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
3788
3789 /*
3790 * Hide the placeholder static control and show the event details
3791 * control instead. Note that the placeholder is here so far just
3792 * to get the dimensions right in the dialog resource editor.
3793 * I plan to remove it and use a custom control with a suitable
3794 * window class for it, that would create the event details control
3795 * instead.
3796 */
3797 ShowWindow(GetDlgItem(hDlg, IDC_STATIC), SW_HIDE);
3798 ShowWindow(hWndDetailsCtrl, SW_SHOW);
3799 return (INT_PTR)TRUE;
3800 }
3801
3802 case WM_DESTROY:
3803 if (IsWindow(hWndDetailsCtrl))
3804 DestroyWindow(hWndDetailsCtrl);
3805 hWndDetailsCtrl = NULL;
3806 return (INT_PTR)TRUE;
3807
3808 case WM_COMMAND:
3809 switch (LOWORD(wParam))
3810 {
3811 case IDOK:
3812 case IDCANCEL:
3813 EndDialog(hDlg, LOWORD(wParam));
3814 return (INT_PTR)TRUE;
3815
3816 case IDHELP:
3817 MessageBoxW(hDlg,
3818 L"Help not implemented yet!",
3819 L"Event Log",
3820 MB_OK | MB_ICONINFORMATION);
3821 return (INT_PTR)TRUE;
3822
3823 default:
3824 break;
3825 }
3826 break;
3827
3828 case WM_SETCURSOR:
3829 if (((HWND)wParam == hWndGrip) && (LOWORD(lParam) == HTCLIENT))
3830 {
3831 SetCursor(LoadCursorW(NULL, IDC_SIZENWSE));
3832 SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, TRUE);
3833 return (INT_PTR)TRUE;
3834 }
3835 break;
3836
3837 case WM_SIZING:
3838 {
3839 /* Forbid resizing the dialog smaller than its minimal size */
3840 PRECT dragRect = (PRECT)lParam;
3841
3842 if ((wParam == WMSZ_LEFT) || (wParam == WMSZ_TOPLEFT) || (wParam == WMSZ_BOTTOMLEFT))
3843 {
3844 if (dragRect->right - dragRect->left < cxMin)
3845 dragRect->left = dragRect->right - cxMin;
3846 }
3847
3848 if ((wParam == WMSZ_RIGHT) || (wParam == WMSZ_TOPRIGHT) || (wParam == WMSZ_BOTTOMRIGHT))
3849 {
3850 if (dragRect->right - dragRect->left < cxMin)
3851 dragRect->right = dragRect->left + cxMin;
3852 }
3853
3854 if ((wParam == WMSZ_TOP) || (wParam == WMSZ_TOPLEFT) || (wParam == WMSZ_TOPRIGHT))
3855 {
3856 if (dragRect->bottom - dragRect->top < cyMin)
3857 dragRect->top = dragRect->bottom - cyMin;
3858 }
3859
3860 if ((wParam == WMSZ_BOTTOM) || (wParam == WMSZ_BOTTOMLEFT) || (wParam == WMSZ_BOTTOMRIGHT))
3861 {
3862 if (dragRect->bottom - dragRect->top < cyMin)
3863 dragRect->bottom = dragRect->top + cyMin;
3864 }
3865
3866 SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, TRUE);
3867 return (INT_PTR)TRUE;
3868 }
3869
3870 case WM_SIZE:
3871 {
3872 INT cx = LOWORD(lParam);
3873 INT cy = HIWORD(lParam);
3874
3875 HDWP hdwp;
3876 HWND hItemWnd;
3877 RECT rect;
3878
3879 hdwp = BeginDeferWindowPos(4);
3880
3881 /* Resize the event details control window */
3882
3883 hItemWnd = hWndDetailsCtrl;
3884 GetWindowRect(hItemWnd, &rect);
3885 MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
3886
3887 if (hdwp)
3888 hdwp = DeferWindowPos(hdwp,
3889 hItemWnd,
3890 HWND_TOP,
3891 0, 0,
3892 (rect.right - rect.left) + (cx - cxOld),
3893 (rect.bottom - rect.top) + (cy - cyOld),
3894 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
3895
3896 /* Move the buttons */
3897
3898 hItemWnd = GetDlgItem(hDlg, IDHELP);
3899 GetWindowRect(hItemWnd, &rect);
3900 MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
3901
3902 if (hdwp)
3903 hdwp = DeferWindowPos(hdwp,
3904 hItemWnd,
3905 HWND_TOP,
3906 rect.left,
3907 rect.top + (cy - cyOld),
3908 0, 0,
3909 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
3910
3911 hItemWnd = GetDlgItem(hDlg, IDOK);
3912 GetWindowRect(hItemWnd, &rect);
3913 MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
3914
3915 if (hdwp)
3916 hdwp = DeferWindowPos(hdwp,
3917 hItemWnd,
3918 HWND_TOP,
3919 rect.left + (cx - cxOld),
3920 rect.top + (cy - cyOld),
3921 0, 0,
3922 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
3923
3924 /* Move the size grip */
3925 if (hWndGrip && hdwp)
3926 {
3927 GetWindowRect(hWndGrip, &rect);
3928 MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
3929
3930 hdwp = DeferWindowPos(hdwp,
3931 hWndGrip,
3932 HWND_TOP,
3933 rect.left + (cx - cxOld),
3934 rect.top + (cy - cyOld),
3935 0, 0,
3936 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
3937 }
3938
3939 if (hdwp)
3940 EndDeferWindowPos(hdwp);
3941
3942 /* Hide the size grip if we are in maximized mode */
3943 if (hWndGrip)
3944 ShowWindow(hWndGrip, (wParam == SIZE_MAXIMIZED) ? SW_HIDE : SW_SHOW);
3945
3946 cxOld = cx;
3947 cyOld = cy;
3948
3949 SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, 0);
3950 return (INT_PTR)TRUE;
3951 }
3952 }
3953
3954 return (INT_PTR)FALSE;
3955 }