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