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