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