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