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