2 * ReactOS Win32 Applications
3 * Copyright (C) 2007 ReactOS Team
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.
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.
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.
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)
26 * Hermes Belusca-Maito
30 #include "evtdetctl.h"
32 #include <sddl.h> // For ConvertSidToStringSidW
36 // #include "resource.h"
38 #define LVM_PROGRESS (WM_APP + 1) // Used by the subclassed ListView
40 static const LPCWSTR szWindowClass
= L
"EVENTVWR"; /* The main window class name */
41 static const WCHAR EVENTLOG_BASE_KEY
[] = L
"SYSTEM\\CurrentControlSet\\Services\\EventLog\\";
43 /* The 3 system logs that should always exist in the user's system */
44 static const LPCWSTR SystemLogs
[] =
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"
59 #define MAX_LOADSTRING 255
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 */
72 INT nVSplitPos
; /* Vertical splitter (1) position */
73 INT nHSplitPos
; /* Horizontal splitter (2) position */
74 BYTE bSplit
= 0; /* Splitter state:
76 * 1: Vertical splitting;
77 * 2: Horizontal splitting.
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 */
88 HTREEITEM htiSystemLogs
= NULL
, htiAppLogs
= NULL
, htiUserLogs
= NULL
;
90 LPWSTR lpComputerName
= NULL
; /* NULL: local user computer (default) */
91 LPWSTR lpszzUserLogsToLoad
= NULL
; /* The list of user logs to load at startup (multi-string) */
92 SIZE_T cbUserLogsSize
= 0;
95 /* Global event records cache for the current active event log filter */
96 DWORD g_TotalRecords
= 0;
97 PEVENTLOGRECORD
* g_RecordPtrs
= NULL
;
99 /* Lists of event logs and event log filters */
100 LIST_ENTRY EventLogList
;
101 LIST_ENTRY EventLogFilterList
;
102 PEVENTLOGFILTER ActiveFilter
= NULL
;
103 BOOL NewestEventsFirst
= TRUE
;
105 HANDLE hEnumEventsThread
= NULL
;
106 HANDLE hStopEnumEvent
= NULL
;
109 * Setting EnumFilter to a valid pointer and raising the hStartEnumEvent event
110 * triggers the event-enumerator thread to perform a new enumeration.
112 PEVENTLOGFILTER EnumFilter
= NULL
;
113 HANDLE hStartStopEnumEvent
= NULL
; // End-of-application event
114 HANDLE hStartEnumEvent
= NULL
; // Command event
116 /* Default Open/Save-As dialog box */
120 /* Forward declarations of functions included in this code module */
123 StartStopEnumEventsThread(IN LPVOID lpParameter
);
125 VOID
OpenUserEventLogFile(IN LPCWSTR lpszFileName
);
127 VOID
BuildLogListAndFilterList(IN LPCWSTR lpComputerName
);
128 VOID
FreeLogList(VOID
);
129 VOID
FreeLogFilterList(VOID
);
131 ATOM
MyRegisterClass(HINSTANCE
);
132 BOOL
InitInstance(HINSTANCE
, int);
133 LRESULT CALLBACK
WndProc(HWND
, UINT
, WPARAM
, LPARAM
);
134 INT_PTR
EventLogProperties(HINSTANCE
, HWND
, PEVENTLOGFILTER
);
135 INT_PTR CALLBACK
EventDetails(HWND
, UINT
, WPARAM
, LPARAM
);
138 /* MAIN FUNCTIONS *************************************************************/
141 ShowWin32Error(IN DWORD dwError
)
143 LPWSTR lpMessageBuffer
;
145 if (dwError
== ERROR_SUCCESS
)
148 if (!FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER
|
149 FORMAT_MESSAGE_FROM_SYSTEM
|
150 FORMAT_MESSAGE_IGNORE_INSERTS
,
154 (LPWSTR
)&lpMessageBuffer
,
160 MessageBoxW(hwndMainWindow
, lpMessageBuffer
, szTitle
, MB_OK
| MB_ICONERROR
);
161 LocalFree(lpMessageBuffer
);
169 INT iUsageLen
= LoadStringW(hInst
, IDS_USAGE
, (LPWSTR
)&lpUsage
, 0);
174 lpBuffer
= HeapAlloc(GetProcessHeap(), 0, (iUsageLen
+ 1) * sizeof(WCHAR
));
178 StringCchCopyNW(lpBuffer
, iUsageLen
+ 1, lpUsage
, iUsageLen
);
179 MessageBoxW(NULL
, lpBuffer
, szTitle
, MB_OK
| MB_ICONINFORMATION
);
181 HeapFree(GetProcessHeap(), 0, lpBuffer
);
185 ProcessCmdLine(IN LPWSTR lpCmdLine
)
187 BOOL Success
= FALSE
;
191 /* Skip any leading whitespace */
194 while (iswspace(*lpCmdLine
))
198 /* No command line means no processing needed */
199 if (!lpCmdLine
|| !*lpCmdLine
)
202 /* Build the arguments vector */
203 argv
= CommandLineToArgvW(lpCmdLine
, &argc
);
207 /* Parse the command line for options (skip the program name) */
208 for (i
= 1; i
< argc
; ++i
)
210 /* Check for new options */
211 if (argv
[i
][0] == L
'-' || argv
[i
][0] == L
'/')
213 if (argv
[i
][1] == L
'?' && argv
[i
][2] == 0)
220 if (argv
[i
][2] == L
':')
222 switch (towupper(argv
[i
][1]))
227 LPWSTR lpFileName
= argv
[i
] + 3;
230 /* Check for a quoted file name */
231 if (*lpFileName
== L
'\"')
233 /* Skip this quote, and the last one too if any */
235 cbFileName
= wcslen(lpFileName
);
236 if (cbFileName
> 0 && lpFileName
[cbFileName
- 1] == L
'\"')
237 lpFileName
[cbFileName
- 1] = UNICODE_NULL
;
240 /* Skip this one if we do not actually have a file name */
244 cbFileName
= (wcslen(lpFileName
) + 1) * sizeof(WCHAR
);
246 /* Reallocate the list of user logs to load */
247 if (lpszzUserLogsToLoad
)
249 lpNewBuffer
= HeapReAlloc(GetProcessHeap(),
252 /* Count the multi-string NULL-terminator */
253 cbUserLogsSize
+ cbFileName
+ sizeof(WCHAR
));
258 lpNewBuffer
= HeapAlloc(GetProcessHeap(),
260 /* Count the multi-string NULL-terminator */
261 cbUserLogsSize
+ cbFileName
+ sizeof(WCHAR
));
266 ShowWin32Error(ERROR_NOT_ENOUGH_MEMORY
);
270 lpszzUserLogsToLoad
= lpNewBuffer
;
271 lpNewBuffer
= (LPWSTR
)((ULONG_PTR
)lpNewBuffer
+ cbUserLogsSize
);
272 cbUserLogsSize
+= cbFileName
;
274 /* Save the file name */
275 StringCbCopyW(lpNewBuffer
, cbFileName
, lpFileName
);
285 /* Unknown argument: display help and bail out */
292 * An argument that does not start with the switch character.
293 * If this is the first argument then this corresponds to the
294 * optional computer name. Otherwise this is a wrong argument.
298 /* Store the computer name */
301 cbLength
= (wcslen(argv
[i
]) + 1) * sizeof(WCHAR
);
302 lpComputerName
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, cbLength
);
305 StringCbCopyW(lpComputerName
, cbLength
, argv
[i
]);
307 /* else, fall back to local computer */
311 /* Invalid syntax: display help and bail out */
321 /* In case of failure, free anything we have allocated */
324 if (lpszzUserLogsToLoad
)
327 HeapFree(GetProcessHeap(), 0, lpszzUserLogsToLoad
);
328 lpszzUserLogsToLoad
= NULL
;
332 HeapFree(GetProcessHeap(), 0, lpComputerName
);
333 lpComputerName
= NULL
;
337 /* Free the arguments vector and exit */
343 wWinMain(HINSTANCE hInstance
,
344 HINSTANCE hPrevInstance
,
349 INITCOMMONCONTROLSEX iccx
;
354 UNREFERENCED_PARAMETER(hPrevInstance
);
355 UNREFERENCED_PARAMETER(lpCmdLine
);
357 /* Whenever any of the common controls are used in your app,
358 * you must call InitCommonControlsEx() to register the classes
359 * for those controls. */
360 iccx
.dwSize
= sizeof(iccx
);
361 iccx
.dwICC
= ICC_LISTVIEW_CLASSES
;
362 InitCommonControlsEx(&iccx
);
364 /* Load the RichEdit DLL to add support for RichEdit controls */
365 hRichEdit
= LoadLibraryW(L
"riched20.dll");
369 msg
.wParam
= (WPARAM
)-1;
371 /* Store the instance handle in the global variable */
374 /* Initialize global strings */
375 LoadStringW(hInstance
, IDS_APP_TITLE
, szTitle
, ARRAYSIZE(szTitle
));
376 LoadStringW(hInstance
, IDS_APP_TITLE_EX
, szTitleTemplate
, ARRAYSIZE(szTitleTemplate
));
377 LoadStringW(hInstance
, IDS_STATUS_MSG
, szStatusBarTemplate
, ARRAYSIZE(szStatusBarTemplate
));
378 LoadStringW(hInstance
, IDS_LOADING_WAIT
, szLoadingWait
, ARRAYSIZE(szLoadingWait
));
379 LoadStringW(hInstance
, IDS_NO_ITEMS
, szEmptyList
, ARRAYSIZE(szEmptyList
));
382 * Process the command-line arguments. Note that we need the full
383 * command-line, with the program file included, and not just what
384 * WinMain() provides in its lpCmdLine parameter.
386 if (!ProcessCmdLine(GetCommandLineW()))
389 if (!MyRegisterClass(hInstance
))
392 /* Perform application initialization */
393 if (!InitInstance(hInstance
, nCmdShow
))
396 hAccelTable
= LoadAcceleratorsW(hInstance
, MAKEINTRESOURCEW(IDA_EVENTVWR
));
398 /* Create the Start/Stop enumerator thread */
399 // Manual-reset event
400 hStartStopEnumEvent
= CreateEventW(NULL
, TRUE
, FALSE
, NULL
);
401 if (!hStartStopEnumEvent
)
405 hStartEnumEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
406 if (!hStartEnumEvent
)
409 hThread
= CreateThread(NULL
, 0,
410 StartStopEnumEventsThread
,
415 /* Retrieve the available event logs on this computer and create filters for them */
416 InitializeListHead(&EventLogList
);
417 InitializeListHead(&EventLogFilterList
);
418 BuildLogListAndFilterList(lpComputerName
);
420 /* Open the user-specified logs if any are present on the command-line */
421 if (lpszzUserLogsToLoad
)
424 for (lpUserLog
= lpszzUserLogsToLoad
; *lpUserLog
; lpUserLog
+= wcslen(lpUserLog
) + 1)
426 OpenUserEventLogFile(lpUserLog
);
429 /* Now cleanup the list of user logs */
431 HeapFree(GetProcessHeap(), 0, lpszzUserLogsToLoad
);
432 lpszzUserLogsToLoad
= NULL
;
435 /* Main message loop */
436 while (GetMessageW(&msg
, NULL
, 0, 0))
438 if (!TranslateAcceleratorW(hwndMainWindow
, hAccelTable
, &msg
))
440 TranslateMessage(&msg
);
441 DispatchMessageW(&msg
);
445 SetEvent(hStartStopEnumEvent
);
446 WaitForSingleObject(hThread
, INFINITE
);
447 CloseHandle(hThread
);
449 /* Free the filters list and the event logs list */
456 CloseHandle(hStartEnumEvent
);
457 if (hStartStopEnumEvent
)
458 CloseHandle(hStartStopEnumEvent
);
462 if (lpszzUserLogsToLoad
)
465 HeapFree(GetProcessHeap(), 0, lpszzUserLogsToLoad
);
466 lpszzUserLogsToLoad
= NULL
;
470 HeapFree(GetProcessHeap(), 0, lpComputerName
);
471 lpComputerName
= NULL
;
473 FreeLibrary(hRichEdit
);
475 return (int)msg
.wParam
;
479 /* GENERIC HELPER FUNCTIONS ***************************************************/
482 EventTimeToSystemTime(IN DWORD EventTime
,
483 OUT PSYSTEMTIME pSystemTime
)
485 SYSTEMTIME st1970
= { 1970, 1, 0, 1, 0, 0, 0, 0 };
493 uUCT
.ft
.dwHighDateTime
= 0;
494 uUCT
.ft
.dwLowDateTime
= EventTime
;
495 SystemTimeToFileTime(&st1970
, &u1970
.ft
);
496 uUCT
.ll
= uUCT
.ll
* 10000000 + u1970
.ll
;
497 FileTimeToLocalFileTime(&uUCT
.ft
, &ftLocal
);
498 FileTimeToSystemTime(&ftLocal
, pSystemTime
);
502 * This function takes in entry a path to a single DLL, in which
503 * the message string of ID dwMessageId has to be searched.
504 * The other parameters are similar to those of the FormatMessageW API.
507 GetMessageStringFromDll(
508 IN LPCWSTR lpMessageDll
,
509 IN DWORD dwFlags
, // If we always use the same flags, just remove this param...
510 IN DWORD dwMessageId
,
512 IN
va_list* Arguments OPTIONAL
)
516 LPWSTR lpMsgBuf
= NULL
;
518 hLibrary
= LoadLibraryExW(lpMessageDll
, NULL
,
519 /* LOAD_LIBRARY_AS_IMAGE_RESOURCE | */ LOAD_LIBRARY_AS_DATAFILE
);
520 if (hLibrary
== NULL
)
523 /* Sanitize dwFlags */
524 dwFlags
&= ~FORMAT_MESSAGE_FROM_STRING
;
525 dwFlags
|= FORMAT_MESSAGE_FROM_HMODULE
;
530 * Retrieve the message string without appending extra newlines.
531 * Wrap in SEH to protect from invalid string parameters.
535 dwLength
= FormatMessageW(dwFlags
,
536 /* FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
537 FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, */
545 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
550 * An exception occurred while calling FormatMessage, this is usually
551 * the sign that a parameter was invalid, either 'lpMsgBuf' was NULL
552 * but we did not pass the flag FORMAT_MESSAGE_ALLOCATE_BUFFER, or the
553 * array pointer 'Arguments' was NULL or did not contain enough elements,
554 * and we did not pass the flag FORMAT_MESSAGE_IGNORE_INSERTS, and the
555 * message string expected too many inserts.
556 * In this last case only, we can call again FormatMessage but ignore
557 * explicitly the inserts. The string that we will return to the user
558 * will not be pre-formatted.
560 if (((dwFlags
& FORMAT_MESSAGE_ALLOCATE_BUFFER
) || lpMsgBuf
) &&
561 !(dwFlags
& FORMAT_MESSAGE_IGNORE_INSERTS
))
563 /* Remove any possible harmful flags and always ignore inserts */
564 dwFlags
&= ~FORMAT_MESSAGE_ARGUMENT_ARRAY
;
565 dwFlags
|= FORMAT_MESSAGE_IGNORE_INSERTS
;
567 /* If this call also throws an exception, we are really dead */
568 dwLength
= FormatMessageW(dwFlags
,
574 NULL
/* Arguments */);
581 FreeLibrary(hLibrary
);
587 ASSERT(lpMsgBuf
== NULL
);
599 * This function takes in entry a comma-separated list of DLLs, in which
600 * the message string of ID dwMessageId has to be searched.
601 * The other parameters are similar to those of the FormatMessageW API.
604 GetMessageStringFromDllList(
605 IN LPCWSTR lpMessageDllList
,
606 IN DWORD dwFlags
, // If we always use the same flags, just remove this param...
607 IN DWORD dwMessageId
,
609 IN
va_list* Arguments OPTIONAL
)
611 BOOL Success
= FALSE
;
613 LPWSTR szMessageDllList
;
615 LPWSTR lpMsgBuf
= NULL
;
617 /* Allocate a local buffer for the DLL list that can be tokenized */
618 // TODO: Optimize that!! Maybe we can cleverly use lpMessageDllList in read/write mode
619 // and cleverly temporarily replace the ';' by UNICODE_NULL, do our job, then reverse the change.
620 cbLength
= (wcslen(lpMessageDllList
) + 1) * sizeof(WCHAR
);
621 szMessageDllList
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, cbLength
);
622 if (!szMessageDllList
)
624 RtlCopyMemory(szMessageDllList
, lpMessageDllList
, cbLength
);
626 /* Loop through the list of message DLLs */
627 szDll
= wcstok(szMessageDllList
, EVENT_DLL_SEPARATOR
);
628 while ((szDll
!= NULL
) && !Success
)
630 // Uses LANG_USER_DEFAULT
631 lpMsgBuf
= GetMessageStringFromDll(szDll
,
638 /* The ID was found and the message was formatted */
644 * The DLL could not be loaded, or the message could not be found,
645 * try the next DLL, if any.
647 szDll
= wcstok(NULL
, EVENT_DLL_SEPARATOR
);
650 HeapFree(GetProcessHeap(), 0, szMessageDllList
);
658 LPWSTR pStartingAddress
; // Pointer to the beginning of a parameter string in pMessage
659 LPWSTR pEndingAddress
; // Pointer to the end of a parameter string in pMessage
660 DWORD pParameterID
; // Parameter identifier found in pMessage
661 LPWSTR pParameter
; // Actual parameter string
662 } param_strings_format_data
;
665 ApplyParameterStringsToMessage(
666 IN LPCWSTR lpMessageDllList
,
667 IN BOOL bMessagePreFormatted
,
668 IN CONST LPCWSTR pMessage
,
669 OUT LPWSTR
* pFinalMessage
)
672 * This code is heavily adapted from the MSDN example:
673 * https://msdn.microsoft.com/en-us/library/windows/desktop/bb427356.aspx
677 DWORD Status
= ERROR_SUCCESS
;
678 DWORD dwParamCount
= 0; // Number of insertion strings found in pMessage
679 size_t cchBuffer
= 0; // Size of the buffer in characters
680 size_t cchParams
= 0; // Number of characters in all the parameter strings
683 param_strings_format_data
* pParamData
= NULL
; // Array of pointers holding information about each parameter string in pMessage
684 LPWSTR pTempMessage
= (LPWSTR
)pMessage
;
685 LPWSTR pTempFinalMessage
= NULL
;
687 *pFinalMessage
= NULL
;
689 /* Determine the number of parameter insertion strings in pMessage */
690 if (bMessagePreFormatted
)
692 while ((pTempMessage
= wcschr(pTempMessage
, L
'%')))
695 if (iswdigit(*pTempMessage
))
698 while (iswdigit(*++pTempMessage
)) ;
704 while ((pTempMessage
= wcsstr(pTempMessage
, L
"%%")))
707 if (iswdigit(*pTempMessage
))
710 while (iswdigit(*++pTempMessage
)) ;
715 /* If there are no parameter insertion strings in pMessage, just return */
716 if (dwParamCount
== 0)
718 // *pFinalMessage = NULL;
722 /* Allocate the array of parameter string format data */
723 pParamData
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, dwParamCount
* sizeof(param_strings_format_data
));
726 Status
= ERROR_OUTOFMEMORY
;
731 * Retrieve each parameter in pMessage and the beginning and end of the
732 * insertion string, as well as the message identifier of the parameter.
734 pTempMessage
= (LPWSTR
)pMessage
;
735 if (bMessagePreFormatted
)
737 while ((pTempMessage
= wcschr(pTempMessage
, L
'%')) && (i
< dwParamCount
))
740 if (iswdigit(*pTempMessage
))
742 pParamData
[i
].pStartingAddress
= pTempMessage
-1;
743 pParamData
[i
].pParameterID
= (DWORD
)_wtol(pTempMessage
);
745 while (iswdigit(*++pTempMessage
)) ;
747 pParamData
[i
].pEndingAddress
= pTempMessage
;
754 while ((pTempMessage
= wcsstr(pTempMessage
, L
"%%")) && (i
< dwParamCount
))
757 if (iswdigit(*pTempMessage
))
759 pParamData
[i
].pStartingAddress
= pTempMessage
-2;
760 pParamData
[i
].pParameterID
= (DWORD
)_wtol(pTempMessage
);
762 while (iswdigit(*++pTempMessage
)) ;
764 pParamData
[i
].pEndingAddress
= pTempMessage
;
770 /* Retrieve each parameter string */
771 for (i
= 0; i
< dwParamCount
; i
++)
773 // pParamData[i].pParameter = GetMessageString(pParamData[i].pParameterID, 0, NULL);
774 pParamData
[i
].pParameter
=
775 GetMessageStringFromDllList(lpMessageDllList
,
776 FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_HMODULE
|
777 FORMAT_MESSAGE_IGNORE_INSERTS
| FORMAT_MESSAGE_MAX_WIDTH_MASK
,
778 pParamData
[i
].pParameterID
,
780 if (!pParamData
[i
].pParameter
)
782 /* Skip the insertion string */
786 cchParams
+= wcslen(pParamData
[i
].pParameter
);
790 * Allocate the final message buffer, the size of which is based on the
791 * length of the original message and the length of each parameter string.
793 cchBuffer
= wcslen(pMessage
) + cchParams
+ 1;
794 *pFinalMessage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, cchBuffer
* sizeof(WCHAR
));
797 Status
= ERROR_OUTOFMEMORY
;
801 pTempFinalMessage
= *pFinalMessage
;
803 /* Build the final message string */
804 pTempMessage
= (LPWSTR
)pMessage
;
805 for (i
= 0; i
< dwParamCount
; i
++)
807 /* Append the segment from pMessage */
808 cch
= pParamData
[i
].pStartingAddress
- pTempMessage
;
809 StringCchCopyNW(pTempFinalMessage
, cchBuffer
, pTempMessage
, cch
);
810 pTempMessage
= pParamData
[i
].pEndingAddress
;
812 pTempFinalMessage
+= cch
;
814 /* Append the parameter string */
815 if (pParamData
[i
].pParameter
)
817 StringCchCopyW(pTempFinalMessage
, cchBuffer
, pParamData
[i
].pParameter
);
818 cch
= wcslen(pParamData
[i
].pParameter
); // pTempFinalMessage
823 * We failed to retrieve the parameter string before, so just
824 * place back the original string placeholder.
826 cch
= pParamData
[i
].pEndingAddress
/* == pTempMessage */ - pParamData
[i
].pStartingAddress
;
827 StringCchCopyNW(pTempFinalMessage
, cchBuffer
, pParamData
[i
].pStartingAddress
, cch
);
828 // cch = wcslen(pTempFinalMessage);
831 pTempFinalMessage
+= cch
;
834 /* Append the last segment from pMessage */
835 StringCchCopyW(pTempFinalMessage
, cchBuffer
, pTempMessage
);
839 // if (Status != ERROR_SUCCESS)
840 // *pFinalMessage = NULL;
844 for (i
= 0; i
< dwParamCount
; i
++)
846 if (pParamData
[i
].pParameter
)
847 LocalFree(pParamData
[i
].pParameter
);
850 HeapFree(GetProcessHeap(), 0, pParamData
);
858 * The following functions were adapted from
859 * shell32!dialogs/filedefext.cpp:``SH_...'' functions.
863 FormatInteger(LONGLONG Num
, LPWSTR pwszResult
, UINT cchResultMax
)
866 WCHAR wszDecimalSep
[8], wszThousandSep
[8];
868 WCHAR wszGrouping
[12];
873 // Print the number in uniform mode
874 swprintf(wszNumber
, L
"%I64u", Num
);
876 // Get system strings for decimal and thousand separators.
877 GetLocaleInfoW(LOCALE_USER_DEFAULT
, LOCALE_SDECIMAL
, wszDecimalSep
, _countof(wszDecimalSep
));
878 GetLocaleInfoW(LOCALE_USER_DEFAULT
, LOCALE_STHOUSAND
, wszThousandSep
, _countof(wszThousandSep
));
880 // Initialize format for printing the number in bytes
881 ZeroMemory(&nf
, sizeof(nf
));
882 nf
.lpDecimalSep
= wszDecimalSep
;
883 nf
.lpThousandSep
= wszThousandSep
;
885 // Get system string for groups separator
886 cchGrouping
= GetLocaleInfoW(LOCALE_USER_DEFAULT
,
889 _countof(wszGrouping
));
891 // Convert grouping specs from string to integer
892 for (i
= 0; i
< cchGrouping
; i
++)
894 WCHAR wch
= wszGrouping
[i
];
896 if (wch
>= L
'0' && wch
<= L
'9')
897 nf
.Grouping
= nf
.Grouping
* 10 + (wch
- L
'0');
898 else if (wch
!= L
';')
902 if ((nf
.Grouping
% 10) == 0)
908 cchResult
= GetNumberFormatW(LOCALE_USER_DEFAULT
,
918 // GetNumberFormatW returns number of characters including UNICODE_NULL
919 return cchResult
- 1;
923 FormatByteSize(LONGLONG cbSize
, LPWSTR pwszResult
, UINT cchResultMax
)
929 /* Write formated bytes count */
930 cchWritten
= FormatInteger(cbSize
, pwszResult
, cchResultMax
);
934 /* Copy " bytes" to buffer */
935 pwszEnd
= pwszResult
+ cchWritten
;
936 cchRemaining
= cchResultMax
- cchWritten
;
937 StringCchCopyExW(pwszEnd
, cchRemaining
, L
" ", &pwszEnd
, &cchRemaining
, 0);
938 cchWritten
= LoadStringW(hInst
, IDS_BYTES_FORMAT
, pwszEnd
, cchRemaining
);
939 cchRemaining
-= cchWritten
;
941 return cchResultMax
- cchRemaining
;
945 FormatFileSizeWithBytes(const PULARGE_INTEGER lpQwSize
, LPWSTR pwszResult
, UINT cchResultMax
)
951 /* Format bytes in KBs, MBs etc */
952 if (StrFormatByteSizeW(lpQwSize
->QuadPart
, pwszResult
, cchResultMax
) == NULL
)
955 /* If there is less bytes than 1KB, we have nothing to do */
956 if (lpQwSize
->QuadPart
< 1024)
959 /* Concatenate " (" */
960 cchWritten
= wcslen(pwszResult
);
961 pwszEnd
= pwszResult
+ cchWritten
;
962 cchRemaining
= cchResultMax
- cchWritten
;
963 StringCchCopyExW(pwszEnd
, cchRemaining
, L
" (", &pwszEnd
, &cchRemaining
, 0);
965 /* Write formated bytes count */
966 cchWritten
= FormatByteSize(lpQwSize
->QuadPart
, pwszEnd
, cchRemaining
);
967 pwszEnd
+= cchWritten
;
968 cchRemaining
-= cchWritten
;
970 /* Copy ")" to the buffer */
971 StringCchCopyW(pwszEnd
, cchRemaining
, L
")");
976 /* Adapted from shell32!dialogs/filedefext.cpp:``CFileDefExt::GetFileTimeString'' */
978 GetFileTimeString(LPFILETIME lpFileTime
, LPWSTR pwszResult
, UINT cchResult
)
983 size_t cchRemaining
= cchResult
;
984 LPWSTR pwszEnd
= pwszResult
;
986 if (!FileTimeToLocalFileTime(lpFileTime
, &ft
) || !FileTimeToSystemTime(&ft
, &st
))
989 cchWritten
= GetDateFormatW(LOCALE_USER_DEFAULT
, DATE_LONGDATE
, &st
, NULL
, pwszEnd
, cchRemaining
);
991 --cchWritten
; // GetDateFormatW returns count with terminating zero
993 // ERR("GetDateFormatW failed\n");
995 cchRemaining
-= cchWritten
;
996 pwszEnd
+= cchWritten
;
998 StringCchCopyExW(pwszEnd
, cchRemaining
, L
", ", &pwszEnd
, &cchRemaining
, 0);
1000 cchWritten
= GetTimeFormatW(LOCALE_USER_DEFAULT
, 0, &st
, NULL
, pwszEnd
, cchRemaining
);
1002 --cchWritten
; // GetTimeFormatW returns count with terminating zero
1004 // ERR("GetTimeFormatW failed\n");
1011 TreeViewAddItem(IN HWND hTreeView
,
1012 IN HTREEITEM hParent
,
1015 IN INT SelectedImage
,
1018 TV_INSERTSTRUCTW Insert
;
1020 ZeroMemory(&Insert
, sizeof(Insert
));
1022 Insert
.item
.mask
= TVIF_TEXT
| TVIF_PARAM
| TVIF_IMAGE
| TVIF_SELECTEDIMAGE
;
1023 Insert
.hInsertAfter
= TVI_LAST
;
1024 Insert
.hParent
= hParent
;
1025 Insert
.item
.pszText
= lpText
;
1026 Insert
.item
.iImage
= Image
;
1027 Insert
.item
.iSelectedImage
= SelectedImage
;
1028 Insert
.item
.lParam
= lParam
;
1030 Insert
.item
.mask
|= TVIF_STATE
;
1031 Insert
.item
.stateMask
= TVIS_OVERLAYMASK
;
1032 Insert
.item
.state
= INDEXTOOVERLAYMASK(1);
1034 return TreeView_InsertItem(hTreeView
, &Insert
);
1038 /* LOG HELPER FUNCTIONS *******************************************************/
1041 AllocEventLog(IN PCWSTR ComputerName OPTIONAL
,
1048 /* Allocate a new event log entry */
1049 EventLog
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*EventLog
));
1053 /* Allocate the computer name string (optional) and copy it */
1056 cchName
= wcslen(ComputerName
) + 1;
1057 EventLog
->ComputerName
= HeapAlloc(GetProcessHeap(), 0, cchName
* sizeof(WCHAR
));
1058 if (EventLog
->ComputerName
)
1059 StringCchCopyW(EventLog
->ComputerName
, cchName
, ComputerName
);
1062 /* Allocate the event log name string and copy it */
1063 cchName
= wcslen(LogName
) + 1;
1064 EventLog
->LogName
= HeapAlloc(GetProcessHeap(), 0, cchName
* sizeof(WCHAR
));
1065 if (!EventLog
->LogName
)
1067 if (EventLog
->ComputerName
)
1068 HeapFree(GetProcessHeap(), 0, EventLog
->ComputerName
);
1069 HeapFree(GetProcessHeap(), 0, EventLog
);
1072 StringCchCopyW(EventLog
->LogName
, cchName
, LogName
);
1074 EventLog
->Permanent
= Permanent
;
1080 EventLog_Free(IN PEVENTLOG EventLog
)
1082 if (EventLog
->LogName
)
1083 HeapFree(GetProcessHeap(), 0, EventLog
->LogName
);
1085 if (EventLog
->ComputerName
)
1086 HeapFree(GetProcessHeap(), 0, EventLog
->ComputerName
);
1088 if (EventLog
->FileName
)
1089 HeapFree(GetProcessHeap(), 0, EventLog
->FileName
);
1091 HeapFree(GetProcessHeap(), 0, EventLog
);
1096 AllocAndCopyMultiStr(IN PCWSTR MultiStr OPTIONAL
)
1104 pStr
= (PWSTR
)MultiStr
;
1105 while (*pStr
) pStr
+= (wcslen(pStr
) + 1);
1106 Length
= MultiStr
- pStr
+ 2;
1108 pStr
= HeapAlloc(GetProcessHeap(), 0, Length
* sizeof(WCHAR
));
1109 // NOTE: If we failed allocating the string, then fall back into no filter!
1111 RtlCopyMemory(pStr
, MultiStr
, Length
* sizeof(WCHAR
));
1117 AllocEventLogFilter(// IN PCWSTR FilterName,
1118 IN BOOL Information
,
1121 IN BOOL AuditSuccess
,
1122 IN BOOL AuditFailure
,
1123 IN PCWSTR Sources OPTIONAL
,
1124 IN PCWSTR Users OPTIONAL
,
1125 IN PCWSTR ComputerNames OPTIONAL
,
1126 IN ULONG NumOfEventLogs
,
1127 IN PEVENTLOG
* EventLogs
)
1129 PEVENTLOGFILTER EventLogFilter
;
1131 /* Allocate a new event log filter entry, big enough to accommodate the list of logs */
1132 EventLogFilter
= HeapAlloc(GetProcessHeap(),
1134 FIELD_OFFSET(EVENTLOGFILTER
, EventLogs
[NumOfEventLogs
]));
1135 if (!EventLogFilter
)
1138 EventLogFilter
->Information
= Information
;
1139 EventLogFilter
->Warning
= Warning
;
1140 EventLogFilter
->Error
= Error
;
1141 EventLogFilter
->AuditSuccess
= AuditSuccess
;
1142 EventLogFilter
->AuditFailure
= AuditFailure
;
1144 /* Allocate and copy the sources, users, and computers multi-strings */
1145 EventLogFilter
->Sources
= AllocAndCopyMultiStr(Sources
);
1146 EventLogFilter
->Users
= AllocAndCopyMultiStr(Users
);
1147 EventLogFilter
->ComputerNames
= AllocAndCopyMultiStr(ComputerNames
);
1149 /* Copy the list of event logs */
1150 EventLogFilter
->NumOfEventLogs
= NumOfEventLogs
;
1151 RtlCopyMemory(EventLogFilter
->EventLogs
, EventLogs
, NumOfEventLogs
* sizeof(PEVENTLOG
));
1153 /* Initialize the filter reference count */
1154 EventLogFilter
->ReferenceCount
= 1;
1156 return EventLogFilter
;
1160 EventLogFilter_Free(IN PEVENTLOGFILTER EventLogFilter
)
1162 if (EventLogFilter
->Sources
)
1163 HeapFree(GetProcessHeap(), 0, EventLogFilter
->Sources
);
1165 if (EventLogFilter
->Users
)
1166 HeapFree(GetProcessHeap(), 0, EventLogFilter
->Users
);
1168 if (EventLogFilter
->ComputerNames
)
1169 HeapFree(GetProcessHeap(), 0, EventLogFilter
->ComputerNames
);
1171 HeapFree(GetProcessHeap(), 0, EventLogFilter
);
1174 LONG
EventLogFilter_AddRef(IN PEVENTLOGFILTER EventLogFilter
)
1176 ASSERT(EventLogFilter
);
1177 return InterlockedIncrement(&EventLogFilter
->ReferenceCount
);
1180 LONG
EventLogFilter_Release(IN PEVENTLOGFILTER EventLogFilter
)
1184 ASSERT(EventLogFilter
);
1186 /* When the reference count reaches zero, delete the filter */
1187 RefCount
= InterlockedDecrement(&EventLogFilter
->ReferenceCount
);
1190 /* Remove the filter from the list */
1191 /** RemoveEntryList(&EventLogFilter->ListEntry); **/
1192 EventLogFilter_Free(EventLogFilter
);
1205 c
= s
+ wcslen(s
) - 1;
1206 while (c
>= s
&& iswspace(*c
))
1213 GetEventMessageFileDLL(IN LPCWSTR lpLogName
,
1214 IN LPCWSTR SourceName
,
1215 IN LPCWSTR EntryName
,
1216 OUT PWCHAR lpModuleName
) // TODO: Add IN DWORD BufLen
1218 BOOL Success
= FALSE
;
1221 WCHAR szModuleName
[MAX_PATH
];
1222 WCHAR szKeyName
[MAX_PATH
];
1223 HKEY hLogKey
= NULL
;
1224 HKEY hSourceKey
= NULL
;
1226 StringCbCopyW(szKeyName
, sizeof(szKeyName
), EVENTLOG_BASE_KEY
);
1227 StringCbCatW(szKeyName
, sizeof(szKeyName
), lpLogName
);
1229 Result
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
,
1234 if (Result
!= ERROR_SUCCESS
)
1237 Result
= RegOpenKeyExW(hLogKey
,
1242 if (Result
!= ERROR_SUCCESS
)
1244 RegCloseKey(hLogKey
);
1248 dwSize
= sizeof(szModuleName
);
1249 Result
= RegQueryValueExW(hSourceKey
,
1253 (LPBYTE
)szModuleName
,
1255 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_EXPAND_SZ
&& Type
!= REG_SZ
))
1257 szModuleName
[0] = UNICODE_NULL
;
1261 /* NULL-terminate the string and expand it */
1262 szModuleName
[dwSize
/ sizeof(WCHAR
) - 1] = UNICODE_NULL
;
1263 ExpandEnvironmentStringsW(szModuleName
, lpModuleName
, ARRAYSIZE(szModuleName
));
1267 RegCloseKey(hSourceKey
);
1268 RegCloseKey(hLogKey
);
1274 GetEventCategory(IN LPCWSTR KeyName
,
1275 IN LPCWSTR SourceName
,
1276 IN PEVENTLOGRECORD pevlr
,
1277 OUT PWCHAR CategoryName
) // TODO: Add IN DWORD BufLen
1279 BOOL Success
= FALSE
;
1280 WCHAR szMessageDLL
[MAX_PATH
];
1281 LPWSTR lpMsgBuf
= NULL
;
1283 if (!GetEventMessageFileDLL(KeyName
, SourceName
, EVENT_CATEGORY_MESSAGE_FILE
, szMessageDLL
))
1286 /* Retrieve the message string without appending extra newlines */
1288 GetMessageStringFromDllList(szMessageDLL
,
1289 FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_HMODULE
|
1290 FORMAT_MESSAGE_IGNORE_INSERTS
| FORMAT_MESSAGE_MAX_WIDTH_MASK
,
1291 pevlr
->EventCategory
,
1292 EVENT_MESSAGE_FILE_BUFFER
,
1296 /* Trim the string */
1297 TrimNulls(lpMsgBuf
);
1299 /* Copy the category name */
1300 StringCchCopyW(CategoryName
, MAX_PATH
, lpMsgBuf
);
1302 /* Free the buffer allocated by FormatMessage */
1303 LocalFree(lpMsgBuf
);
1305 /* The ID was found and the message was formatted */
1312 if (pevlr
->EventCategory
!= 0)
1314 StringCchPrintfW(CategoryName
, MAX_PATH
, L
"(%lu)", pevlr
->EventCategory
);
1323 BOOL
// NOTE: Used by evtdetctl.c
1324 GetEventMessage(IN LPCWSTR KeyName
,
1325 IN LPCWSTR SourceName
,
1326 IN PEVENTLOGRECORD pevlr
,
1327 OUT PWCHAR EventText
) // TODO: Add IN DWORD BufLen
1329 BOOL Success
= FALSE
;
1332 WCHAR SourceModuleName
[1024];
1333 WCHAR ParameterModuleName
[1024];
1334 BOOL IsParamModNameCached
= FALSE
;
1335 LPWSTR lpMsgBuf
= NULL
;
1336 LPWSTR szStringArray
, szMessage
;
1337 LPWSTR
*szArguments
;
1339 /* Get the event string array */
1340 szStringArray
= (LPWSTR
)((LPBYTE
)pevlr
+ pevlr
->StringOffset
);
1342 /* NOTE: GetEventMessageFileDLL can return a comma-separated list of DLLs */
1343 if (!GetEventMessageFileDLL(KeyName
, SourceName
, EVENT_MESSAGE_FILE
, SourceModuleName
))
1346 /* Allocate space for insertion strings */
1347 szArguments
= HeapAlloc(GetProcessHeap(), 0, pevlr
->NumStrings
* sizeof(LPVOID
));
1351 if (!IsParamModNameCached
)
1353 /* Now that the parameter file list is loaded, no need to reload it at the next run! */
1354 IsParamModNameCached
= GetEventMessageFileDLL(KeyName
, SourceName
, EVENT_PARAMETER_MESSAGE_FILE
, ParameterModuleName
);
1355 // FIXME: If the string loading failed the first time, no need to retry it just after???
1358 if (IsParamModNameCached
)
1360 /* Not yet support for reading messages from parameter message DLL */
1363 szMessage
= szStringArray
;
1366 * We do some hackish preformatting of the cached event strings...
1367 * That's because after we pass the string to FormatMessage
1368 * (via GetMessageStringFromDllList) with the FORMAT_MESSAGE_ARGUMENT_ARRAY
1369 * flag, instead of ignoring the insertion parameters and do the formatting
1370 * by ourselves. Therefore, the resulting string should have the parameter
1371 * string placeholders starting with a single '%' instead of a mix of one
1374 /* HACK part 1: Compute the full length of the string array */
1376 for (i
= 0; i
< pevlr
->NumStrings
; i
++)
1378 szMessage
+= wcslen(szMessage
) + 1;
1380 cch
= szMessage
- szStringArray
;
1382 /* HACK part 2: Now do the HACK proper! */
1383 szMessage
= szStringArray
;
1384 for (i
= 0; i
< pevlr
->NumStrings
; i
++)
1386 lpMsgBuf
= szMessage
;
1387 while ((lpMsgBuf
= wcsstr(lpMsgBuf
, L
"%%")))
1389 if (iswdigit(lpMsgBuf
[2]))
1391 RtlMoveMemory(lpMsgBuf
, lpMsgBuf
+1, ((szStringArray
+ cch
) - lpMsgBuf
- 1) * sizeof(WCHAR
));
1395 szArguments
[i
] = szMessage
;
1396 szMessage
+= wcslen(szMessage
) + 1;
1399 /* Retrieve the message string without appending extra newlines */
1401 GetMessageStringFromDllList(SourceModuleName
,
1402 FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_HMODULE
|
1403 FORMAT_MESSAGE_ARGUMENT_ARRAY
| FORMAT_MESSAGE_MAX_WIDTH_MASK
,
1406 (va_list*)szArguments
);
1409 /* Trim the string */
1410 TrimNulls(lpMsgBuf
);
1413 Success
= (ApplyParameterStringsToMessage(ParameterModuleName
,
1416 &szMessage
) == ERROR_SUCCESS
);
1417 if (Success
&& szMessage
)
1419 /* Free the buffer allocated by FormatMessage */
1420 LocalFree(lpMsgBuf
);
1421 lpMsgBuf
= szMessage
;
1424 /* Copy the event text */
1425 StringCchCopyW(EventText
, EVENT_MESSAGE_EVENTTEXT_BUFFER
, lpMsgBuf
);
1427 /* Free the buffer allocated by FormatMessage */
1428 LocalFree(lpMsgBuf
);
1431 HeapFree(GetProcessHeap(), 0, szArguments
);
1436 /* Get a read-only pointer to the "event-not-found" string */
1437 lpMsgBuf
= HeapAlloc(GetProcessHeap(), 0, EVENT_MESSAGE_EVENTTEXT_BUFFER
* sizeof(WCHAR
));
1438 LoadStringW(hInst
, IDS_EVENTSTRINGIDNOTFOUND
, lpMsgBuf
, EVENT_MESSAGE_EVENTTEXT_BUFFER
);
1439 StringCchPrintfW(EventText
, EVENT_MESSAGE_EVENTTEXT_BUFFER
, lpMsgBuf
, (pevlr
->EventID
& 0xFFFF), SourceName
);
1441 /* Append the strings */
1442 szMessage
= szStringArray
;
1443 for (i
= 0; i
< pevlr
->NumStrings
; i
++)
1445 StringCchCatW(EventText
, EVENT_MESSAGE_EVENTTEXT_BUFFER
, szMessage
);
1446 StringCchCatW(EventText
, EVENT_MESSAGE_EVENTTEXT_BUFFER
, L
"\n");
1447 szMessage
+= wcslen(szMessage
) + 1;
1455 GetEventType(IN WORD dwEventType
,
1456 OUT PWCHAR eventTypeText
) // TODO: Add IN DWORD BufLen
1458 switch (dwEventType
)
1460 case EVENTLOG_ERROR_TYPE
:
1461 LoadStringW(hInst
, IDS_EVENTLOG_ERROR_TYPE
, eventTypeText
, MAX_LOADSTRING
);
1463 case EVENTLOG_WARNING_TYPE
:
1464 LoadStringW(hInst
, IDS_EVENTLOG_WARNING_TYPE
, eventTypeText
, MAX_LOADSTRING
);
1466 case EVENTLOG_INFORMATION_TYPE
:
1467 LoadStringW(hInst
, IDS_EVENTLOG_INFORMATION_TYPE
, eventTypeText
, MAX_LOADSTRING
);
1469 case EVENTLOG_SUCCESS
:
1470 LoadStringW(hInst
, IDS_EVENTLOG_SUCCESS
, eventTypeText
, MAX_LOADSTRING
);
1472 case EVENTLOG_AUDIT_SUCCESS
:
1473 LoadStringW(hInst
, IDS_EVENTLOG_AUDIT_SUCCESS
, eventTypeText
, MAX_LOADSTRING
);
1475 case EVENTLOG_AUDIT_FAILURE
:
1476 LoadStringW(hInst
, IDS_EVENTLOG_AUDIT_FAILURE
, eventTypeText
, MAX_LOADSTRING
);
1479 LoadStringW(hInst
, IDS_EVENTLOG_UNKNOWN_TYPE
, eventTypeText
, MAX_LOADSTRING
);
1485 GetEventUserName(IN PEVENTLOGRECORD pelr
,
1486 IN OUT PSID
*pLastSid
,
1487 OUT PWCHAR pszUser
) // TODO: Add IN DWORD BufLen
1492 WCHAR szDomain
[1024];
1494 DWORD cchName
= ARRAYSIZE(szName
);
1495 DWORD cchDomain
= ARRAYSIZE(szDomain
);
1496 BOOL Success
= FALSE
;
1498 /* Point to the SID */
1499 pCurrentSid
= (PSID
)((LPBYTE
)pelr
+ pelr
->UserSidOffset
);
1501 if (!IsValidSid(pCurrentSid
))
1506 else if (*pLastSid
&& EqualSid(*pLastSid
, pCurrentSid
))
1512 if (pelr
->UserSidLength
> 0)
1515 * Try to retrieve the user account name and domain name corresponding
1516 * to the SID. If it cannot be retrieved, try to convert the SID to a
1517 * string-form. It should not be bigger than the user-provided buffer
1518 * 'pszUser', otherwise we return an error.
1520 if (LookupAccountSidW(NULL
, // FIXME: Use computer name? From the particular event?
1528 StringCchCopyW(pszUser
, MAX_PATH
, szName
);
1531 else if (ConvertSidToStringSidW(pCurrentSid
, &StringSid
))
1533 /* Copy the string only if the user-provided buffer is big enough */
1534 if (wcslen(StringSid
) + 1 <= MAX_PATH
) // + 1 for NULL-terminator
1536 StringCchCopyW(pszUser
, MAX_PATH
, StringSid
);
1541 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1545 /* Free the allocated buffer */
1546 LocalFree(StringSid
);
1550 *pLastSid
= Success
? pCurrentSid
: NULL
;
1556 static VOID
FreeRecords(VOID
)
1563 for (iIndex
= 0; iIndex
< g_TotalRecords
; iIndex
++)
1565 if (g_RecordPtrs
[iIndex
])
1566 HeapFree(GetProcessHeap(), 0, g_RecordPtrs
[iIndex
]);
1568 HeapFree(GetProcessHeap(), 0, g_RecordPtrs
);
1569 g_RecordPtrs
= NULL
;
1574 FilterByType(IN PEVENTLOGFILTER EventLogFilter
,
1575 IN PEVENTLOGRECORD pevlr
)
1577 if ((pevlr
->EventType
== EVENTLOG_SUCCESS
&& !EventLogFilter
->Information
) ||
1578 (pevlr
->EventType
== EVENTLOG_INFORMATION_TYPE
&& !EventLogFilter
->Information
) ||
1579 (pevlr
->EventType
== EVENTLOG_WARNING_TYPE
&& !EventLogFilter
->Warning
) ||
1580 (pevlr
->EventType
== EVENTLOG_ERROR_TYPE
&& !EventLogFilter
->Error
) ||
1581 (pevlr
->EventType
== EVENTLOG_AUDIT_SUCCESS
&& !EventLogFilter
->AuditSuccess
) ||
1582 (pevlr
->EventType
== EVENTLOG_AUDIT_FAILURE
&& !EventLogFilter
->AuditFailure
))
1590 FilterByString(IN PCWSTR FilterString
, // This is a multi-string
1595 /* The filter string is NULL so it does not filter anything */
1600 * If the filter string filters for an empty string AND the source string
1601 * is an empty string, we have a match (particular case of the last one).
1603 if (!*FilterString
&& !*String
)
1606 // if (*FilterString || *String)
1609 * If the filter string is empty BUT the source string is not empty,
1610 * OR vice-versa, we cannot have a match.
1612 if ( (!*FilterString
&& *String
) || (*FilterString
&& !*String
) )
1616 * If the filter string filters for at least a non-empty string,
1617 * browse it and search for a string that matches the source string.
1619 // else if (*FilterString && *String)
1621 pStr
= FilterString
;
1624 if (wcsicmp(pStr
, String
) == 0)
1626 /* We have a match, break the loop */
1630 pStr
+= (wcslen(pStr
) + 1);
1632 if (!*pStr
) // && *String
1634 /* We do not have a match */
1639 /* We have a match */
1644 * The events enumerator thread.
1647 EnumEventsThread(IN LPVOID lpParameter
)
1649 PEVENTLOGFILTER EventLogFilter
= (PEVENTLOGFILTER
)lpParameter
;
1654 PEVENTLOGRECORD pEvlr
= NULL
;
1657 DWORD dwWanted
, dwRead
, dwNeeded
, dwStatus
= ERROR_SUCCESS
;
1658 DWORD dwTotalRecords
= 0, dwCurrentRecord
= 0;
1659 DWORD dwFlags
, dwMaxLength
;
1660 size_t cchRemaining
;
1661 LPWSTR lpszSourceName
;
1662 LPWSTR lpszComputerName
;
1663 BOOL bResult
= TRUE
; /* Read succeeded */
1664 HANDLE hProcessHeap
= GetProcessHeap();
1665 PSID pLastSid
= NULL
;
1667 UINT uStep
= 0, uStepAt
= 0, uPos
= 0;
1669 WCHAR szWindowTitle
[MAX_PATH
];
1670 WCHAR szStatusText
[MAX_PATH
];
1671 WCHAR szLocalDate
[MAX_PATH
];
1672 WCHAR szLocalTime
[MAX_PATH
];
1673 WCHAR szEventID
[MAX_PATH
];
1674 WCHAR szEventTypeText
[MAX_LOADSTRING
];
1675 WCHAR szCategoryID
[MAX_PATH
];
1676 WCHAR szUsername
[MAX_PATH
];
1677 WCHAR szNoUsername
[MAX_PATH
];
1678 WCHAR szCategory
[MAX_PATH
];
1679 WCHAR szNoCategory
[MAX_PATH
];
1680 PWCHAR lpTitleTemplateEnd
;
1683 LVITEMW lviEventItem
;
1685 /* Save the current event log filter globally */
1686 EventLogFilter_AddRef(EventLogFilter
);
1687 ActiveFilter
= EventLogFilter
;
1691 EventLog
= EventLogFilter
->EventLogs
[0];
1693 // FIXME: Use something else instead of EventLog->LogName !!
1696 * Use a different formatting, whether the event log filter holds
1697 * only one log, or many logs (the latter case is WIP TODO!)
1699 if (EventLogFilter
->NumOfEventLogs
<= 1)
1701 StringCchPrintfExW(szWindowTitle
,
1702 ARRAYSIZE(szWindowTitle
),
1703 &lpTitleTemplateEnd
,
1706 szTitleTemplate
, szTitle
, EventLog
->LogName
); /* i = number of characters written */
1707 dwMaxLength
= (DWORD
)cchRemaining
;
1708 if (!EventLog
->ComputerName
)
1709 GetComputerNameW(lpTitleTemplateEnd
, &dwMaxLength
);
1711 StringCchCopyW(lpTitleTemplateEnd
, dwMaxLength
, EventLog
->ComputerName
);
1713 StringCbPrintfW(szStatusText
,
1714 sizeof(szStatusText
),
1715 szStatusBarTemplate
,
1722 // TODO: Use a different title & implement filtering for multi-log filters !!
1723 // (EventLogFilter->NumOfEventLogs > 1)
1724 MessageBoxW(hwndMainWindow
,
1725 L
"Many-logs filtering is not implemented yet!!",
1727 MB_OK
| MB_ICONINFORMATION
);
1730 /* Set the window title */
1731 SetWindowTextW(hwndMainWindow
, szWindowTitle
);
1733 /* Update the status bar */
1734 StatusBar_SetText(hwndStatus
, 0, szStatusText
);
1737 /* Disable list view redraw */
1738 SendMessageW(hwndListView
, WM_SETREDRAW
, FALSE
, 0);
1740 /* Clear the list view and free the cached records */
1741 ListView_DeleteAllItems(hwndListView
);
1744 SendMessageW(hwndListView
, LVM_PROGRESS
, 0, TRUE
);
1745 ProgressBar_SetRange(hwndStatusProgress
, 0);
1746 StatusBar_SetText(hwndStatus
, 0, NULL
);
1747 ShowWindow(hwndStatusProgress
, SW_SHOW
);
1749 /* Do a loop over the logs enumerated in the filter */
1750 // FIXME: For now we only support 1 event log per filter!
1752 // for (LogIndex = 0; LogIndex < EventLogFilter->NumOfEventLogs; ++LogIndex)
1755 EventLog
= EventLogFilter
->EventLogs
[LogIndex
];
1757 /* Open the event log */
1758 if (EventLog
->Permanent
)
1759 hEventLog
= OpenEventLogW(EventLog
->ComputerName
, EventLog
->LogName
);
1761 hEventLog
= OpenBackupEventLogW(EventLog
->ComputerName
, EventLog
->LogName
); // FileName
1763 if (hEventLog
== NULL
)
1765 ShowWin32Error(GetLastError());
1769 // GetOldestEventLogRecord(hEventLog, &dwThisRecord);
1771 /* Get the total number of event log records */
1772 GetNumberOfEventLogRecords(hEventLog
, &dwTotalRecords
);
1774 if (dwTotalRecords
> 0)
1776 EnableMenuItem(hMainMenu
, IDM_CLEAR_EVENTS
, MF_BYCOMMAND
| MF_ENABLED
);
1777 EnableMenuItem(hMainMenu
, IDM_SAVE_EVENTLOG
, MF_BYCOMMAND
| MF_ENABLED
);
1781 EnableMenuItem(hMainMenu
, IDM_CLEAR_EVENTS
, MF_BYCOMMAND
| MF_GRAYED
);
1782 EnableMenuItem(hMainMenu
, IDM_SAVE_EVENTLOG
, MF_BYCOMMAND
| MF_GRAYED
);
1785 /* Set up the event records cache */
1786 g_RecordPtrs
= HeapAlloc(hProcessHeap
, HEAP_ZERO_MEMORY
, dwTotalRecords
* sizeof(*g_RecordPtrs
));
1789 // ShowWin32Error(GetLastError());
1792 g_TotalRecords
= dwTotalRecords
;
1794 if (WaitForSingleObject(hStopEnumEvent
, 0) == WAIT_OBJECT_0
)
1797 LoadStringW(hInst
, IDS_NOT_AVAILABLE
, szNoUsername
, ARRAYSIZE(szNoUsername
));
1798 LoadStringW(hInst
, IDS_NONE
, szNoCategory
, ARRAYSIZE(szNoCategory
));
1800 ProgressBar_SetRange(hwndStatusProgress
, MAKELPARAM(0, 100));
1801 uStepAt
= (dwTotalRecords
/ 100) + 1;
1803 dwFlags
= EVENTLOG_SEQUENTIAL_READ
| (NewestEventsFirst
? EVENTLOG_FORWARDS_READ
: EVENTLOG_BACKWARDS_READ
);
1805 /* 0x7ffff is the maximum buffer size ReadEventLog will accept */
1807 pEvlr
= HeapAlloc(hProcessHeap
, 0, dwWanted
);
1812 while (dwStatus
== ERROR_SUCCESS
)
1814 bResult
= ReadEventLogW(hEventLog
, dwFlags
, 0, pEvlr
, dwWanted
, &dwRead
, &dwNeeded
);
1815 dwStatus
= GetLastError();
1817 if (!bResult
&& dwStatus
== ERROR_INSUFFICIENT_BUFFER
)
1819 pEvlr
= HeapReAlloc(hProcessHeap
, 0, pEvlr
, dwNeeded
);
1820 dwWanted
= dwNeeded
;
1825 bResult
= ReadEventLogW(hEventLog
, dwFlags
, 0, pEvlr
, dwNeeded
, &dwRead
, &dwNeeded
);
1832 /* exit on other errors (ERROR_HANDLE_EOF) */
1836 pEvlrBuffer
= (LPBYTE
)pEvlr
;
1837 pEvlrEnd
= pEvlrBuffer
+ dwRead
;
1839 while (pEvlrBuffer
< pEvlrEnd
)
1841 PEVENTLOGRECORD pEvlrTmp
= (PEVENTLOGRECORD
)pEvlrBuffer
;
1842 PWSTR lpszUsername
, lpszCategoryName
;
1843 g_RecordPtrs
[dwCurrentRecord
] = NULL
;
1845 // ProgressBar_StepIt(hwndStatusProgress);
1847 if (uStep
% uStepAt
== 0)
1850 ProgressBar_SetPos(hwndStatusProgress
, uPos
);
1853 if (WaitForSingleObject(hStopEnumEvent
, 0) == WAIT_OBJECT_0
)
1856 /* Filter by event type */
1857 if (!FilterByType(EventLogFilter
, pEvlrTmp
))
1860 /* Get the event source name and filter it */
1861 lpszSourceName
= (LPWSTR
)(pEvlrBuffer
+ sizeof(EVENTLOGRECORD
));
1862 if (!FilterByString(EventLogFilter
->Sources
, lpszSourceName
))
1865 /* Get the computer name and filter it */
1866 lpszComputerName
= (LPWSTR
)(pEvlrBuffer
+ sizeof(EVENTLOGRECORD
) + (wcslen(lpszSourceName
) + 1) * sizeof(WCHAR
));
1867 if (!FilterByString(EventLogFilter
->ComputerNames
, lpszComputerName
))
1870 /* Compute the event time */
1871 EventTimeToSystemTime(pEvlrTmp
->TimeWritten
, &time
);
1872 GetDateFormatW(LOCALE_USER_DEFAULT
, DATE_SHORTDATE
, &time
, NULL
, szLocalDate
, ARRAYSIZE(szLocalDate
));
1873 GetTimeFormatW(LOCALE_USER_DEFAULT
, 0, &time
, NULL
, szLocalTime
, ARRAYSIZE(szLocalTime
));
1875 /* Get the username that generated the event, and filter it */
1876 lpszUsername
= GetEventUserName(pEvlrTmp
, &pLastSid
, szUsername
) ? szUsername
: szNoUsername
;
1878 if (!FilterByString(EventLogFilter
->Users
, lpszUsername
))
1881 // TODO: Filter by event ID and category
1882 GetEventType(pEvlrTmp
->EventType
, szEventTypeText
);
1884 lpszCategoryName
= GetEventCategory(EventLog
->LogName
, lpszSourceName
, pEvlrTmp
, szCategory
) ? szCategory
: szNoCategory
;
1886 StringCbPrintfW(szEventID
, sizeof(szEventID
), L
"%u", (pEvlrTmp
->EventID
& 0xFFFF));
1887 StringCbPrintfW(szCategoryID
, sizeof(szCategoryID
), L
"%u", pEvlrTmp
->EventCategory
);
1889 g_RecordPtrs
[dwCurrentRecord
] = HeapAlloc(hProcessHeap
, 0, pEvlrTmp
->Length
);
1890 RtlCopyMemory(g_RecordPtrs
[dwCurrentRecord
], pEvlrTmp
, pEvlrTmp
->Length
);
1892 lviEventItem
.mask
= LVIF_IMAGE
| LVIF_TEXT
| LVIF_PARAM
;
1893 lviEventItem
.iItem
= 0;
1894 lviEventItem
.iSubItem
= 0;
1895 lviEventItem
.lParam
= (LPARAM
)g_RecordPtrs
[dwCurrentRecord
];
1896 lviEventItem
.pszText
= szEventTypeText
;
1898 switch (pEvlrTmp
->EventType
)
1900 case EVENTLOG_SUCCESS
:
1901 case EVENTLOG_INFORMATION_TYPE
:
1902 lviEventItem
.iImage
= 0;
1905 case EVENTLOG_WARNING_TYPE
:
1906 lviEventItem
.iImage
= 1;
1909 case EVENTLOG_ERROR_TYPE
:
1910 lviEventItem
.iImage
= 2;
1913 case EVENTLOG_AUDIT_SUCCESS
:
1914 lviEventItem
.iImage
= 3;
1917 case EVENTLOG_AUDIT_FAILURE
:
1918 lviEventItem
.iImage
= 4;
1922 lviEventItem
.iItem
= ListView_InsertItem(hwndListView
, &lviEventItem
);
1924 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 1, szLocalDate
);
1925 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 2, szLocalTime
);
1926 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 3, lpszSourceName
);
1927 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 4, lpszCategoryName
);
1928 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 5, szEventID
);
1929 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 6, lpszUsername
);
1930 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 7, lpszComputerName
);
1933 pEvlrBuffer
+= pEvlrTmp
->Length
;
1941 HeapFree(hProcessHeap
, 0, pEvlr
);
1943 /* Close the event log */
1944 CloseEventLog(hEventLog
);
1946 } // end-for (LogIndex)
1948 /* All events loaded */
1952 ShowWindow(hwndStatusProgress
, SW_HIDE
);
1953 SendMessageW(hwndListView
, LVM_PROGRESS
, 0, FALSE
);
1955 // FIXME: Use something else instead of EventLog->LogName !!
1958 * Use a different formatting, whether the event log filter holds
1959 * only one log, or many logs (the latter case is WIP TODO!)
1961 if (EventLogFilter
->NumOfEventLogs
<= 1)
1963 StringCbPrintfW(szStatusText
,
1964 sizeof(szStatusText
),
1965 szStatusBarTemplate
,
1968 ListView_GetItemCount(hwndListView
));
1972 // TODO: Use a different title & implement filtering for multi-log filters !!
1973 // (EventLogFilter->NumOfEventLogs > 1)
1976 /* Update the status bar */
1977 StatusBar_SetText(hwndStatus
, 0, szStatusText
);
1979 /* Resume list view redraw */
1980 SendMessageW(hwndListView
, WM_SETREDRAW
, TRUE
, 0);
1982 EventLogFilter_Release(EventLogFilter
);
1984 CloseHandle(hStopEnumEvent
);
1985 InterlockedExchangePointer((PVOID
*)&hStopEnumEvent
, NULL
);
1991 * The purpose of this thread is to serialize the creation of the events
1992 * enumeration thread, since the Event Log Viewer currently only supports
1993 * one view, one event list, one enumeration.
1996 StartStopEnumEventsThread(IN LPVOID lpParameter
)
1998 HANDLE WaitHandles
[2];
2001 WaitHandles
[0] = hStartStopEnumEvent
; // End-of-application event
2002 WaitHandles
[1] = hStartEnumEvent
; // Command event
2006 WaitResult
= WaitForMultipleObjects(ARRAYSIZE(WaitHandles
),
2012 case WAIT_OBJECT_0
+ 0:
2014 /* End-of-application event signaled, quit this thread */
2016 /* Stop the previous enumeration */
2017 if (hEnumEventsThread
)
2021 SetEvent(hStopEnumEvent
);
2022 WaitForSingleObject(hEnumEventsThread
, INFINITE
);
2023 // NOTE: The following is done by the enumeration thread just before terminating.
2024 // hStopEnumEvent = NULL;
2027 CloseHandle(hEnumEventsThread
);
2028 hEnumEventsThread
= NULL
;
2031 /* Clear the list view and free the cached records */
2032 ListView_DeleteAllItems(hwndListView
);
2035 /* Reset the active filter */
2036 ActiveFilter
= NULL
;
2041 case WAIT_OBJECT_0
+ 1:
2043 /* Restart a new enumeration if needed */
2044 PEVENTLOGFILTER EventLogFilter
;
2046 /* Stop the previous enumeration */
2047 if (hEnumEventsThread
)
2051 SetEvent(hStopEnumEvent
);
2052 WaitForSingleObject(hEnumEventsThread
, INFINITE
);
2053 // NOTE: The following is done by the enumeration thread just before terminating.
2054 // hStopEnumEvent = NULL;
2057 CloseHandle(hEnumEventsThread
);
2058 hEnumEventsThread
= NULL
;
2061 /* Clear the list view and free the cached records */
2062 ListView_DeleteAllItems(hwndListView
);
2065 /* Reset the active filter */
2066 ActiveFilter
= NULL
;
2068 EventLogFilter
= InterlockedExchangePointer((PVOID
*)&EnumFilter
, NULL
);
2069 if (!EventLogFilter
)
2072 // Manual-reset event
2073 hStopEnumEvent
= CreateEventW(NULL
, TRUE
, FALSE
, NULL
);
2074 if (!hStopEnumEvent
)
2077 hEnumEventsThread
= CreateThread(NULL
,
2080 (LPVOID
)EventLogFilter
,
2083 if (!hEnumEventsThread
)
2085 CloseHandle(hStopEnumEvent
);
2086 hStopEnumEvent
= NULL
;
2089 // CloseHandle(hEnumEventsThread);
2090 ResumeThread(hEnumEventsThread
);
2097 /* Unknown command, must never go there! */
2098 return GetLastError();
2107 EnumEvents(IN PEVENTLOGFILTER EventLogFilter
)
2109 /* Signal the enumerator thread we want to enumerate events */
2110 InterlockedExchangePointer((PVOID
*)&EnumFilter
, EventLogFilter
);
2111 SetEvent(hStartEnumEvent
);
2117 GetSelectedFilter(OUT HTREEITEM
* phti OPTIONAL
)
2125 /* Get index of selected item */
2126 hti
= TreeView_GetSelection(hwndTreeView
);
2128 return NULL
; // No filter
2130 tvItemEx
.mask
= TVIF_PARAM
;
2131 tvItemEx
.hItem
= hti
;
2133 TreeView_GetItem(hwndTreeView
, &tvItemEx
);
2136 *phti
= tvItemEx
.hItem
;
2138 return (PEVENTLOGFILTER
)tvItemEx
.lParam
;
2143 OpenUserEventLogFile(IN LPCWSTR lpszFileName
)
2145 WIN32_FIND_DATAW FindData
;
2148 PEVENTLOGFILTER EventLogFilter
;
2150 HTREEITEM hItem
= NULL
;
2152 /* Check whether the file actually exists */
2153 hFind
= FindFirstFileW(lpszFileName
, &FindData
);
2154 if (hFind
== INVALID_HANDLE_VALUE
)
2156 ShowWin32Error(GetLastError());
2161 /* Allocate a new event log entry */
2162 EventLog
= AllocEventLog(NULL
, lpszFileName
, FALSE
);
2163 if (EventLog
== NULL
)
2165 ShowWin32Error(ERROR_NOT_ENOUGH_MEMORY
);
2169 /* Allocate a new event log filter entry for this event log */
2170 EventLogFilter
= AllocEventLogFilter(// LogName,
2171 TRUE
, TRUE
, TRUE
, TRUE
, TRUE
,
2174 if (EventLogFilter
== NULL
)
2176 ShowWin32Error(ERROR_NOT_ENOUGH_MEMORY
);
2177 EventLog_Free(EventLog
);
2181 /* Add the event log and the filter into their lists */
2182 InsertTailList(&EventLogList
, &EventLog
->ListEntry
);
2183 InsertTailList(&EventLogFilterList
, &EventLogFilter
->ListEntry
);
2185 /* Retrieve and cache the event log file */
2186 cchFileName
= wcslen(lpszFileName
) + 1;
2187 EventLog
->FileName
= HeapAlloc(GetProcessHeap(), 0, cchFileName
* sizeof(WCHAR
));
2188 if (EventLog
->FileName
)
2189 StringCchCopyW(EventLog
->FileName
, cchFileName
, lpszFileName
);
2191 hItem
= TreeViewAddItem(hwndTreeView
, htiUserLogs
,
2192 (LPWSTR
)lpszFileName
,
2193 2, 3, (LPARAM
)EventLogFilter
);
2195 /* Select the event log */
2198 // TreeView_Expand(hwndTreeView, htiUserLogs, TVE_EXPAND);
2199 TreeView_SelectItem(hwndTreeView
, hItem
);
2200 TreeView_EnsureVisible(hwndTreeView
, hItem
);
2202 InvalidateRect(hwndTreeView
, NULL
, FALSE
);
2203 SetFocus(hwndTreeView
);
2207 OpenUserEventLog(VOID
)
2209 WCHAR szFileName
[MAX_PATH
];
2211 ZeroMemory(szFileName
, sizeof(szFileName
));
2213 sfn
.lpstrFile
= szFileName
;
2214 sfn
.nMaxFile
= ARRAYSIZE(szFileName
);
2216 if (!GetOpenFileNameW(&sfn
))
2218 sfn
.lpstrFile
[sfn
.nMaxFile
-1] = UNICODE_NULL
;
2220 OpenUserEventLogFile(sfn
.lpstrFile
);
2224 SaveEventLog(IN PEVENTLOGFILTER EventLogFilter
)
2228 WCHAR szFileName
[MAX_PATH
];
2230 /* Bail out if there is no available filter */
2231 if (!EventLogFilter
)
2234 ZeroMemory(szFileName
, sizeof(szFileName
));
2236 sfn
.lpstrFile
= szFileName
;
2237 sfn
.nMaxFile
= ARRAYSIZE(szFileName
);
2239 if (!GetSaveFileNameW(&sfn
))
2242 EventLogFilter_AddRef(EventLogFilter
);
2244 EventLog
= EventLogFilter
->EventLogs
[0];
2245 hEventLog
= OpenEventLogW(EventLog
->ComputerName
, EventLog
->LogName
);
2247 EventLogFilter_Release(EventLogFilter
);
2251 ShowWin32Error(GetLastError());
2255 if (!BackupEventLogW(hEventLog
, szFileName
))
2256 ShowWin32Error(GetLastError());
2258 CloseEventLog(hEventLog
);
2262 CloseUserEventLog(IN PEVENTLOGFILTER EventLogFilter
, IN HTREEITEM hti
)
2264 /* Bail out if there is no available filter */
2265 if (!EventLogFilter
)
2268 if (InterlockedCompareExchangePointer((PVOID
*)&ActiveFilter
, NULL
, NULL
) == EventLogFilter
)
2270 /* Signal the enumerator thread we want to stop enumerating events */
2271 // EnumEvents(NULL);
2272 InterlockedExchangePointer((PVOID
*)&EnumFilter
, NULL
);
2273 SetEvent(hStartEnumEvent
);
2277 * The deletion of the item automatically triggers a TVN_SELCHANGED
2278 * notification, that will reset the ActiveFilter (in case the item
2279 * selected is a filter). Otherwise we reset it there.
2281 TreeView_DeleteItem(hwndTreeView
, hti
);
2283 /* Remove the filter from the list */
2284 RemoveEntryList(&EventLogFilter
->ListEntry
);
2285 EventLogFilter_Release(EventLogFilter
);
2287 // /* Select the default event log */
2288 // // TreeView_Expand(hwndTreeView, htiUserLogs, TVE_EXPAND);
2289 // TreeView_SelectItem(hwndTreeView, hItem);
2290 // TreeView_EnsureVisible(hwndTreeView, hItem);
2291 InvalidateRect(hwndTreeView
, NULL
, FALSE
);
2292 SetFocus(hwndTreeView
);
2297 ClearEvents(IN PEVENTLOGFILTER EventLogFilter
)
2302 WCHAR szFileName
[MAX_PATH
];
2303 WCHAR szMessage
[MAX_LOADSTRING
];
2305 /* Bail out if there is no available filter */
2306 if (!EventLogFilter
)
2309 ZeroMemory(szFileName
, sizeof(szFileName
));
2310 ZeroMemory(szMessage
, sizeof(szMessage
));
2312 LoadStringW(hInst
, IDS_CLEAREVENTS_MSG
, szMessage
, ARRAYSIZE(szMessage
));
2314 sfn
.lpstrFile
= szFileName
;
2315 sfn
.nMaxFile
= ARRAYSIZE(szFileName
);
2317 switch (MessageBoxW(hwndMainWindow
, szMessage
, szTitle
, MB_YESNOCANCEL
| MB_ICONINFORMATION
))
2323 sfn
.lpstrFile
= NULL
;
2327 if (!GetSaveFileNameW(&sfn
))
2332 EventLogFilter_AddRef(EventLogFilter
);
2334 EventLog
= EventLogFilter
->EventLogs
[0];
2335 hEventLog
= OpenEventLogW(EventLog
->ComputerName
, EventLog
->LogName
);
2337 EventLogFilter_Release(EventLogFilter
);
2341 ShowWin32Error(GetLastError());
2345 Success
= ClearEventLogW(hEventLog
, sfn
.lpstrFile
);
2347 ShowWin32Error(GetLastError());
2349 CloseEventLog(hEventLog
);
2355 Refresh(IN PEVENTLOGFILTER EventLogFilter
)
2357 /* Bail out if there is no available filter */
2358 if (!EventLogFilter
)
2361 /* Reenumerate the events through the filter */
2362 EnumEvents(EventLogFilter
);
2367 MyRegisterClass(HINSTANCE hInstance
)
2371 wcex
.cbSize
= sizeof(wcex
);
2373 wcex
.lpfnWndProc
= WndProc
;
2374 wcex
.cbClsExtra
= 0;
2375 wcex
.cbWndExtra
= 0;
2376 wcex
.hInstance
= hInstance
;
2377 wcex
.hIcon
= LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_EVENTVWR
));
2378 wcex
.hCursor
= LoadCursorW(NULL
, MAKEINTRESOURCEW(IDC_ARROW
));
2379 wcex
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1); // COLOR_WINDOW + 1
2380 wcex
.lpszMenuName
= MAKEINTRESOURCEW(IDM_EVENTVWR
);
2381 wcex
.lpszClassName
= szWindowClass
;
2382 wcex
.hIconSm
= (HICON
)LoadImageW(hInstance
,
2383 MAKEINTRESOURCEW(IDI_EVENTVWR
),
2389 return RegisterClassExW(&wcex
);
2394 GetDisplayNameFileAndID(IN LPCWSTR lpLogName
,
2395 OUT PWCHAR lpModuleName
, // TODO: Add IN DWORD BufLen
2396 OUT PDWORD pdwMessageID
)
2398 BOOL Success
= FALSE
;
2404 DWORD dwMessageID
= 0;
2405 WCHAR szModuleName
[MAX_PATH
];
2407 /* Use a default value for the message ID */
2410 cbKeyPath
= (wcslen(EVENTLOG_BASE_KEY
) + wcslen(lpLogName
) + 1) * sizeof(WCHAR
);
2411 KeyPath
= HeapAlloc(GetProcessHeap(), 0, cbKeyPath
);
2415 StringCbCopyW(KeyPath
, cbKeyPath
, EVENTLOG_BASE_KEY
);
2416 StringCbCatW(KeyPath
, cbKeyPath
, lpLogName
);
2418 Result
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, KeyPath
, 0, KEY_QUERY_VALUE
, &hLogKey
);
2419 HeapFree(GetProcessHeap(), 0, KeyPath
);
2420 if (Result
!= ERROR_SUCCESS
)
2423 cbData
= sizeof(szModuleName
);
2424 Result
= RegQueryValueExW(hLogKey
,
2428 (LPBYTE
)szModuleName
,
2430 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_EXPAND_SZ
&& Type
!= REG_SZ
))
2432 szModuleName
[0] = UNICODE_NULL
;
2436 /* NULL-terminate the string and expand it */
2437 szModuleName
[cbData
/ sizeof(WCHAR
) - 1] = UNICODE_NULL
;
2438 ExpandEnvironmentStringsW(szModuleName
, lpModuleName
, ARRAYSIZE(szModuleName
));
2443 * If we have a 'DisplayNameFile', query for 'DisplayNameID';
2444 * otherwise it's not really useful. 'DisplayNameID' is optional.
2448 cbData
= sizeof(dwMessageID
);
2449 Result
= RegQueryValueExW(hLogKey
,
2453 (LPBYTE
)&dwMessageID
,
2455 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_DWORD
))
2458 *pdwMessageID
= dwMessageID
;
2461 RegCloseKey(hLogKey
);
2468 BuildLogListAndFilterList(IN LPCWSTR lpComputerName
)
2471 HKEY hEventLogKey
, hLogKey
;
2472 DWORD dwNumLogs
= 0;
2473 DWORD dwIndex
, dwMaxKeyLength
;
2476 PEVENTLOGFILTER EventLogFilter
;
2477 LPWSTR LogName
= NULL
;
2478 WCHAR szModuleName
[MAX_PATH
];
2481 LPWSTR lpDisplayName
;
2482 HTREEITEM hRootNode
= NULL
, hItem
= NULL
, hItemDefault
= NULL
;
2484 /* Open the EventLog key */
2485 // TODO: Implement connection to remote computer...
2486 // At the moment we only support the user local computer.
2487 // FIXME: Use local or remote computer
2488 Result
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, EVENTLOG_BASE_KEY
, 0, KEY_READ
, &hEventLogKey
);
2489 if (Result
!= ERROR_SUCCESS
)
2494 /* Retrieve the number of event logs enumerated as registry keys */
2495 Result
= RegQueryInfoKeyW(hEventLogKey
, NULL
, NULL
, NULL
, &dwNumLogs
, &dwMaxKeyLength
,
2496 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
2497 if (Result
!= ERROR_SUCCESS
)
2504 /* Take the NULL terminator into account */
2507 /* Allocate the temporary buffer */
2508 LogName
= HeapAlloc(GetProcessHeap(), 0, dwMaxKeyLength
* sizeof(WCHAR
));
2512 /* Enumerate and retrieve each event log name */
2513 for (dwIndex
= 0; dwIndex
< dwNumLogs
; dwIndex
++)
2515 lpcName
= dwMaxKeyLength
;
2516 Result
= RegEnumKeyExW(hEventLogKey
, dwIndex
, LogName
, &lpcName
, NULL
, NULL
, NULL
, NULL
);
2517 if (Result
!= ERROR_SUCCESS
)
2520 /* Take the NULL terminator into account */
2523 /* Allocate a new event log entry */
2524 EventLog
= AllocEventLog(lpComputerName
, LogName
, TRUE
);
2525 if (EventLog
== NULL
)
2528 /* Allocate a new event log filter entry for this event log */
2529 EventLogFilter
= AllocEventLogFilter(// LogName,
2530 TRUE
, TRUE
, TRUE
, TRUE
, TRUE
,
2533 if (EventLogFilter
== NULL
)
2535 EventLog_Free(EventLog
);
2539 /* Add the event log and the filter into their lists */
2540 InsertTailList(&EventLogList
, &EventLog
->ListEntry
);
2541 InsertTailList(&EventLogFilterList
, &EventLogFilter
->ListEntry
);
2543 EventLog
->FileName
= NULL
;
2545 /* Retrieve and cache the event log file */
2546 Result
= RegOpenKeyExW(hEventLogKey
,
2551 if (Result
== ERROR_SUCCESS
)
2554 Result
= RegQueryValueExW(hLogKey
,
2560 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_EXPAND_SZ
&& Type
!= REG_SZ
))
2562 // Windows' EventLog uses some kind of default value, we do not.
2563 EventLog
->FileName
= NULL
;
2567 lpcName
= ROUND_DOWN(lpcName
, sizeof(WCHAR
));
2568 EventLog
->FileName
= HeapAlloc(GetProcessHeap(), 0, lpcName
);
2569 if (EventLog
->FileName
)
2571 Result
= RegQueryValueExW(hLogKey
,
2575 (LPBYTE
)EventLog
->FileName
,
2577 if (Result
!= ERROR_SUCCESS
)
2579 HeapFree(GetProcessHeap(), 0, EventLog
->FileName
);
2580 EventLog
->FileName
= NULL
;
2584 EventLog
->FileName
[lpcName
/ sizeof(WCHAR
) - 1] = UNICODE_NULL
;
2589 RegCloseKey(hLogKey
);
2592 /* Get the display name for the event log */
2593 lpDisplayName
= NULL
;
2595 ZeroMemory(szModuleName
, sizeof(szModuleName
));
2596 if (GetDisplayNameFileAndID(LogName
, szModuleName
, &dwMessageID
))
2598 /* Retrieve the message string without appending extra newlines */
2600 GetMessageStringFromDll(szModuleName
,
2601 FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_HMODULE
|
2602 FORMAT_MESSAGE_IGNORE_INSERTS
| FORMAT_MESSAGE_MAX_WIDTH_MASK
,
2609 * Select the correct tree root node, whether the log is a System
2610 * or an Application log. Default to Application log otherwise.
2612 hRootNode
= htiAppLogs
;
2613 for (lpcName
= 0; lpcName
< ARRAYSIZE(SystemLogs
); ++lpcName
)
2615 /* Check whether the log name is part of the system logs */
2616 if (wcsicmp(LogName
, SystemLogs
[lpcName
]) == 0)
2618 hRootNode
= htiSystemLogs
;
2623 hItem
= TreeViewAddItem(hwndTreeView
, hRootNode
,
2624 (lpDisplayName
? lpDisplayName
: LogName
),
2625 2, 3, (LPARAM
)EventLogFilter
);
2627 /* Try to get the default event log: "Application" */
2628 if ((hItemDefault
== NULL
) && (wcsicmp(LogName
, SystemLogs
[0]) == 0))
2630 hItemDefault
= hItem
;
2633 /* Free the buffer allocated by FormatMessage */
2635 LocalFree(lpDisplayName
);
2638 HeapFree(GetProcessHeap(), 0, LogName
);
2641 RegCloseKey(hEventLogKey
);
2643 /* Select the default event log */
2646 // TreeView_Expand(hwndTreeView, hRootNode, TVE_EXPAND);
2647 TreeView_SelectItem(hwndTreeView
, hItemDefault
);
2648 TreeView_EnsureVisible(hwndTreeView
, hItemDefault
);
2650 InvalidateRect(hwndTreeView
, NULL
, FALSE
);
2651 SetFocus(hwndTreeView
);
2662 while (!IsListEmpty(&EventLogList
))
2664 Entry
= RemoveHeadList(&EventLogList
);
2665 EventLog
= (PEVENTLOG
)CONTAINING_RECORD(Entry
, EVENTLOG
, ListEntry
);
2666 EventLog_Free(EventLog
);
2673 FreeLogFilterList(VOID
)
2676 PEVENTLOGFILTER EventLogFilter
;
2678 while (!IsListEmpty(&EventLogFilterList
))
2680 Entry
= RemoveHeadList(&EventLogFilterList
);
2681 EventLogFilter
= (PEVENTLOGFILTER
)CONTAINING_RECORD(Entry
, EVENTLOGFILTER
, ListEntry
);
2682 EventLogFilter_Free(EventLogFilter
);
2685 ActiveFilter
= NULL
;
2691 InitInstance(HINSTANCE hInstance
,
2697 LVCOLUMNW lvc
= {0};
2700 /* Create the main window */
2701 hwndMainWindow
= CreateWindowW(szWindowClass
,
2703 WS_OVERLAPPEDWINDOW
| WS_CLIPCHILDREN
,
2704 CW_USEDEFAULT
, 0, CW_USEDEFAULT
, 0,
2709 if (!hwndMainWindow
)
2712 /* Create the status bar */
2713 hwndStatus
= CreateWindowExW(0, // no extended styles
2714 STATUSCLASSNAMEW
, // status bar
2716 WS_CHILD
| WS_VISIBLE
| CCS_BOTTOM
| SBARS_SIZEGRIP
, // styles
2717 0, 0, 0, 0, // x, y, cx, cy
2718 hwndMainWindow
, // parent window
2719 (HMENU
)100, // window ID
2720 hInstance
, // instance
2721 NULL
); // window data
2723 GetClientRect(hwndMainWindow
, &rcClient
);
2724 GetWindowRect(hwndStatus
, &rs
);
2725 StatusHeight
= rs
.bottom
- rs
.top
;
2727 /* Create a progress bar in the status bar (hidden by default) */
2728 StatusBar_GetItemRect(hwndStatus
, 0, &rs
);
2729 hwndStatusProgress
= CreateWindowExW(0, // no extended styles
2730 PROGRESS_CLASSW
, // status bar
2732 WS_CHILD
| PBS_SMOOTH
, // styles
2733 rs
.left
, rs
.top
, // x, y
2734 rs
.right
-rs
.left
, rs
.bottom
-rs
.top
, // cx, cy
2735 hwndStatus
, // parent window
2737 hInstance
, // instance
2738 NULL
); // window data
2739 ProgressBar_SetStep(hwndStatusProgress
, 1);
2741 /* Initialize the splitter default positions */
2745 /* Create the TreeView */
2746 hwndTreeView
= CreateWindowExW(WS_EX_CLIENTEDGE
,
2749 // WS_CHILD | WS_VISIBLE | TVS_HASLINES | TVS_SHOWSELALWAYS,
2750 WS_CHILD
| WS_VISIBLE
| /* WS_TABSTOP | */ TVS_HASLINES
| TVS_HASBUTTONS
| TVS_LINESATROOT
| TVS_EDITLABELS
| TVS_SHOWSELALWAYS
,
2752 nVSplitPos
- SPLIT_WIDTH
/2,
2753 (rcClient
.bottom
- rcClient
.top
) - StatusHeight
,
2759 /* Create the ImageList */
2760 hSmall
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
),
2761 GetSystemMetrics(SM_CYSMICON
),
2762 ILC_COLOR32
| ILC_MASK
, // ILC_COLOR24
2765 /* Add event type icons to the ImageList: closed/opened folder, event log (normal/viewed) */
2766 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_CLOSED_CATEGORY
)));
2767 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_OPENED_CATEGORY
)));
2768 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_EVENTLOG
)));
2769 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_EVENTVWR
)));
2771 /* Assign the ImageList to the Tree View */
2772 TreeView_SetImageList(hwndTreeView
, hSmall
, TVSIL_NORMAL
);
2774 /* Add the event logs nodes */
2776 LoadStringW(hInstance
, IDS_EVENTLOG_SYSTEM
, szTemp
, ARRAYSIZE(szTemp
));
2777 htiSystemLogs
= TreeViewAddItem(hwndTreeView
, NULL
, szTemp
, 0, 1, (LPARAM
)NULL
);
2778 // "Application Logs"
2779 LoadStringW(hInstance
, IDS_EVENTLOG_APP
, szTemp
, ARRAYSIZE(szTemp
));
2780 htiAppLogs
= TreeViewAddItem(hwndTreeView
, NULL
, szTemp
, 0, 1, (LPARAM
)NULL
);
2782 LoadStringW(hInstance
, IDS_EVENTLOG_USER
, szTemp
, ARRAYSIZE(szTemp
));
2783 htiUserLogs
= TreeViewAddItem(hwndTreeView
, NULL
, szTemp
, 0, 1, (LPARAM
)NULL
);
2785 /* Create the Event details pane (optional) */
2786 hwndEventDetails
= CreateEventDetailsCtrl(hInst
, hwndMainWindow
, (LPARAM
)NULL
);
2787 // hwndEventDetails = NULL;
2788 if (hwndEventDetails
)
2790 // SetWindowLongPtrW(hwndEventDetails, GWL_STYLE,
2791 // GetWindowLongPtrW(hwndEventDetails, GWL_STYLE) | WS_BORDER);
2792 SetWindowLongPtrW(hwndEventDetails
, GWL_EXSTYLE
,
2793 GetWindowLongPtrW(hwndEventDetails
, GWL_EXSTYLE
) | WS_EX_CLIENTEDGE
);
2794 SetWindowPos(hwndEventDetails
, NULL
,
2795 nVSplitPos
+ SPLIT_WIDTH
/2,
2796 nHSplitPos
+ SPLIT_WIDTH
/2,
2797 (rcClient
.right
- rcClient
.left
) - nVSplitPos
- SPLIT_WIDTH
/2,
2798 (rcClient
.bottom
- rcClient
.top
) - nHSplitPos
- SPLIT_WIDTH
/2 - StatusHeight
,
2799 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_SHOWWINDOW
);
2802 /* Create the ListView */
2803 hwndListView
= CreateWindowExW(WS_EX_CLIENTEDGE
,
2806 WS_CHILD
| WS_VISIBLE
| LVS_SHOWSELALWAYS
| LVS_REPORT
,
2807 nVSplitPos
+ SPLIT_WIDTH
/2,
2809 (rcClient
.right
- rcClient
.left
) - nVSplitPos
- SPLIT_WIDTH
/2,
2810 hwndEventDetails
? nHSplitPos
- SPLIT_WIDTH
/2
2811 : (rcClient
.bottom
- rcClient
.top
) - StatusHeight
,
2817 /* Add the extended ListView styles */
2818 ListView_SetExtendedListViewStyle(hwndListView
, LVS_EX_HEADERDRAGDROP
| LVS_EX_FULLROWSELECT
|LVS_EX_LABELTIP
);
2820 /* Create the ImageList */
2821 hSmall
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
),
2822 GetSystemMetrics(SM_CYSMICON
),
2823 ILC_COLOR32
| ILC_MASK
, // ILC_COLOR24
2826 /* Add event type icons to the ImageList */
2827 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_INFORMATIONICON
)));
2828 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_WARNINGICON
)));
2829 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_ERRORICON
)));
2830 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_AUDITSUCCESSICON
)));
2831 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_AUDITFAILUREICON
)));
2833 /* Assign the ImageList to the List View */
2834 ListView_SetImageList(hwndListView
, hSmall
, LVSIL_SMALL
);
2836 /* Now set up the listview with its columns */
2837 lvc
.mask
= LVCF_TEXT
| LVCF_WIDTH
;
2839 LoadStringW(hInstance
,
2843 lvc
.pszText
= szTemp
;
2844 ListView_InsertColumn(hwndListView
, 0, &lvc
);
2847 LoadStringW(hInstance
,
2851 lvc
.pszText
= szTemp
;
2852 ListView_InsertColumn(hwndListView
, 1, &lvc
);
2855 LoadStringW(hInstance
,
2859 lvc
.pszText
= szTemp
;
2860 ListView_InsertColumn(hwndListView
, 2, &lvc
);
2863 LoadStringW(hInstance
,
2867 lvc
.pszText
= szTemp
;
2868 ListView_InsertColumn(hwndListView
, 3, &lvc
);
2871 LoadStringW(hInstance
,
2875 lvc
.pszText
= szTemp
;
2876 ListView_InsertColumn(hwndListView
, 4, &lvc
);
2879 LoadStringW(hInstance
,
2883 lvc
.pszText
= szTemp
;
2884 ListView_InsertColumn(hwndListView
, 5, &lvc
);
2887 LoadStringW(hInstance
,
2891 lvc
.pszText
= szTemp
;
2892 ListView_InsertColumn(hwndListView
, 6, &lvc
);
2895 LoadStringW(hInstance
,
2899 lvc
.pszText
= szTemp
;
2900 ListView_InsertColumn(hwndListView
, 7, &lvc
);
2902 /* Initialize the save Dialog */
2903 ZeroMemory(&sfn
, sizeof(sfn
));
2904 ZeroMemory(szSaveFilter
, sizeof(szSaveFilter
));
2906 LoadStringW(hInst
, IDS_SAVE_FILTER
, szSaveFilter
, ARRAYSIZE(szSaveFilter
));
2908 sfn
.lStructSize
= sizeof(sfn
);
2909 sfn
.hwndOwner
= hwndMainWindow
;
2910 sfn
.hInstance
= hInstance
;
2911 sfn
.lpstrFilter
= szSaveFilter
;
2912 sfn
.lpstrInitialDir
= NULL
;
2913 sfn
.Flags
= OFN_HIDEREADONLY
| OFN_SHAREAWARE
;
2914 sfn
.lpstrDefExt
= NULL
;
2916 ShowWindow(hwndMainWindow
, nCmdShow
);
2917 UpdateWindow(hwndMainWindow
);
2922 VOID
ResizeWnd(INT cx
, INT cy
)
2928 /* Resize the status bar -- now done in WM_SIZE */
2929 // SendMessageW(hwndStatus, WM_SIZE, 0, 0);
2930 GetWindowRect(hwndStatus
, &rs
);
2931 StatusHeight
= rs
.bottom
- rs
.top
;
2933 /* Move the progress bar */
2934 StatusBar_GetItemRect(hwndStatus
, 0, &rs
);
2935 MoveWindow(hwndStatusProgress
,
2936 rs
.left
, rs
.top
, rs
.right
-rs
.left
, rs
.bottom
-rs
.top
,
2937 IsWindowVisible(hwndStatusProgress
) ? TRUE
: FALSE
);
2940 * TODO: Adjust the splitter positions:
2941 * - Vertical splitter (1) : fixed position from the left window side.
2942 * - Horizontal splitter (2): fixed position from the bottom window side.
2944 nVSplitPos
= min(max(nVSplitPos
, SPLIT_WIDTH
/2), cx
- SPLIT_WIDTH
/2); // OK
2945 nHSplitPos
= min(max(nHSplitPos
, SPLIT_WIDTH
/2), cy
- SPLIT_WIDTH
/2 - StatusHeight
); // FIXME!
2947 hdwp
= BeginDeferWindowPos(3);
2950 hdwp
= DeferWindowPos(hdwp
,
2954 nVSplitPos
- SPLIT_WIDTH
/2,
2956 SWP_NOZORDER
| SWP_NOACTIVATE
);
2959 hdwp
= DeferWindowPos(hdwp
,
2962 nVSplitPos
+ SPLIT_WIDTH
/2, 0,
2963 cx
- nVSplitPos
- SPLIT_WIDTH
/2,
2964 hwndEventDetails
? nHSplitPos
- SPLIT_WIDTH
/2
2965 : cy
- StatusHeight
,
2966 SWP_NOZORDER
| SWP_NOACTIVATE
);
2968 if (hwndEventDetails
&& hdwp
)
2969 hdwp
= DeferWindowPos(hdwp
,
2972 nVSplitPos
+ SPLIT_WIDTH
/2,
2973 nHSplitPos
+ SPLIT_WIDTH
/2,
2974 cx
- nVSplitPos
- SPLIT_WIDTH
/2,
2975 cy
- nHSplitPos
- SPLIT_WIDTH
/2 - StatusHeight
,
2976 SWP_NOZORDER
| SWP_NOACTIVATE
);
2979 EndDeferWindowPos(hdwp
);
2984 WndProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
2991 hMainMenu
= GetMenu(hWnd
);
3000 LPNMHDR hdr
= (LPNMHDR
)lParam
;
3002 if (hdr
->hwndFrom
== hwndListView
)
3006 case LVN_ITEMCHANGED
:
3008 LPNMLISTVIEW pnmv
= (LPNMLISTVIEW
)lParam
;
3010 if ( (pnmv
->uChanged
& LVIF_STATE
) && /* The state has changed */
3011 (pnmv
->uNewState
& LVIS_SELECTED
) /* The item has been (de)selected */ )
3013 if (hwndEventDetails
)
3014 SendMessageW(hwndEventDetails
, EVT_DISPLAY
, 0, 0);
3021 SendMessageW(hWnd
, WM_COMMAND
, IDM_EVENT_DETAILS
, 0);
3025 else if (hdr
->hwndFrom
== hwndTreeView
)
3029 case TVN_BEGINLABELEDIT
:
3031 HTREEITEM hItem
= ((LPNMTVDISPINFO
)lParam
)->item
.hItem
;
3033 /* Disable label editing for root nodes */
3034 return ((hItem
== htiSystemLogs
) ||
3035 (hItem
== htiAppLogs
) ||
3036 (hItem
== htiUserLogs
));
3039 case TVN_ENDLABELEDIT
:
3041 TVITEMW item
= ((LPNMTVDISPINFO
)lParam
)->item
;
3042 HTREEITEM hItem
= item
.hItem
;
3044 /* Disable label editing for root nodes */
3045 if ((hItem
== htiSystemLogs
) ||
3046 (hItem
== htiAppLogs
) ||
3047 (hItem
== htiUserLogs
))
3054 LPWSTR pszText
= item
.pszText
;
3056 /* Trim all whitespace */
3057 while (*pszText
&& iswspace(*pszText
))
3071 case TVN_SELCHANGED
:
3073 PEVENTLOGFILTER EventLogFilter
=
3074 (PEVENTLOGFILTER
)((LPNMTREEVIEW
)lParam
)->itemNew
.lParam
;
3076 // FIXME: It might be nice to reference here the filter,
3077 // so that we don't have to reference/dereference it many times
3078 // in the other functions???
3080 // FIXME: This is a hack!!
3081 if (hwndEventDetails
&& EventLogFilter
)
3083 SendMessageW(hwndEventDetails
, EVT_SETFILTER
, 0, (LPARAM
)EventLogFilter
);
3089 * If we have selected a filter, enable the menu commands;
3090 * they will possibly be updated after events enumeration.
3092 EnableMenuItem(hMainMenu
, IDM_SAVE_EVENTLOG
, MF_BYCOMMAND
| MF_ENABLED
);
3093 EnableMenuItem(hMainMenu
, IDM_CLOSE_EVENTLOG
, MF_BYCOMMAND
| MF_ENABLED
);
3094 EnableMenuItem(hMainMenu
, IDM_CLEAR_EVENTS
, MF_BYCOMMAND
| MF_ENABLED
);
3095 EnableMenuItem(hMainMenu
, IDM_RENAME_EVENTLOG
, MF_BYCOMMAND
| MF_ENABLED
);
3096 EnableMenuItem(hMainMenu
, IDM_EVENTLOG_SETTINGS
, MF_BYCOMMAND
| MF_ENABLED
);
3100 EnableMenuItem(hMainMenu
, IDM_SAVE_EVENTLOG
, MF_BYCOMMAND
| MF_GRAYED
);
3101 EnableMenuItem(hMainMenu
, IDM_CLOSE_EVENTLOG
, MF_BYCOMMAND
| MF_GRAYED
);
3102 EnableMenuItem(hMainMenu
, IDM_CLEAR_EVENTS
, MF_BYCOMMAND
| MF_GRAYED
);
3103 EnableMenuItem(hMainMenu
, IDM_RENAME_EVENTLOG
, MF_BYCOMMAND
| MF_GRAYED
);
3104 EnableMenuItem(hMainMenu
, IDM_EVENTLOG_SETTINGS
, MF_BYCOMMAND
| MF_GRAYED
);
3108 * The enumeration thread that is triggered by EnumEvents
3109 * will set a new value for the 'ActiveFilter'.
3112 EnumEvents(EventLogFilter
);
3123 /* Parse the menu selections */
3124 switch (LOWORD(wParam
))
3126 case IDM_OPEN_EVENTLOG
:
3130 case IDM_SAVE_EVENTLOG
:
3131 SaveEventLog(GetSelectedFilter(NULL
));
3134 case IDM_CLOSE_EVENTLOG
:
3137 PEVENTLOGFILTER EventLogFilter
= GetSelectedFilter(&hti
);
3138 CloseUserEventLog(EventLogFilter
, hti
);
3142 case IDM_CLEAR_EVENTS
:
3144 PEVENTLOGFILTER EventLogFilter
= GetSelectedFilter(NULL
);
3145 if (EventLogFilter
&& ClearEvents(EventLogFilter
))
3146 Refresh(EventLogFilter
);
3150 case IDM_RENAME_EVENTLOG
:
3151 if (GetFocus() == hwndTreeView
)
3152 TreeView_EditLabel(hwndTreeView
, TreeView_GetSelection(hwndTreeView
));
3155 case IDM_EVENTLOG_SETTINGS
:
3157 PEVENTLOGFILTER EventLogFilter
= GetSelectedFilter(NULL
);
3158 // TODO: Check the returned value?
3160 EventLogProperties(hInst
, hWnd
, EventLogFilter
);
3164 case IDM_LIST_NEWEST
:
3166 CheckMenuRadioItem(hMainMenu
, IDM_LIST_NEWEST
, IDM_LIST_OLDEST
, IDM_LIST_NEWEST
, MF_BYCOMMAND
);
3167 if (!NewestEventsFirst
)
3169 NewestEventsFirst
= TRUE
;
3170 Refresh(GetSelectedFilter(NULL
));
3175 case IDM_LIST_OLDEST
:
3177 CheckMenuRadioItem(hMainMenu
, IDM_LIST_NEWEST
, IDM_LIST_OLDEST
, IDM_LIST_OLDEST
, MF_BYCOMMAND
);
3178 if (NewestEventsFirst
)
3180 NewestEventsFirst
= FALSE
;
3181 Refresh(GetSelectedFilter(NULL
));
3186 case IDM_EVENT_DETAILS
:
3188 // LPNMITEMACTIVATE lpnmitem = (LPNMITEMACTIVATE)lParam;
3189 PEVENTLOGFILTER EventLogFilter
= GetSelectedFilter(NULL
);
3190 if (/*lpnmitem->iItem != -1 &&*/ EventLogFilter
)
3192 EventLogFilter_AddRef(EventLogFilter
);
3193 DialogBoxParamW(hInst
,
3194 MAKEINTRESOURCEW(IDD_EVENTDETAILS_DLG
),
3197 (LPARAM
)EventLogFilter
);
3198 EventLogFilter_Release(EventLogFilter
);
3204 Refresh(GetSelectedFilter(NULL
));
3210 WCHAR szCopyright
[MAX_LOADSTRING
];
3212 hIcon
= LoadIconW(hInst
, MAKEINTRESOURCEW(IDI_EVENTVWR
));
3213 LoadStringW(hInst
, IDS_COPYRIGHT
, szCopyright
, ARRAYSIZE(szCopyright
));
3214 ShellAboutW(hWnd
, szTitle
, szCopyright
, hIcon
);
3215 DeleteObject(hIcon
);
3220 MessageBoxW(hwndMainWindow
,
3221 L
"Help not implemented yet!",
3223 MB_OK
| MB_ICONINFORMATION
);
3227 DestroyWindow(hWnd
);
3231 return DefWindowProcW(hWnd
, uMsg
, wParam
, lParam
);
3237 if (LOWORD(lParam
) == HTCLIENT
)
3241 ScreenToClient(hWnd
, &pt
);
3243 /* Set the cursor for the vertical splitter */
3244 if (pt
.x
>= nVSplitPos
- SPLIT_WIDTH
/2 && pt
.x
< nVSplitPos
+ SPLIT_WIDTH
/2 + 1)
3247 GetClientRect(hWnd
, &rect
);
3248 GetWindowRect(hwndStatus
, &rs
);
3249 if (pt
.y
>= rect
.top
&& pt
.y
< rect
.bottom
- (rs
.bottom
- rs
.top
))
3251 SetCursor(LoadCursorW(NULL
, IDC_SIZEWE
));
3256 /* Set the cursor for the horizontal splitter, if the Event details pane is displayed */
3257 if (hwndEventDetails
&&
3258 (pt
.y
>= nHSplitPos
- SPLIT_WIDTH
/2 && pt
.y
< nHSplitPos
+ SPLIT_WIDTH
/2 + 1))
3261 GetClientRect(hWnd
, &rect
);
3262 // GetWindowRect(hwndStatus, &rs);
3263 if (pt
.x
>= nVSplitPos
+ SPLIT_WIDTH
/2 + 1 /* rect.left + (rs.bottom - rs.top) */ &&
3266 SetCursor(LoadCursorW(NULL
, IDC_SIZENS
));
3273 case WM_LBUTTONDOWN
:
3275 INT x
= GET_X_LPARAM(lParam
);
3276 INT y
= GET_Y_LPARAM(lParam
);
3278 /* Reset the splitter state */
3281 /* Capture the cursor for the vertical splitter */
3282 if (x
>= nVSplitPos
- SPLIT_WIDTH
/2 && x
< nVSplitPos
+ SPLIT_WIDTH
/2 + 1)
3288 /* Capture the cursor for the horizontal splitter, if the Event details pane is displayed */
3289 if (hwndEventDetails
&&
3290 (y
>= nHSplitPos
- SPLIT_WIDTH
/2 && y
< nHSplitPos
+ SPLIT_WIDTH
/2 + 1))
3299 case WM_RBUTTONDOWN
:
3300 if (GetCapture() == hWnd
)
3302 /* Adjust the correct splitter position */
3304 nVSplitPos
= GET_X_LPARAM(lParam
);
3305 else if (bSplit
== 2)
3306 nHSplitPos
= GET_Y_LPARAM(lParam
);
3308 /* If we are splitting, resize the windows */
3311 GetClientRect(hWnd
, &rect
);
3312 ResizeWnd(rect
.right
- rect
.left
, rect
.bottom
- rect
.top
);
3315 /* Reset the splitter state */
3323 if (GetCapture() == hWnd
)
3325 /* Move the correct splitter */
3328 INT x
= GET_X_LPARAM(lParam
);
3330 GetClientRect(hWnd
, &rect
);
3332 x
= min(max(x
, SPLIT_WIDTH
/2), rect
.right
- rect
.left
- SPLIT_WIDTH
/2);
3333 if (nVSplitPos
!= x
)
3336 ResizeWnd(rect
.right
- rect
.left
, rect
.bottom
- rect
.top
);
3339 else if (bSplit
== 2)
3342 INT y
= GET_Y_LPARAM(lParam
);
3344 GetClientRect(hWnd
, &rect
);
3345 GetWindowRect(hwndStatus
, &rs
);
3347 y
= min(max(y
, SPLIT_WIDTH
/2), rect
.bottom
- rect
.top
- SPLIT_WIDTH
/2 - (rs
.bottom
- rs
.top
));
3348 if (nHSplitPos
!= y
)
3351 ResizeWnd(rect
.right
- rect
.left
, rect
.bottom
- rect
.top
);
3359 if (wParam
!= SIZE_MINIMIZED
)
3361 SendMessageW(hwndStatus
, WM_SIZE
, 0, 0);
3362 ResizeWnd(LOWORD(lParam
), HIWORD(lParam
));
3365 /* Fall through the default case */
3369 return DefWindowProcW(hWnd
, uMsg
, wParam
, lParam
);
3378 InitPropertiesDlg(HWND hDlg
, PEVENTLOG EventLog
)
3380 LPWSTR lpLogName
= EventLog
->LogName
;
3383 DWORD dwMaxSize
= 0, dwRetention
= 0;
3385 WIN32_FIND_DATAW FileInfo
; // WIN32_FILE_ATTRIBUTE_DATA
3386 ULARGE_INTEGER FileSize
;
3387 WCHAR wszBuf
[MAX_PATH
];
3388 WCHAR szTemp
[MAX_LOADSTRING
];
3396 if (EventLog
->Permanent
)
3399 cbKeyPath
= (wcslen(EVENTLOG_BASE_KEY
) + wcslen(lpLogName
) + 1) * sizeof(WCHAR
);
3400 KeyPath
= HeapAlloc(GetProcessHeap(), 0, cbKeyPath
);
3406 StringCbCopyW(KeyPath
, cbKeyPath
, EVENTLOG_BASE_KEY
);
3407 StringCbCatW(KeyPath
, cbKeyPath
, lpLogName
);
3409 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE
, KeyPath
, 0, KEY_QUERY_VALUE
, &hLogKey
) != ERROR_SUCCESS
)
3411 HeapFree(GetProcessHeap(), 0, KeyPath
);
3414 HeapFree(GetProcessHeap(), 0, KeyPath
);
3417 cbData
= sizeof(dwMaxSize
);
3418 Result
= RegQueryValueExW(hLogKey
,
3424 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_DWORD
))
3426 // dwMaxSize = 512 * 1024; /* 512 kBytes */
3432 cbData
= sizeof(dwRetention
);
3433 Result
= RegQueryValueExW(hLogKey
,
3437 (LPBYTE
)&dwRetention
,
3439 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_DWORD
))
3441 /* On Windows 2003 it is 604800 (secs) == 7 days */
3444 /* Convert in days, rounded up */ // ROUND_UP
3445 // dwRetention = ROUND_UP(dwRetention, 24*3600) / (24*3600);
3446 dwRetention
= (dwRetention
+ 24*3600 - 1) / (24*3600);
3449 RegCloseKey(hLogKey
);
3456 SetDlgItemTextW(hDlg
, IDC_DISPLAYNAME
, lpLogName
); // FIXME!
3457 SetDlgItemTextW(hDlg
, IDC_LOGNAME
, lpLogName
);
3459 FileName
= EventLog
->FileName
;
3460 if (FileName
&& *FileName
)
3462 ExpandEnvironmentStringsW(FileName
, wszBuf
, MAX_PATH
);
3465 SetDlgItemTextW(hDlg
, IDC_LOGFILE
, FileName
);
3468 * The general problem here (and in the shell as well) is that
3469 * GetFileAttributesEx fails for files that are opened without
3470 * shared access. To retrieve file information for those we need
3471 * to use something else: FindFirstFile, on the full file name.
3474 Success
= GetFileAttributesExW(FileName
,
3475 GetFileExInfoStandard
,
3476 (LPWIN32_FILE_ATTRIBUTE_DATA
)&FileInfo
);
3479 HANDLE hFind
= FindFirstFileW(FileName
, &FileInfo
);
3480 Success
= (hFind
!= INVALID_HANDLE_VALUE
);
3485 // Starting there, FileName is invalid (because it uses wszBuf)
3489 FileSize
.u
.LowPart
= FileInfo
.nFileSizeLow
;
3490 FileSize
.u
.HighPart
= FileInfo
.nFileSizeHigh
;
3491 if (FormatFileSizeWithBytes(&FileSize
, wszBuf
, ARRAYSIZE(wszBuf
)))
3492 SetDlgItemTextW(hDlg
, IDC_SIZE_LABEL
, wszBuf
);
3494 LoadStringW(hInst
, IDS_NOT_AVAILABLE
, szTemp
, ARRAYSIZE(szTemp
));
3496 if (GetFileTimeString(&FileInfo
.ftCreationTime
, wszBuf
, ARRAYSIZE(wszBuf
)))
3497 SetDlgItemTextW(hDlg
, IDC_CREATED_LABEL
, wszBuf
);
3499 SetDlgItemTextW(hDlg
, IDC_CREATED_LABEL
, szTemp
);
3501 if (GetFileTimeString(&FileInfo
.ftLastWriteTime
, wszBuf
, ARRAYSIZE(wszBuf
)))
3502 SetDlgItemTextW(hDlg
, IDC_MODIFIED_LABEL
, wszBuf
);
3504 SetDlgItemTextW(hDlg
, IDC_MODIFIED_LABEL
, szTemp
);
3506 if (GetFileTimeString(&FileInfo
.ftLastAccessTime
, wszBuf
, ARRAYSIZE(wszBuf
)))
3507 SetDlgItemTextW(hDlg
, IDC_ACCESSED_LABEL
, wszBuf
);
3509 SetDlgItemTextW(hDlg
, IDC_MODIFIED_LABEL
, szTemp
);
3513 LoadStringW(hInst
, IDS_NOT_AVAILABLE
, szTemp
, ARRAYSIZE(szTemp
));
3515 SetDlgItemTextW(hDlg
, IDC_SIZE_LABEL
, szTemp
);
3516 SetDlgItemTextW(hDlg
, IDC_CREATED_LABEL
, szTemp
);
3517 SetDlgItemTextW(hDlg
, IDC_MODIFIED_LABEL
, szTemp
);
3518 SetDlgItemTextW(hDlg
, IDC_ACCESSED_LABEL
, szTemp
);
3521 if (EventLog
->Permanent
)
3523 SendDlgItemMessageW(hDlg
, IDC_UPDOWN_MAXLOGSIZE
, UDM_SETRANGE32
, (WPARAM
)1, (LPARAM
)0x3FFFC0);
3524 SendDlgItemMessageW(hDlg
, IDC_UPDOWN_EVENTS_AGE
, UDM_SETRANGE
, 0, (LPARAM
)MAKELONG(365, 1));
3526 SetDlgItemInt(hDlg
, IDC_EDIT_MAXLOGSIZE
, dwMaxSize
, FALSE
);
3527 SetDlgItemInt(hDlg
, IDC_EDIT_EVENTS_AGE
, dwRetention
, FALSE
);
3529 if (dwRetention
== 0)
3531 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_OVERWRITE_AS_NEEDED
);
3532 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, FALSE
);
3533 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, FALSE
);
3535 else if (dwRetention
== INFINITE
)
3537 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_NO_OVERWRITE
);
3538 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, FALSE
);
3539 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, FALSE
);
3543 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_OVERWRITE_OLDER_THAN
);
3544 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, TRUE
);
3545 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, TRUE
);
3550 // TODO: Hide the unused controls! Or, just use another type of property sheet!
3554 /* Message handler for EventLog Properties dialog */
3556 EventLogPropProc(HWND hDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
3560 EventLog
= (PEVENTLOG
)GetWindowLongPtrW(hDlg
, DWLP_USER
);
3566 EventLog
= (PEVENTLOG
)((LPPROPSHEETPAGE
)lParam
)->lParam
;
3567 SetWindowLongPtrW(hDlg
, DWLP_USER
, (LONG_PTR
)EventLog
);
3569 InitPropertiesDlg(hDlg
, EventLog
);
3571 PropSheet_UnChanged(GetParent(hDlg
), hDlg
);
3572 return (INT_PTR
)TRUE
;
3576 return (INT_PTR
)TRUE
;
3579 switch (LOWORD(wParam
))
3583 EndDialog(hDlg
, LOWORD(wParam
));
3584 return (INT_PTR
)TRUE
;
3586 case IDC_OVERWRITE_AS_NEEDED
:
3588 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_OVERWRITE_AS_NEEDED
);
3589 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, FALSE
);
3590 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, FALSE
);
3594 case IDC_OVERWRITE_OLDER_THAN
:
3596 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_OVERWRITE_OLDER_THAN
);
3597 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, TRUE
);
3598 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, TRUE
);
3602 case IDC_NO_OVERWRITE
:
3604 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_NO_OVERWRITE
);
3605 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, FALSE
);
3606 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, FALSE
);
3612 L
"Help not implemented yet!",
3614 MB_OK
| MB_ICONINFORMATION
);
3615 return (INT_PTR
)TRUE
;
3623 return (INT_PTR
)FALSE
;
3627 EventLogProperties(HINSTANCE hInstance
, HWND hWndParent
, PEVENTLOGFILTER EventLogFilter
)
3630 PROPSHEETHEADERW psh
;
3631 PROPSHEETPAGEW psp
[1]; // 2
3634 * Bail out if there is no available filter, or if the filter
3635 * contains more than one log.
3637 if (!EventLogFilter
)
3640 EventLogFilter_AddRef(EventLogFilter
);
3642 if (EventLogFilter
->NumOfEventLogs
> 1 ||
3643 EventLogFilter
->EventLogs
[0] == NULL
)
3649 psh
.dwSize
= sizeof(psh
);
3650 psh
.dwFlags
= PSH_PROPSHEETPAGE
/*| PSH_USEICONID */ | PSH_PROPTITLE
| PSH_HASHELP
/*| PSH_NOCONTEXTHELP */ /*| PSH_USECALLBACK */;
3651 psh
.hInstance
= hInstance
;
3652 psh
.hwndParent
= hWndParent
;
3653 // psh.pszIcon = MAKEINTRESOURCEW(IDI_APPICON); // Disabled because it only sets the small icon; the big icon is a stretched version of the small one.
3654 psh
.pszCaption
= EventLogFilter
->EventLogs
[0]->LogName
;
3657 psh
.nPages
= ARRAYSIZE(psp
);
3658 // psh.pfnCallback = PropSheetCallback;
3660 /* Log properties page */
3661 psp
[0].dwSize
= sizeof(psp
[0]);
3662 psp
[0].dwFlags
= PSP_HASHELP
;
3663 psp
[0].hInstance
= hInstance
;
3664 psp
[0].pszTemplate
= MAKEINTRESOURCEW(IDD_LOGPROPERTIES_GENERAL
);
3665 psp
[0].pfnDlgProc
= EventLogPropProc
;
3666 psp
[0].lParam
= (LPARAM
)EventLogFilter
->EventLogs
[0];
3669 /* TODO: Log sources page */
3670 psp
[1].dwSize
= sizeof(psp
[1]);
3671 psp
[1].dwFlags
= PSP_HASHELP
;
3672 psp
[1].hInstance
= hInstance
;
3673 psp
[1].pszTemplate
= MAKEINTRESOURCEW(IDD_GENERAL_PAGE
);
3674 psp
[1].pfnDlgProc
= GeneralPageWndProc
;
3675 psp
[0].lParam
= (LPARAM
)EventLogFilter
->EventLogs
[0];
3678 /* Create the property sheet */
3679 ret
= PropertySheetW(&psh
);
3682 EventLogFilter_Release(EventLogFilter
);
3686 /* Message handler for Event Details dialog */
3687 static HWND hWndDetailsCtrl
= NULL
; // May go into the DWLP_USER
3688 static HWND hWndGrip
= NULL
;
3689 static INT cxMin
, cyMin
; // In window coordinates
3690 static INT cxOld
, cyOld
; // In client coordinates
3693 EventDetails(HWND hDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
3700 INT sbVXSize
, sbHYSize
;
3703 hWndDetailsCtrl
= CreateEventDetailsCtrl(hInst
, hDlg
, lParam
);
3704 if (!hWndDetailsCtrl
)
3707 return (INT_PTR
)TRUE
;
3710 /* Create a size grip if the dialog has a sizing border */
3711 GetClientRect(hDlg
, &rcWnd
);
3712 dwStyle
= GetWindowLongPtrW(hDlg
, GWL_STYLE
);
3713 sbVXSize
= GetSystemMetrics(SM_CXVSCROLL
);
3714 sbHYSize
= GetSystemMetrics(SM_CYHSCROLL
);
3715 if (dwStyle
& WS_THICKFRAME
/* == WS_SIZEBOX */)
3717 hWndGrip
= CreateWindowW(WC_SCROLLBARW
,
3719 WS_CHILD
| WS_VISIBLE
| /**/ WS_CLIPSIBLINGS
| /**/ SBS_SIZEGRIP
| SBS_SIZEBOXBOTTOMRIGHTALIGN
,
3720 rcWnd
.right
- sbVXSize
,
3721 rcWnd
.bottom
- sbHYSize
,
3729 // SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)hWndDetailsCtrl);
3732 * Compute the minimum window size (in window coordinates) by
3733 * adding the widths/heights of the "Help" and "Close" buttons,
3734 * together with the margins, and add some minimal spacing
3735 * between the buttons.
3737 GetWindowRect(hDlg
, &rcWnd
);
3740 GetWindowRect(GetDlgItem(hDlg
, IDHELP
), &rect
);
3741 cxMin
+= (rect
.right
- rect
.left
) + (rect
.left
- rcWnd
.left
); // == (rect.right - rcWnd.left);
3742 cyMin
+= (rect
.bottom
- rect
.top
) + (rcWnd
.bottom
- rect
.bottom
); // == (rcWnd.bottom - rect.top);
3744 GetWindowRect(GetDlgItem(hDlg
, IDOK
), &rect
);
3745 cxMin
+= (rect
.right
- rect
.left
) + (rcWnd
.right
- rect
.right
); // == (rcWnd.right - rect.left);
3746 cyMin
+= (rect
.bottom
- rect
.top
) + (rcWnd
.bottom
- rect
.bottom
); // == (rcWnd.bottom - rect.top);
3749 * Convert the window rect from window to client coordinates
3750 * in order to retrieve the sizes of the left and top margins,
3751 * and add some extra space.
3753 MapWindowPoints(HWND_DESKTOP
/*NULL*/, hDlg
, (LPPOINT
)&rcWnd
, sizeof(RECT
)/sizeof(POINT
));
3755 cxMin
+= -2*rcWnd
.left
; // Minimal spacing between the buttons == 2 * left margin
3756 cyMin
+= -rcWnd
.top
+ 12; // Add some space on top
3758 GetClientRect(hDlg
, &rcWnd
);
3759 cxOld
= rcWnd
.right
- rcWnd
.left
;
3760 cyOld
= rcWnd
.bottom
- rcWnd
.top
;
3762 /* Show event info on dialog control */
3763 SendMessageW(hWndDetailsCtrl
, EVT_DISPLAY
, 0, 0);
3765 // SetWindowPos(hWndDetailsCtrl, NULL,
3767 // (rcWnd.right - rcWnd.left),
3768 // (rcWnd.bottom - rcWnd.top),
3769 // SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
3772 * Hide the placeholder static control and show the event details
3773 * control instead. Note that the placeholder is here so far just
3774 * to get the dimensions right in the dialog resource editor.
3775 * I plan to remove it and use a custom control with a suitable
3776 * window class for it, that would create the event details control
3779 ShowWindow(GetDlgItem(hDlg
, IDC_STATIC
), SW_HIDE
);
3780 ShowWindow(hWndDetailsCtrl
, SW_SHOW
);
3781 return (INT_PTR
)TRUE
;
3785 if (IsWindow(hWndDetailsCtrl
))
3786 DestroyWindow(hWndDetailsCtrl
);
3787 hWndDetailsCtrl
= NULL
;
3788 return (INT_PTR
)TRUE
;
3791 switch (LOWORD(wParam
))
3795 EndDialog(hDlg
, LOWORD(wParam
));
3796 return (INT_PTR
)TRUE
;
3800 L
"Help not implemented yet!",
3802 MB_OK
| MB_ICONINFORMATION
);
3803 return (INT_PTR
)TRUE
;
3811 if (((HWND
)wParam
== hWndGrip
) && (LOWORD(lParam
) == HTCLIENT
))
3813 SetCursor(LoadCursorW(NULL
, IDC_SIZENWSE
));
3814 SetWindowLongPtrW(hDlg
, DWLP_MSGRESULT
, TRUE
);
3815 return (INT_PTR
)TRUE
;
3821 /* Forbid resizing the dialog smaller than its minimal size */
3822 PRECT dragRect
= (PRECT
)lParam
;
3824 if ((wParam
== WMSZ_LEFT
) || (wParam
== WMSZ_TOPLEFT
) || (wParam
== WMSZ_BOTTOMLEFT
))
3826 if (dragRect
->right
- dragRect
->left
< cxMin
)
3827 dragRect
->left
= dragRect
->right
- cxMin
;
3830 if ((wParam
== WMSZ_RIGHT
) || (wParam
== WMSZ_TOPRIGHT
) || (wParam
== WMSZ_BOTTOMRIGHT
))
3832 if (dragRect
->right
- dragRect
->left
< cxMin
)
3833 dragRect
->right
= dragRect
->left
+ cxMin
;
3836 if ((wParam
== WMSZ_TOP
) || (wParam
== WMSZ_TOPLEFT
) || (wParam
== WMSZ_TOPRIGHT
))
3838 if (dragRect
->bottom
- dragRect
->top
< cyMin
)
3839 dragRect
->top
= dragRect
->bottom
- cyMin
;
3842 if ((wParam
== WMSZ_BOTTOM
) || (wParam
== WMSZ_BOTTOMLEFT
) || (wParam
== WMSZ_BOTTOMRIGHT
))
3844 if (dragRect
->bottom
- dragRect
->top
< cyMin
)
3845 dragRect
->bottom
= dragRect
->top
+ cyMin
;
3848 SetWindowLongPtrW(hDlg
, DWLP_MSGRESULT
, TRUE
);
3849 return (INT_PTR
)TRUE
;
3854 INT cx
= LOWORD(lParam
);
3855 INT cy
= HIWORD(lParam
);
3861 hdwp
= BeginDeferWindowPos(4);
3863 /* Resize the event details control window */
3865 hItemWnd
= hWndDetailsCtrl
;
3866 GetWindowRect(hItemWnd
, &rect
);
3867 MapWindowPoints(HWND_DESKTOP
/*NULL*/, hDlg
, (LPPOINT
)&rect
, sizeof(RECT
)/sizeof(POINT
));
3870 hdwp
= DeferWindowPos(hdwp
,
3874 (rect
.right
- rect
.left
) + (cx
- cxOld
),
3875 (rect
.bottom
- rect
.top
) + (cy
- cyOld
),
3876 SWP_NOMOVE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
3878 /* Move the buttons */
3880 hItemWnd
= GetDlgItem(hDlg
, IDHELP
);
3881 GetWindowRect(hItemWnd
, &rect
);
3882 MapWindowPoints(HWND_DESKTOP
/*NULL*/, hDlg
, (LPPOINT
)&rect
, sizeof(RECT
)/sizeof(POINT
));
3885 hdwp
= DeferWindowPos(hdwp
,
3889 rect
.top
+ (cy
- cyOld
),
3891 SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
3893 hItemWnd
= GetDlgItem(hDlg
, IDOK
);
3894 GetWindowRect(hItemWnd
, &rect
);
3895 MapWindowPoints(HWND_DESKTOP
/*NULL*/, hDlg
, (LPPOINT
)&rect
, sizeof(RECT
)/sizeof(POINT
));
3898 hdwp
= DeferWindowPos(hdwp
,
3901 rect
.left
+ (cx
- cxOld
),
3902 rect
.top
+ (cy
- cyOld
),
3904 SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
3906 /* Move the size grip */
3907 if (hWndGrip
&& hdwp
)
3909 GetWindowRect(hWndGrip
, &rect
);
3910 MapWindowPoints(HWND_DESKTOP
/*NULL*/, hDlg
, (LPPOINT
)&rect
, sizeof(RECT
)/sizeof(POINT
));
3912 hdwp
= DeferWindowPos(hdwp
,
3915 rect
.left
+ (cx
- cxOld
),
3916 rect
.top
+ (cy
- cyOld
),
3918 SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
3922 EndDeferWindowPos(hdwp
);
3924 /* Hide the size grip if we are in maximized mode */
3926 ShowWindow(hWndGrip
, (wParam
== SIZE_MAXIMIZED
) ? SW_HIDE
: SW_SHOW
);
3931 SetWindowLongPtrW(hDlg
, DWLP_MSGRESULT
, 0);
3932 return (INT_PTR
)TRUE
;
3936 return (INT_PTR
)FALSE
;