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 EVENTVWR_WNDCLASS
= L
"EVENTVWR"; /* The main window class name */
41 static const LPCWSTR 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 /* Stop the enumerator thread */
446 SetEvent(hStartStopEnumEvent
);
447 WaitForSingleObject(hThread
, INFINITE
);
448 CloseHandle(hThread
);
450 /* Free the filters list and the event logs list */
457 CloseHandle(hStartEnumEvent
);
458 if (hStartStopEnumEvent
)
459 CloseHandle(hStartStopEnumEvent
);
463 if (lpszzUserLogsToLoad
)
466 HeapFree(GetProcessHeap(), 0, lpszzUserLogsToLoad
);
467 lpszzUserLogsToLoad
= NULL
;
471 HeapFree(GetProcessHeap(), 0, lpComputerName
);
472 lpComputerName
= NULL
;
474 FreeLibrary(hRichEdit
);
476 return (int)msg
.wParam
;
480 /* GENERIC HELPER FUNCTIONS ***************************************************/
483 EventTimeToSystemTime(IN DWORD EventTime
,
484 OUT PSYSTEMTIME pSystemTime
)
486 SYSTEMTIME st1970
= { 1970, 1, 0, 1, 0, 0, 0, 0 };
494 uUCT
.ft
.dwHighDateTime
= 0;
495 uUCT
.ft
.dwLowDateTime
= EventTime
;
496 SystemTimeToFileTime(&st1970
, &u1970
.ft
);
497 uUCT
.ll
= uUCT
.ll
* 10000000 + u1970
.ll
;
498 FileTimeToLocalFileTime(&uUCT
.ft
, &ftLocal
);
499 FileTimeToSystemTime(&ftLocal
, pSystemTime
);
503 * This function takes in entry a path to a single DLL, in which
504 * the message string of ID dwMessageId has to be searched.
505 * The other parameters are similar to those of the FormatMessageW API.
508 GetMessageStringFromDll(
509 IN LPCWSTR lpMessageDll
,
510 IN DWORD dwFlags
, // If we always use the same flags, just remove this param...
511 IN DWORD dwMessageId
,
513 IN
va_list* Arguments OPTIONAL
)
517 LPWSTR lpMsgBuf
= NULL
;
519 hLibrary
= LoadLibraryExW(lpMessageDll
, NULL
,
520 /* LOAD_LIBRARY_AS_IMAGE_RESOURCE | */ LOAD_LIBRARY_AS_DATAFILE
);
521 if (hLibrary
== NULL
)
524 /* Sanitize dwFlags */
525 dwFlags
&= ~FORMAT_MESSAGE_FROM_STRING
;
526 dwFlags
|= FORMAT_MESSAGE_FROM_HMODULE
;
531 * Retrieve the message string without appending extra newlines.
532 * Wrap in SEH to protect from invalid string parameters.
536 dwLength
= FormatMessageW(dwFlags
,
537 /* FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
538 FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, */
546 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
551 * An exception occurred while calling FormatMessage, this is usually
552 * the sign that a parameter was invalid, either 'lpMsgBuf' was NULL
553 * but we did not pass the flag FORMAT_MESSAGE_ALLOCATE_BUFFER, or the
554 * array pointer 'Arguments' was NULL or did not contain enough elements,
555 * and we did not pass the flag FORMAT_MESSAGE_IGNORE_INSERTS, and the
556 * message string expected too many inserts.
557 * In this last case only, we can call again FormatMessage but ignore
558 * explicitly the inserts. The string that we will return to the user
559 * will not be pre-formatted.
561 if (((dwFlags
& FORMAT_MESSAGE_ALLOCATE_BUFFER
) || lpMsgBuf
) &&
562 !(dwFlags
& FORMAT_MESSAGE_IGNORE_INSERTS
))
564 /* Remove any possible harmful flags and always ignore inserts */
565 dwFlags
&= ~FORMAT_MESSAGE_ARGUMENT_ARRAY
;
566 dwFlags
|= FORMAT_MESSAGE_IGNORE_INSERTS
;
568 /* If this call also throws an exception, we are really dead */
569 dwLength
= FormatMessageW(dwFlags
,
575 NULL
/* Arguments */);
582 FreeLibrary(hLibrary
);
588 ASSERT(lpMsgBuf
== NULL
);
600 * This function takes in entry a comma-separated list of DLLs, in which
601 * the message string of ID dwMessageId has to be searched.
602 * The other parameters are similar to those of the FormatMessageW API.
605 GetMessageStringFromDllList(
606 IN LPCWSTR lpMessageDllList
,
607 IN DWORD dwFlags
, // If we always use the same flags, just remove this param...
608 IN DWORD dwMessageId
,
610 IN
va_list* Arguments OPTIONAL
)
612 BOOL Success
= FALSE
;
614 LPWSTR szMessageDllList
;
616 LPWSTR lpMsgBuf
= NULL
;
618 /* Allocate a local buffer for the DLL list that can be tokenized */
619 // TODO: Optimize that!! Maybe we can cleverly use lpMessageDllList in read/write mode
620 // and cleverly temporarily replace the ';' by UNICODE_NULL, do our job, then reverse the change.
621 cbLength
= (wcslen(lpMessageDllList
) + 1) * sizeof(WCHAR
);
622 szMessageDllList
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, cbLength
);
623 if (!szMessageDllList
)
625 RtlCopyMemory(szMessageDllList
, lpMessageDllList
, cbLength
);
627 /* Loop through the list of message DLLs */
628 szDll
= wcstok(szMessageDllList
, EVENT_DLL_SEPARATOR
);
629 while ((szDll
!= NULL
) && !Success
)
631 // Uses LANG_USER_DEFAULT
632 lpMsgBuf
= GetMessageStringFromDll(szDll
,
639 /* The ID was found and the message was formatted */
645 * The DLL could not be loaded, or the message could not be found,
646 * try the next DLL, if any.
648 szDll
= wcstok(NULL
, EVENT_DLL_SEPARATOR
);
651 HeapFree(GetProcessHeap(), 0, szMessageDllList
);
659 LPWSTR pStartingAddress
; // Pointer to the beginning of a parameter string in pMessage
660 LPWSTR pEndingAddress
; // Pointer to the end of a parameter string in pMessage
661 DWORD pParameterID
; // Parameter identifier found in pMessage
662 LPWSTR pParameter
; // Actual parameter string
663 } param_strings_format_data
;
666 ApplyParameterStringsToMessage(
667 IN LPCWSTR lpMessageDllList
,
668 IN BOOL bMessagePreFormatted
,
669 IN CONST LPCWSTR pMessage
,
670 OUT LPWSTR
* pFinalMessage
)
673 * This code is heavily adapted from the MSDN example:
674 * https://msdn.microsoft.com/en-us/library/windows/desktop/bb427356.aspx
678 DWORD Status
= ERROR_SUCCESS
;
679 DWORD dwParamCount
= 0; // Number of insertion strings found in pMessage
680 size_t cchBuffer
= 0; // Size of the buffer in characters
681 size_t cchParams
= 0; // Number of characters in all the parameter strings
684 param_strings_format_data
* pParamData
= NULL
; // Array of pointers holding information about each parameter string in pMessage
685 LPWSTR pTempMessage
= (LPWSTR
)pMessage
;
686 LPWSTR pTempFinalMessage
= NULL
;
688 *pFinalMessage
= NULL
;
690 /* Determine the number of parameter insertion strings in pMessage */
691 if (bMessagePreFormatted
)
693 while ((pTempMessage
= wcschr(pTempMessage
, L
'%')))
696 if (iswdigit(*pTempMessage
))
699 while (iswdigit(*++pTempMessage
)) ;
705 while ((pTempMessage
= wcsstr(pTempMessage
, L
"%%")))
708 if (iswdigit(*pTempMessage
))
711 while (iswdigit(*++pTempMessage
)) ;
716 /* If there are no parameter insertion strings in pMessage, just return */
717 if (dwParamCount
== 0)
719 // *pFinalMessage = NULL;
723 /* Allocate the array of parameter string format data */
724 pParamData
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, dwParamCount
* sizeof(param_strings_format_data
));
727 Status
= ERROR_OUTOFMEMORY
;
732 * Retrieve each parameter in pMessage and the beginning and end of the
733 * insertion string, as well as the message identifier of the parameter.
735 pTempMessage
= (LPWSTR
)pMessage
;
736 if (bMessagePreFormatted
)
738 while ((pTempMessage
= wcschr(pTempMessage
, L
'%')) && (i
< dwParamCount
))
741 if (iswdigit(*pTempMessage
))
743 pParamData
[i
].pStartingAddress
= pTempMessage
-1;
744 pParamData
[i
].pParameterID
= (DWORD
)_wtol(pTempMessage
);
746 while (iswdigit(*++pTempMessage
)) ;
748 pParamData
[i
].pEndingAddress
= pTempMessage
;
755 while ((pTempMessage
= wcsstr(pTempMessage
, L
"%%")) && (i
< dwParamCount
))
758 if (iswdigit(*pTempMessage
))
760 pParamData
[i
].pStartingAddress
= pTempMessage
-2;
761 pParamData
[i
].pParameterID
= (DWORD
)_wtol(pTempMessage
);
763 while (iswdigit(*++pTempMessage
)) ;
765 pParamData
[i
].pEndingAddress
= pTempMessage
;
771 /* Retrieve each parameter string */
772 for (i
= 0; i
< dwParamCount
; i
++)
774 // pParamData[i].pParameter = GetMessageString(pParamData[i].pParameterID, 0, NULL);
775 pParamData
[i
].pParameter
=
776 GetMessageStringFromDllList(lpMessageDllList
,
777 FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_HMODULE
|
778 FORMAT_MESSAGE_IGNORE_INSERTS
| FORMAT_MESSAGE_MAX_WIDTH_MASK
,
779 pParamData
[i
].pParameterID
,
781 if (!pParamData
[i
].pParameter
)
783 /* Skip the insertion string */
787 cchParams
+= wcslen(pParamData
[i
].pParameter
);
791 * Allocate the final message buffer, the size of which is based on the
792 * length of the original message and the length of each parameter string.
794 cchBuffer
= wcslen(pMessage
) + cchParams
+ 1;
795 *pFinalMessage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, cchBuffer
* sizeof(WCHAR
));
798 Status
= ERROR_OUTOFMEMORY
;
802 pTempFinalMessage
= *pFinalMessage
;
804 /* Build the final message string */
805 pTempMessage
= (LPWSTR
)pMessage
;
806 for (i
= 0; i
< dwParamCount
; i
++)
808 /* Append the segment from pMessage */
809 cch
= pParamData
[i
].pStartingAddress
- pTempMessage
;
810 StringCchCopyNW(pTempFinalMessage
, cchBuffer
, pTempMessage
, cch
);
811 pTempMessage
= pParamData
[i
].pEndingAddress
;
813 pTempFinalMessage
+= cch
;
815 /* Append the parameter string */
816 if (pParamData
[i
].pParameter
)
818 StringCchCopyW(pTempFinalMessage
, cchBuffer
, pParamData
[i
].pParameter
);
819 cch
= wcslen(pParamData
[i
].pParameter
); // pTempFinalMessage
824 * We failed to retrieve the parameter string before, so just
825 * place back the original string placeholder.
827 cch
= pParamData
[i
].pEndingAddress
/* == pTempMessage */ - pParamData
[i
].pStartingAddress
;
828 StringCchCopyNW(pTempFinalMessage
, cchBuffer
, pParamData
[i
].pStartingAddress
, cch
);
829 // cch = wcslen(pTempFinalMessage);
832 pTempFinalMessage
+= cch
;
835 /* Append the last segment from pMessage */
836 StringCchCopyW(pTempFinalMessage
, cchBuffer
, pTempMessage
);
840 // if (Status != ERROR_SUCCESS)
841 // *pFinalMessage = NULL;
845 for (i
= 0; i
< dwParamCount
; i
++)
847 if (pParamData
[i
].pParameter
)
848 LocalFree(pParamData
[i
].pParameter
);
851 HeapFree(GetProcessHeap(), 0, pParamData
);
859 * The following functions were adapted from
860 * shell32!dialogs/filedefext.cpp:``SH_...'' functions.
864 FormatInteger(LONGLONG Num
, LPWSTR pwszResult
, UINT cchResultMax
)
867 WCHAR wszDecimalSep
[8], wszThousandSep
[8];
869 WCHAR wszGrouping
[12];
874 // Print the number in uniform mode
875 swprintf(wszNumber
, L
"%I64u", Num
);
877 // Get system strings for decimal and thousand separators.
878 GetLocaleInfoW(LOCALE_USER_DEFAULT
, LOCALE_SDECIMAL
, wszDecimalSep
, _countof(wszDecimalSep
));
879 GetLocaleInfoW(LOCALE_USER_DEFAULT
, LOCALE_STHOUSAND
, wszThousandSep
, _countof(wszThousandSep
));
881 // Initialize format for printing the number in bytes
882 ZeroMemory(&nf
, sizeof(nf
));
883 nf
.lpDecimalSep
= wszDecimalSep
;
884 nf
.lpThousandSep
= wszThousandSep
;
886 // Get system string for groups separator
887 cchGrouping
= GetLocaleInfoW(LOCALE_USER_DEFAULT
,
890 _countof(wszGrouping
));
892 // Convert grouping specs from string to integer
893 for (i
= 0; i
< cchGrouping
; i
++)
895 WCHAR wch
= wszGrouping
[i
];
897 if (wch
>= L
'0' && wch
<= L
'9')
898 nf
.Grouping
= nf
.Grouping
* 10 + (wch
- L
'0');
899 else if (wch
!= L
';')
903 if ((nf
.Grouping
% 10) == 0)
909 cchResult
= GetNumberFormatW(LOCALE_USER_DEFAULT
,
919 // GetNumberFormatW returns number of characters including UNICODE_NULL
920 return cchResult
- 1;
924 FormatByteSize(LONGLONG cbSize
, LPWSTR pwszResult
, UINT cchResultMax
)
930 /* Write formated bytes count */
931 cchWritten
= FormatInteger(cbSize
, pwszResult
, cchResultMax
);
935 /* Copy " bytes" to buffer */
936 pwszEnd
= pwszResult
+ cchWritten
;
937 cchRemaining
= cchResultMax
- cchWritten
;
938 StringCchCopyExW(pwszEnd
, cchRemaining
, L
" ", &pwszEnd
, &cchRemaining
, 0);
939 cchWritten
= LoadStringW(hInst
, IDS_BYTES_FORMAT
, pwszEnd
, cchRemaining
);
940 cchRemaining
-= cchWritten
;
942 return cchResultMax
- cchRemaining
;
946 FormatFileSizeWithBytes(const PULARGE_INTEGER lpQwSize
, LPWSTR pwszResult
, UINT cchResultMax
)
952 /* Format bytes in KBs, MBs etc */
953 if (StrFormatByteSizeW(lpQwSize
->QuadPart
, pwszResult
, cchResultMax
) == NULL
)
956 /* If there is less bytes than 1KB, we have nothing to do */
957 if (lpQwSize
->QuadPart
< 1024)
960 /* Concatenate " (" */
961 cchWritten
= wcslen(pwszResult
);
962 pwszEnd
= pwszResult
+ cchWritten
;
963 cchRemaining
= cchResultMax
- cchWritten
;
964 StringCchCopyExW(pwszEnd
, cchRemaining
, L
" (", &pwszEnd
, &cchRemaining
, 0);
966 /* Write formated bytes count */
967 cchWritten
= FormatByteSize(lpQwSize
->QuadPart
, pwszEnd
, cchRemaining
);
968 pwszEnd
+= cchWritten
;
969 cchRemaining
-= cchWritten
;
971 /* Copy ")" to the buffer */
972 StringCchCopyW(pwszEnd
, cchRemaining
, L
")");
977 /* Adapted from shell32!dialogs/filedefext.cpp:``CFileDefExt::GetFileTimeString'' */
979 GetFileTimeString(LPFILETIME lpFileTime
, LPWSTR pwszResult
, UINT cchResult
)
984 size_t cchRemaining
= cchResult
;
985 LPWSTR pwszEnd
= pwszResult
;
987 if (!FileTimeToLocalFileTime(lpFileTime
, &ft
) || !FileTimeToSystemTime(&ft
, &st
))
990 cchWritten
= GetDateFormatW(LOCALE_USER_DEFAULT
, DATE_LONGDATE
, &st
, NULL
, pwszEnd
, cchRemaining
);
992 --cchWritten
; // GetDateFormatW returns count with terminating zero
994 // ERR("GetDateFormatW failed\n");
996 cchRemaining
-= cchWritten
;
997 pwszEnd
+= cchWritten
;
999 StringCchCopyExW(pwszEnd
, cchRemaining
, L
", ", &pwszEnd
, &cchRemaining
, 0);
1001 cchWritten
= GetTimeFormatW(LOCALE_USER_DEFAULT
, 0, &st
, NULL
, pwszEnd
, cchRemaining
);
1003 --cchWritten
; // GetTimeFormatW returns count with terminating zero
1005 // ERR("GetTimeFormatW failed\n");
1012 TreeViewAddItem(IN HWND hTreeView
,
1013 IN HTREEITEM hParent
,
1016 IN INT SelectedImage
,
1019 TV_INSERTSTRUCTW Insert
;
1021 ZeroMemory(&Insert
, sizeof(Insert
));
1023 Insert
.item
.mask
= TVIF_TEXT
| TVIF_PARAM
| TVIF_IMAGE
| TVIF_SELECTEDIMAGE
;
1024 Insert
.hInsertAfter
= TVI_LAST
;
1025 Insert
.hParent
= hParent
;
1026 Insert
.item
.pszText
= lpText
;
1027 Insert
.item
.iImage
= Image
;
1028 Insert
.item
.iSelectedImage
= SelectedImage
;
1029 Insert
.item
.lParam
= lParam
;
1031 Insert
.item
.mask
|= TVIF_STATE
;
1032 Insert
.item
.stateMask
= TVIS_OVERLAYMASK
;
1033 Insert
.item
.state
= INDEXTOOVERLAYMASK(1);
1035 return TreeView_InsertItem(hTreeView
, &Insert
);
1039 /* LOG HELPER FUNCTIONS *******************************************************/
1042 AllocEventLog(IN PCWSTR ComputerName OPTIONAL
,
1049 /* Allocate a new event log entry */
1050 EventLog
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*EventLog
));
1054 /* Allocate the computer name string (optional) and copy it */
1057 cchName
= wcslen(ComputerName
) + 1;
1058 EventLog
->ComputerName
= HeapAlloc(GetProcessHeap(), 0, cchName
* sizeof(WCHAR
));
1059 if (EventLog
->ComputerName
)
1060 StringCchCopyW(EventLog
->ComputerName
, cchName
, ComputerName
);
1063 /* Allocate the event log name string and copy it */
1064 cchName
= wcslen(LogName
) + 1;
1065 EventLog
->LogName
= HeapAlloc(GetProcessHeap(), 0, cchName
* sizeof(WCHAR
));
1066 if (!EventLog
->LogName
)
1068 if (EventLog
->ComputerName
)
1069 HeapFree(GetProcessHeap(), 0, EventLog
->ComputerName
);
1070 HeapFree(GetProcessHeap(), 0, EventLog
);
1073 StringCchCopyW(EventLog
->LogName
, cchName
, LogName
);
1075 EventLog
->Permanent
= Permanent
;
1081 EventLog_Free(IN PEVENTLOG EventLog
)
1083 if (EventLog
->LogName
)
1084 HeapFree(GetProcessHeap(), 0, EventLog
->LogName
);
1086 if (EventLog
->ComputerName
)
1087 HeapFree(GetProcessHeap(), 0, EventLog
->ComputerName
);
1089 if (EventLog
->FileName
)
1090 HeapFree(GetProcessHeap(), 0, EventLog
->FileName
);
1092 HeapFree(GetProcessHeap(), 0, EventLog
);
1097 AllocAndCopyMultiStr(IN PCWSTR MultiStr OPTIONAL
)
1105 pStr
= (PWSTR
)MultiStr
;
1106 while (*pStr
) pStr
+= (wcslen(pStr
) + 1);
1107 Length
= MultiStr
- pStr
+ 2;
1109 pStr
= HeapAlloc(GetProcessHeap(), 0, Length
* sizeof(WCHAR
));
1110 // NOTE: If we failed allocating the string, then fall back into no filter!
1112 RtlCopyMemory(pStr
, MultiStr
, Length
* sizeof(WCHAR
));
1118 AllocEventLogFilter(// IN PCWSTR FilterName,
1119 IN BOOL Information
,
1122 IN BOOL AuditSuccess
,
1123 IN BOOL AuditFailure
,
1124 IN PCWSTR Sources OPTIONAL
,
1125 IN PCWSTR Users OPTIONAL
,
1126 IN PCWSTR ComputerNames OPTIONAL
,
1127 IN ULONG NumOfEventLogs
,
1128 IN PEVENTLOG
* EventLogs
)
1130 PEVENTLOGFILTER EventLogFilter
;
1132 /* Allocate a new event log filter entry, big enough to accommodate the list of logs */
1133 EventLogFilter
= HeapAlloc(GetProcessHeap(),
1135 FIELD_OFFSET(EVENTLOGFILTER
, EventLogs
[NumOfEventLogs
]));
1136 if (!EventLogFilter
)
1139 EventLogFilter
->Information
= Information
;
1140 EventLogFilter
->Warning
= Warning
;
1141 EventLogFilter
->Error
= Error
;
1142 EventLogFilter
->AuditSuccess
= AuditSuccess
;
1143 EventLogFilter
->AuditFailure
= AuditFailure
;
1145 /* Allocate and copy the sources, users, and computers multi-strings */
1146 EventLogFilter
->Sources
= AllocAndCopyMultiStr(Sources
);
1147 EventLogFilter
->Users
= AllocAndCopyMultiStr(Users
);
1148 EventLogFilter
->ComputerNames
= AllocAndCopyMultiStr(ComputerNames
);
1150 /* Copy the list of event logs */
1151 EventLogFilter
->NumOfEventLogs
= NumOfEventLogs
;
1152 RtlCopyMemory(EventLogFilter
->EventLogs
, EventLogs
, NumOfEventLogs
* sizeof(PEVENTLOG
));
1154 /* Initialize the filter reference count */
1155 EventLogFilter
->ReferenceCount
= 1;
1157 return EventLogFilter
;
1161 EventLogFilter_Free(IN PEVENTLOGFILTER EventLogFilter
)
1163 if (EventLogFilter
->Sources
)
1164 HeapFree(GetProcessHeap(), 0, EventLogFilter
->Sources
);
1166 if (EventLogFilter
->Users
)
1167 HeapFree(GetProcessHeap(), 0, EventLogFilter
->Users
);
1169 if (EventLogFilter
->ComputerNames
)
1170 HeapFree(GetProcessHeap(), 0, EventLogFilter
->ComputerNames
);
1172 HeapFree(GetProcessHeap(), 0, EventLogFilter
);
1175 LONG
EventLogFilter_AddRef(IN PEVENTLOGFILTER EventLogFilter
)
1177 ASSERT(EventLogFilter
);
1178 return InterlockedIncrement(&EventLogFilter
->ReferenceCount
);
1181 LONG
EventLogFilter_Release(IN PEVENTLOGFILTER EventLogFilter
)
1185 ASSERT(EventLogFilter
);
1187 /* When the reference count reaches zero, delete the filter */
1188 RefCount
= InterlockedDecrement(&EventLogFilter
->ReferenceCount
);
1191 /* Remove the filter from the list */
1192 /** RemoveEntryList(&EventLogFilter->ListEntry); **/
1193 EventLogFilter_Free(EventLogFilter
);
1206 c
= s
+ wcslen(s
) - 1;
1207 while (c
>= s
&& iswspace(*c
))
1214 GetEventMessageFileDLL(IN LPCWSTR lpLogName
,
1215 IN LPCWSTR SourceName
,
1216 IN LPCWSTR EntryName
,
1217 OUT PWCHAR lpModuleName
) // TODO: Add IN DWORD BufLen
1219 BOOL Success
= FALSE
;
1221 DWORD dwType
, dwSize
;
1222 WCHAR szModuleName
[MAX_PATH
];
1223 WCHAR szKeyName
[MAX_PATH
];
1224 HKEY hLogKey
= NULL
;
1225 HKEY hSourceKey
= NULL
;
1227 StringCbCopyW(szKeyName
, sizeof(szKeyName
), EVENTLOG_BASE_KEY
);
1228 StringCbCatW(szKeyName
, sizeof(szKeyName
), lpLogName
);
1230 Result
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
,
1235 if (Result
!= ERROR_SUCCESS
)
1238 Result
= RegOpenKeyExW(hLogKey
,
1243 if (Result
!= ERROR_SUCCESS
)
1245 RegCloseKey(hLogKey
);
1249 dwSize
= sizeof(szModuleName
);
1250 Result
= RegQueryValueExW(hSourceKey
,
1254 (LPBYTE
)szModuleName
,
1256 if ((Result
!= ERROR_SUCCESS
) || (dwType
!= REG_EXPAND_SZ
&& dwType
!= REG_SZ
))
1258 szModuleName
[0] = UNICODE_NULL
;
1262 /* NULL-terminate the string and expand it */
1263 szModuleName
[dwSize
/ sizeof(WCHAR
) - 1] = UNICODE_NULL
;
1264 ExpandEnvironmentStringsW(szModuleName
, lpModuleName
, ARRAYSIZE(szModuleName
));
1268 RegCloseKey(hSourceKey
);
1269 RegCloseKey(hLogKey
);
1275 GetEventCategory(IN LPCWSTR KeyName
,
1276 IN LPCWSTR SourceName
,
1277 IN PEVENTLOGRECORD pevlr
,
1278 OUT PWCHAR CategoryName
) // TODO: Add IN DWORD BufLen
1280 BOOL Success
= FALSE
;
1281 WCHAR szMessageDLL
[MAX_PATH
];
1282 LPWSTR lpMsgBuf
= NULL
;
1284 if (!GetEventMessageFileDLL(KeyName
, SourceName
, EVENT_CATEGORY_MESSAGE_FILE
, szMessageDLL
))
1287 /* Retrieve the message string without appending extra newlines */
1289 GetMessageStringFromDllList(szMessageDLL
,
1290 FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_HMODULE
|
1291 FORMAT_MESSAGE_IGNORE_INSERTS
| FORMAT_MESSAGE_MAX_WIDTH_MASK
,
1292 pevlr
->EventCategory
,
1293 EVENT_MESSAGE_FILE_BUFFER
,
1297 /* Trim the string */
1298 TrimNulls(lpMsgBuf
);
1300 /* Copy the category name */
1301 StringCchCopyW(CategoryName
, MAX_PATH
, lpMsgBuf
);
1303 /* Free the buffer allocated by FormatMessage */
1304 LocalFree(lpMsgBuf
);
1306 /* The ID was found and the message was formatted */
1313 if (pevlr
->EventCategory
!= 0)
1315 StringCchPrintfW(CategoryName
, MAX_PATH
, L
"(%lu)", pevlr
->EventCategory
);
1324 BOOL
// NOTE: Used by evtdetctl.c
1325 GetEventMessage(IN LPCWSTR KeyName
,
1326 IN LPCWSTR SourceName
,
1327 IN PEVENTLOGRECORD pevlr
,
1328 OUT PWCHAR EventText
) // TODO: Add IN DWORD BufLen
1330 BOOL Success
= FALSE
;
1333 WCHAR SourceModuleName
[1024];
1334 WCHAR ParameterModuleName
[1024];
1335 BOOL IsParamModNameCached
= FALSE
;
1336 LPWSTR lpMsgBuf
= NULL
;
1337 LPWSTR szStringArray
, szMessage
;
1338 LPWSTR
*szArguments
;
1340 /* Get the event string array */
1341 szStringArray
= (LPWSTR
)((LPBYTE
)pevlr
+ pevlr
->StringOffset
);
1343 /* NOTE: GetEventMessageFileDLL can return a comma-separated list of DLLs */
1344 if (!GetEventMessageFileDLL(KeyName
, SourceName
, EVENT_MESSAGE_FILE
, SourceModuleName
))
1347 /* Allocate space for insertion strings */
1348 szArguments
= HeapAlloc(GetProcessHeap(), 0, pevlr
->NumStrings
* sizeof(LPVOID
));
1352 if (!IsParamModNameCached
)
1354 /* Now that the parameter file list is loaded, no need to reload it at the next run! */
1355 IsParamModNameCached
= GetEventMessageFileDLL(KeyName
, SourceName
, EVENT_PARAMETER_MESSAGE_FILE
, ParameterModuleName
);
1356 // FIXME: If the string loading failed the first time, no need to retry it just after???
1359 if (IsParamModNameCached
)
1361 /* Not yet support for reading messages from parameter message DLL */
1364 szMessage
= szStringArray
;
1367 * We do some hackish preformatting of the cached event strings...
1368 * That's because after we pass the string to FormatMessage
1369 * (via GetMessageStringFromDllList) with the FORMAT_MESSAGE_ARGUMENT_ARRAY
1370 * flag, instead of ignoring the insertion parameters and do the formatting
1371 * by ourselves. Therefore, the resulting string should have the parameter
1372 * string placeholders starting with a single '%' instead of a mix of one
1375 /* HACK part 1: Compute the full length of the string array */
1377 for (i
= 0; i
< pevlr
->NumStrings
; i
++)
1379 szMessage
+= wcslen(szMessage
) + 1;
1381 cch
= szMessage
- szStringArray
;
1383 /* HACK part 2: Now do the HACK proper! */
1384 szMessage
= szStringArray
;
1385 for (i
= 0; i
< pevlr
->NumStrings
; i
++)
1387 lpMsgBuf
= szMessage
;
1388 while ((lpMsgBuf
= wcsstr(lpMsgBuf
, L
"%%")))
1390 if (iswdigit(lpMsgBuf
[2]))
1392 RtlMoveMemory(lpMsgBuf
, lpMsgBuf
+1, ((szStringArray
+ cch
) - lpMsgBuf
- 1) * sizeof(WCHAR
));
1396 szArguments
[i
] = szMessage
;
1397 szMessage
+= wcslen(szMessage
) + 1;
1400 /* Retrieve the message string without appending extra newlines */
1402 GetMessageStringFromDllList(SourceModuleName
,
1403 FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_HMODULE
|
1404 FORMAT_MESSAGE_ARGUMENT_ARRAY
| FORMAT_MESSAGE_MAX_WIDTH_MASK
,
1407 (va_list*)szArguments
);
1410 /* Trim the string */
1411 TrimNulls(lpMsgBuf
);
1414 Success
= (ApplyParameterStringsToMessage(ParameterModuleName
,
1417 &szMessage
) == ERROR_SUCCESS
);
1418 if (Success
&& szMessage
)
1420 /* Free the buffer allocated by FormatMessage */
1421 LocalFree(lpMsgBuf
);
1422 lpMsgBuf
= szMessage
;
1425 /* Copy the event text */
1426 StringCchCopyW(EventText
, EVENT_MESSAGE_EVENTTEXT_BUFFER
, lpMsgBuf
);
1428 /* Free the buffer allocated by FormatMessage */
1429 LocalFree(lpMsgBuf
);
1432 HeapFree(GetProcessHeap(), 0, szArguments
);
1437 /* Get a read-only pointer to the "event-not-found" string */
1438 lpMsgBuf
= HeapAlloc(GetProcessHeap(), 0, EVENT_MESSAGE_EVENTTEXT_BUFFER
* sizeof(WCHAR
));
1439 LoadStringW(hInst
, IDS_EVENTSTRINGIDNOTFOUND
, lpMsgBuf
, EVENT_MESSAGE_EVENTTEXT_BUFFER
);
1440 StringCchPrintfW(EventText
, EVENT_MESSAGE_EVENTTEXT_BUFFER
, lpMsgBuf
, (pevlr
->EventID
& 0xFFFF), SourceName
);
1442 /* Append the strings */
1443 szMessage
= szStringArray
;
1444 for (i
= 0; i
< pevlr
->NumStrings
; i
++)
1446 StringCchCatW(EventText
, EVENT_MESSAGE_EVENTTEXT_BUFFER
, szMessage
);
1447 StringCchCatW(EventText
, EVENT_MESSAGE_EVENTTEXT_BUFFER
, L
"\n");
1448 szMessage
+= wcslen(szMessage
) + 1;
1456 GetEventType(IN WORD dwEventType
,
1457 OUT PWCHAR eventTypeText
) // TODO: Add IN DWORD BufLen
1459 switch (dwEventType
)
1461 case EVENTLOG_ERROR_TYPE
:
1462 LoadStringW(hInst
, IDS_EVENTLOG_ERROR_TYPE
, eventTypeText
, MAX_LOADSTRING
);
1464 case EVENTLOG_WARNING_TYPE
:
1465 LoadStringW(hInst
, IDS_EVENTLOG_WARNING_TYPE
, eventTypeText
, MAX_LOADSTRING
);
1467 case EVENTLOG_INFORMATION_TYPE
:
1468 LoadStringW(hInst
, IDS_EVENTLOG_INFORMATION_TYPE
, eventTypeText
, MAX_LOADSTRING
);
1470 case EVENTLOG_SUCCESS
:
1471 LoadStringW(hInst
, IDS_EVENTLOG_SUCCESS
, eventTypeText
, MAX_LOADSTRING
);
1473 case EVENTLOG_AUDIT_SUCCESS
:
1474 LoadStringW(hInst
, IDS_EVENTLOG_AUDIT_SUCCESS
, eventTypeText
, MAX_LOADSTRING
);
1476 case EVENTLOG_AUDIT_FAILURE
:
1477 LoadStringW(hInst
, IDS_EVENTLOG_AUDIT_FAILURE
, eventTypeText
, MAX_LOADSTRING
);
1480 LoadStringW(hInst
, IDS_EVENTLOG_UNKNOWN_TYPE
, eventTypeText
, MAX_LOADSTRING
);
1486 GetEventUserName(IN PEVENTLOGRECORD pelr
,
1487 IN OUT PSID
*pLastSid
,
1488 OUT PWCHAR pszUser
) // TODO: Add IN DWORD BufLen
1493 WCHAR szDomain
[1024];
1495 DWORD cchName
= ARRAYSIZE(szName
);
1496 DWORD cchDomain
= ARRAYSIZE(szDomain
);
1497 BOOL Success
= FALSE
;
1499 /* Point to the SID */
1500 pCurrentSid
= (PSID
)((LPBYTE
)pelr
+ pelr
->UserSidOffset
);
1502 if (!IsValidSid(pCurrentSid
))
1507 else if (*pLastSid
&& EqualSid(*pLastSid
, pCurrentSid
))
1513 if (pelr
->UserSidLength
> 0)
1516 * Try to retrieve the user account name and domain name corresponding
1517 * to the SID. If it cannot be retrieved, try to convert the SID to a
1518 * string-form. It should not be bigger than the user-provided buffer
1519 * 'pszUser', otherwise we return an error.
1521 if (LookupAccountSidW(NULL
, // FIXME: Use computer name? From the particular event?
1529 StringCchCopyW(pszUser
, MAX_PATH
, szName
);
1532 else if (ConvertSidToStringSidW(pCurrentSid
, &StringSid
))
1534 /* Copy the string only if the user-provided buffer is big enough */
1535 if (wcslen(StringSid
) + 1 <= MAX_PATH
) // + 1 for NULL-terminator
1537 StringCchCopyW(pszUser
, MAX_PATH
, StringSid
);
1542 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1546 /* Free the allocated buffer */
1547 LocalFree(StringSid
);
1551 *pLastSid
= Success
? pCurrentSid
: NULL
;
1557 static VOID
FreeRecords(VOID
)
1564 for (iIndex
= 0; iIndex
< g_TotalRecords
; iIndex
++)
1566 if (g_RecordPtrs
[iIndex
])
1567 HeapFree(GetProcessHeap(), 0, g_RecordPtrs
[iIndex
]);
1569 HeapFree(GetProcessHeap(), 0, g_RecordPtrs
);
1570 g_RecordPtrs
= NULL
;
1575 FilterByType(IN PEVENTLOGFILTER EventLogFilter
,
1576 IN PEVENTLOGRECORD pevlr
)
1578 if ((pevlr
->EventType
== EVENTLOG_SUCCESS
&& !EventLogFilter
->Information
) ||
1579 (pevlr
->EventType
== EVENTLOG_INFORMATION_TYPE
&& !EventLogFilter
->Information
) ||
1580 (pevlr
->EventType
== EVENTLOG_WARNING_TYPE
&& !EventLogFilter
->Warning
) ||
1581 (pevlr
->EventType
== EVENTLOG_ERROR_TYPE
&& !EventLogFilter
->Error
) ||
1582 (pevlr
->EventType
== EVENTLOG_AUDIT_SUCCESS
&& !EventLogFilter
->AuditSuccess
) ||
1583 (pevlr
->EventType
== EVENTLOG_AUDIT_FAILURE
&& !EventLogFilter
->AuditFailure
))
1591 FilterByString(IN PCWSTR FilterString
, // This is a multi-string
1596 /* The filter string is NULL so it does not filter anything */
1601 * If the filter string filters for an empty string AND the source string
1602 * is an empty string, we have a match (particular case of the last one).
1604 if (!*FilterString
&& !*String
)
1607 // if (*FilterString || *String)
1610 * If the filter string is empty BUT the source string is not empty,
1611 * OR vice-versa, we cannot have a match.
1613 if ( (!*FilterString
&& *String
) || (*FilterString
&& !*String
) )
1617 * If the filter string filters for at least a non-empty string,
1618 * browse it and search for a string that matches the source string.
1620 // else if (*FilterString && *String)
1622 pStr
= FilterString
;
1625 if (wcsicmp(pStr
, String
) == 0)
1627 /* We have a match, break the loop */
1631 pStr
+= (wcslen(pStr
) + 1);
1633 if (!*pStr
) // && *String
1635 /* We do not have a match */
1640 /* We have a match */
1645 * The events enumerator thread.
1648 EnumEventsThread(IN LPVOID lpParameter
)
1650 PEVENTLOGFILTER EventLogFilter
= (PEVENTLOGFILTER
)lpParameter
;
1655 PEVENTLOGRECORD pEvlr
= NULL
;
1658 DWORD dwWanted
, dwRead
, dwNeeded
, dwStatus
= ERROR_SUCCESS
;
1659 DWORD dwTotalRecords
= 0, dwCurrentRecord
= 0;
1660 DWORD dwFlags
, dwMaxLength
;
1661 size_t cchRemaining
;
1662 LPWSTR lpszSourceName
;
1663 LPWSTR lpszComputerName
;
1664 BOOL bResult
= TRUE
; /* Read succeeded */
1665 HANDLE hProcessHeap
= GetProcessHeap();
1666 PSID pLastSid
= NULL
;
1668 UINT uStep
= 0, uStepAt
= 0, uPos
= 0;
1670 WCHAR szWindowTitle
[MAX_PATH
];
1671 WCHAR szStatusText
[MAX_PATH
];
1672 WCHAR szLocalDate
[MAX_PATH
];
1673 WCHAR szLocalTime
[MAX_PATH
];
1674 WCHAR szEventID
[MAX_PATH
];
1675 WCHAR szEventTypeText
[MAX_LOADSTRING
];
1676 WCHAR szCategoryID
[MAX_PATH
];
1677 WCHAR szUsername
[MAX_PATH
];
1678 WCHAR szNoUsername
[MAX_PATH
];
1679 WCHAR szCategory
[MAX_PATH
];
1680 WCHAR szNoCategory
[MAX_PATH
];
1681 PWCHAR lpTitleTemplateEnd
;
1684 LVITEMW lviEventItem
;
1686 /* Save the current event log filter globally */
1687 EventLogFilter_AddRef(EventLogFilter
);
1688 ActiveFilter
= EventLogFilter
;
1692 EventLog
= EventLogFilter
->EventLogs
[0];
1694 // FIXME: Use something else instead of EventLog->LogName !!
1697 * Use a different formatting, whether the event log filter holds
1698 * only one log, or many logs (the latter case is WIP TODO!)
1700 if (EventLogFilter
->NumOfEventLogs
<= 1)
1702 StringCchPrintfExW(szWindowTitle
,
1703 ARRAYSIZE(szWindowTitle
),
1704 &lpTitleTemplateEnd
,
1707 szTitleTemplate
, szTitle
, EventLog
->LogName
); /* i = number of characters written */
1708 dwMaxLength
= (DWORD
)cchRemaining
;
1709 if (!EventLog
->ComputerName
)
1710 GetComputerNameW(lpTitleTemplateEnd
, &dwMaxLength
);
1712 StringCchCopyW(lpTitleTemplateEnd
, dwMaxLength
, EventLog
->ComputerName
);
1714 StringCbPrintfW(szStatusText
,
1715 sizeof(szStatusText
),
1716 szStatusBarTemplate
,
1723 // TODO: Use a different title & implement filtering for multi-log filters !!
1724 // (EventLogFilter->NumOfEventLogs > 1)
1725 MessageBoxW(hwndMainWindow
,
1726 L
"Many-logs filtering is not implemented yet!!",
1728 MB_OK
| MB_ICONINFORMATION
);
1731 /* Set the window title */
1732 SetWindowTextW(hwndMainWindow
, szWindowTitle
);
1734 /* Update the status bar */
1735 StatusBar_SetText(hwndStatus
, 0, szStatusText
);
1738 /* Disable list view redraw */
1739 SendMessageW(hwndListView
, WM_SETREDRAW
, FALSE
, 0);
1741 /* Clear the list view and free the cached records */
1742 ListView_DeleteAllItems(hwndListView
);
1745 SendMessageW(hwndListView
, LVM_PROGRESS
, 0, TRUE
);
1746 ProgressBar_SetRange(hwndStatusProgress
, 0);
1747 StatusBar_SetText(hwndStatus
, 0, NULL
);
1748 ShowWindow(hwndStatusProgress
, SW_SHOW
);
1750 /* Do a loop over the logs enumerated in the filter */
1751 // FIXME: For now we only support 1 event log per filter!
1753 // for (LogIndex = 0; LogIndex < EventLogFilter->NumOfEventLogs; ++LogIndex)
1756 EventLog
= EventLogFilter
->EventLogs
[LogIndex
];
1758 /* Open the event log */
1759 if (EventLog
->Permanent
)
1760 hEventLog
= OpenEventLogW(EventLog
->ComputerName
, EventLog
->LogName
);
1762 hEventLog
= OpenBackupEventLogW(EventLog
->ComputerName
, EventLog
->LogName
); // FileName
1764 if (hEventLog
== NULL
)
1766 ShowWin32Error(GetLastError());
1770 // GetOldestEventLogRecord(hEventLog, &dwThisRecord);
1772 /* Get the total number of event log records */
1773 GetNumberOfEventLogRecords(hEventLog
, &dwTotalRecords
);
1775 if (dwTotalRecords
> 0)
1777 EnableMenuItem(hMainMenu
, IDM_CLEAR_EVENTS
, MF_BYCOMMAND
| MF_ENABLED
);
1778 EnableMenuItem(hMainMenu
, IDM_SAVE_EVENTLOG
, MF_BYCOMMAND
| MF_ENABLED
);
1782 EnableMenuItem(hMainMenu
, IDM_CLEAR_EVENTS
, MF_BYCOMMAND
| MF_GRAYED
);
1783 EnableMenuItem(hMainMenu
, IDM_SAVE_EVENTLOG
, MF_BYCOMMAND
| MF_GRAYED
);
1786 /* Set up the event records cache */
1787 g_RecordPtrs
= HeapAlloc(hProcessHeap
, HEAP_ZERO_MEMORY
, dwTotalRecords
* sizeof(*g_RecordPtrs
));
1790 // ShowWin32Error(GetLastError());
1793 g_TotalRecords
= dwTotalRecords
;
1795 if (WaitForSingleObject(hStopEnumEvent
, 0) == WAIT_OBJECT_0
)
1798 LoadStringW(hInst
, IDS_NOT_AVAILABLE
, szNoUsername
, ARRAYSIZE(szNoUsername
));
1799 LoadStringW(hInst
, IDS_NONE
, szNoCategory
, ARRAYSIZE(szNoCategory
));
1801 ProgressBar_SetRange(hwndStatusProgress
, MAKELPARAM(0, 100));
1802 uStepAt
= (dwTotalRecords
/ 100) + 1;
1804 dwFlags
= EVENTLOG_SEQUENTIAL_READ
| (NewestEventsFirst
? EVENTLOG_FORWARDS_READ
: EVENTLOG_BACKWARDS_READ
);
1806 /* 0x7ffff is the maximum buffer size ReadEventLog will accept */
1808 pEvlr
= HeapAlloc(hProcessHeap
, 0, dwWanted
);
1813 while (dwStatus
== ERROR_SUCCESS
)
1815 bResult
= ReadEventLogW(hEventLog
, dwFlags
, 0, pEvlr
, dwWanted
, &dwRead
, &dwNeeded
);
1816 dwStatus
= GetLastError();
1818 if (!bResult
&& dwStatus
== ERROR_INSUFFICIENT_BUFFER
)
1820 pEvlr
= HeapReAlloc(hProcessHeap
, 0, pEvlr
, dwNeeded
);
1821 dwWanted
= dwNeeded
;
1826 bResult
= ReadEventLogW(hEventLog
, dwFlags
, 0, pEvlr
, dwNeeded
, &dwRead
, &dwNeeded
);
1833 /* Exit on other errors (ERROR_HANDLE_EOF) */
1837 pEvlrBuffer
= (LPBYTE
)pEvlr
;
1838 pEvlrEnd
= pEvlrBuffer
+ dwRead
;
1840 while (pEvlrBuffer
< pEvlrEnd
)
1842 PEVENTLOGRECORD pEvlrTmp
= (PEVENTLOGRECORD
)pEvlrBuffer
;
1843 PWSTR lpszUsername
, lpszCategoryName
;
1844 g_RecordPtrs
[dwCurrentRecord
] = NULL
;
1846 // ProgressBar_StepIt(hwndStatusProgress);
1848 if (uStep
% uStepAt
== 0)
1851 ProgressBar_SetPos(hwndStatusProgress
, uPos
);
1854 if (WaitForSingleObject(hStopEnumEvent
, 0) == WAIT_OBJECT_0
)
1857 /* Filter by event type */
1858 if (!FilterByType(EventLogFilter
, pEvlrTmp
))
1861 /* Get the event source name and filter it */
1862 lpszSourceName
= (LPWSTR
)(pEvlrBuffer
+ sizeof(EVENTLOGRECORD
));
1863 if (!FilterByString(EventLogFilter
->Sources
, lpszSourceName
))
1866 /* Get the computer name and filter it */
1867 lpszComputerName
= (LPWSTR
)(pEvlrBuffer
+ sizeof(EVENTLOGRECORD
) + (wcslen(lpszSourceName
) + 1) * sizeof(WCHAR
));
1868 if (!FilterByString(EventLogFilter
->ComputerNames
, lpszComputerName
))
1871 /* Compute the event time */
1872 EventTimeToSystemTime(pEvlrTmp
->TimeWritten
, &time
);
1873 GetDateFormatW(LOCALE_USER_DEFAULT
, DATE_SHORTDATE
, &time
, NULL
, szLocalDate
, ARRAYSIZE(szLocalDate
));
1874 GetTimeFormatW(LOCALE_USER_DEFAULT
, 0, &time
, NULL
, szLocalTime
, ARRAYSIZE(szLocalTime
));
1876 /* Get the username that generated the event, and filter it */
1877 lpszUsername
= GetEventUserName(pEvlrTmp
, &pLastSid
, szUsername
) ? szUsername
: szNoUsername
;
1879 if (!FilterByString(EventLogFilter
->Users
, lpszUsername
))
1882 // TODO: Filter by event ID and category
1883 GetEventType(pEvlrTmp
->EventType
, szEventTypeText
);
1885 lpszCategoryName
= GetEventCategory(EventLog
->LogName
, lpszSourceName
, pEvlrTmp
, szCategory
) ? szCategory
: szNoCategory
;
1887 StringCbPrintfW(szEventID
, sizeof(szEventID
), L
"%u", (pEvlrTmp
->EventID
& 0xFFFF));
1888 StringCbPrintfW(szCategoryID
, sizeof(szCategoryID
), L
"%u", pEvlrTmp
->EventCategory
);
1890 g_RecordPtrs
[dwCurrentRecord
] = HeapAlloc(hProcessHeap
, 0, pEvlrTmp
->Length
);
1891 RtlCopyMemory(g_RecordPtrs
[dwCurrentRecord
], pEvlrTmp
, pEvlrTmp
->Length
);
1893 lviEventItem
.mask
= LVIF_IMAGE
| LVIF_TEXT
| LVIF_PARAM
;
1894 lviEventItem
.iItem
= 0;
1895 lviEventItem
.iSubItem
= 0;
1896 lviEventItem
.lParam
= (LPARAM
)g_RecordPtrs
[dwCurrentRecord
];
1897 lviEventItem
.pszText
= szEventTypeText
;
1899 switch (pEvlrTmp
->EventType
)
1901 case EVENTLOG_SUCCESS
:
1902 case EVENTLOG_INFORMATION_TYPE
:
1903 lviEventItem
.iImage
= 0;
1906 case EVENTLOG_WARNING_TYPE
:
1907 lviEventItem
.iImage
= 1;
1910 case EVENTLOG_ERROR_TYPE
:
1911 lviEventItem
.iImage
= 2;
1914 case EVENTLOG_AUDIT_SUCCESS
:
1915 lviEventItem
.iImage
= 3;
1918 case EVENTLOG_AUDIT_FAILURE
:
1919 lviEventItem
.iImage
= 4;
1923 lviEventItem
.iItem
= ListView_InsertItem(hwndListView
, &lviEventItem
);
1925 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 1, szLocalDate
);
1926 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 2, szLocalTime
);
1927 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 3, lpszSourceName
);
1928 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 4, lpszCategoryName
);
1929 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 5, szEventID
);
1930 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 6, lpszUsername
);
1931 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 7, lpszComputerName
);
1934 pEvlrBuffer
+= pEvlrTmp
->Length
;
1942 HeapFree(hProcessHeap
, 0, pEvlr
);
1944 /* Close the event log */
1945 CloseEventLog(hEventLog
);
1947 } // end-for (LogIndex)
1949 /* All events loaded */
1953 ShowWindow(hwndStatusProgress
, SW_HIDE
);
1954 SendMessageW(hwndListView
, LVM_PROGRESS
, 0, FALSE
);
1956 // FIXME: Use something else instead of EventLog->LogName !!
1959 * Use a different formatting, whether the event log filter holds
1960 * only one log, or many logs (the latter case is WIP TODO!)
1962 if (EventLogFilter
->NumOfEventLogs
<= 1)
1964 StringCbPrintfW(szStatusText
,
1965 sizeof(szStatusText
),
1966 szStatusBarTemplate
,
1969 ListView_GetItemCount(hwndListView
));
1973 // TODO: Use a different title & implement filtering for multi-log filters !!
1974 // (EventLogFilter->NumOfEventLogs > 1)
1977 /* Update the status bar */
1978 StatusBar_SetText(hwndStatus
, 0, szStatusText
);
1980 /* Resume list view redraw */
1981 SendMessageW(hwndListView
, WM_SETREDRAW
, TRUE
, 0);
1983 EventLogFilter_Release(EventLogFilter
);
1985 CloseHandle(hStopEnumEvent
);
1986 InterlockedExchangePointer((PVOID
*)&hStopEnumEvent
, NULL
);
1992 * The purpose of this thread is to serialize the creation of the events
1993 * enumeration thread, since the Event Log Viewer currently only supports
1994 * one view, one event list, one enumeration.
1997 StartStopEnumEventsThread(IN LPVOID lpParameter
)
1999 HANDLE WaitHandles
[2];
2002 WaitHandles
[0] = hStartStopEnumEvent
; // End-of-application event
2003 WaitHandles
[1] = hStartEnumEvent
; // Command event
2007 WaitResult
= WaitForMultipleObjects(ARRAYSIZE(WaitHandles
),
2013 case WAIT_OBJECT_0
+ 0:
2015 /* End-of-application event signaled, quit this thread */
2017 /* Stop the previous enumeration */
2018 if (hEnumEventsThread
)
2022 SetEvent(hStopEnumEvent
);
2023 WaitForSingleObject(hEnumEventsThread
, INFINITE
);
2024 // NOTE: The following is done by the enumeration thread just before terminating.
2025 // hStopEnumEvent = NULL;
2028 CloseHandle(hEnumEventsThread
);
2029 hEnumEventsThread
= NULL
;
2032 /* Clear the list view and free the cached records */
2033 ListView_DeleteAllItems(hwndListView
);
2036 /* Reset the active filter */
2037 ActiveFilter
= NULL
;
2042 case WAIT_OBJECT_0
+ 1:
2044 /* Restart a new enumeration if needed */
2045 PEVENTLOGFILTER EventLogFilter
;
2047 /* Stop the previous enumeration */
2048 if (hEnumEventsThread
)
2052 SetEvent(hStopEnumEvent
);
2053 WaitForSingleObject(hEnumEventsThread
, INFINITE
);
2054 // NOTE: The following is done by the enumeration thread just before terminating.
2055 // hStopEnumEvent = NULL;
2058 CloseHandle(hEnumEventsThread
);
2059 hEnumEventsThread
= NULL
;
2062 /* Clear the list view and free the cached records */
2063 ListView_DeleteAllItems(hwndListView
);
2066 /* Reset the active filter */
2067 ActiveFilter
= NULL
;
2069 EventLogFilter
= InterlockedExchangePointer((PVOID
*)&EnumFilter
, NULL
);
2070 if (!EventLogFilter
)
2073 // Manual-reset event
2074 hStopEnumEvent
= CreateEventW(NULL
, TRUE
, FALSE
, NULL
);
2075 if (!hStopEnumEvent
)
2078 hEnumEventsThread
= CreateThread(NULL
,
2081 (LPVOID
)EventLogFilter
,
2084 if (!hEnumEventsThread
)
2086 CloseHandle(hStopEnumEvent
);
2087 hStopEnumEvent
= NULL
;
2090 // CloseHandle(hEnumEventsThread);
2091 ResumeThread(hEnumEventsThread
);
2098 /* Unknown command, must never go there! */
2099 return GetLastError();
2108 EnumEvents(IN PEVENTLOGFILTER EventLogFilter
)
2110 /* Signal the enumerator thread we want to enumerate events */
2111 InterlockedExchangePointer((PVOID
*)&EnumFilter
, EventLogFilter
);
2112 SetEvent(hStartEnumEvent
);
2118 GetSelectedFilter(OUT HTREEITEM
* phti OPTIONAL
)
2126 /* Get index of selected item */
2127 hti
= TreeView_GetSelection(hwndTreeView
);
2129 return NULL
; // No filter
2131 tvItemEx
.mask
= TVIF_PARAM
;
2132 tvItemEx
.hItem
= hti
;
2134 TreeView_GetItem(hwndTreeView
, &tvItemEx
);
2137 *phti
= tvItemEx
.hItem
;
2139 return (PEVENTLOGFILTER
)tvItemEx
.lParam
;
2144 OpenUserEventLogFile(IN LPCWSTR lpszFileName
)
2146 WIN32_FIND_DATAW FindData
;
2149 PEVENTLOGFILTER EventLogFilter
;
2151 HTREEITEM hItem
= NULL
;
2153 /* Check whether the file actually exists */
2154 hFind
= FindFirstFileW(lpszFileName
, &FindData
);
2155 if (hFind
== INVALID_HANDLE_VALUE
)
2157 ShowWin32Error(GetLastError());
2162 /* Allocate a new event log entry */
2163 EventLog
= AllocEventLog(NULL
, lpszFileName
, FALSE
);
2164 if (EventLog
== NULL
)
2166 ShowWin32Error(ERROR_NOT_ENOUGH_MEMORY
);
2170 /* Allocate a new event log filter entry for this event log */
2171 EventLogFilter
= AllocEventLogFilter(// LogName,
2172 TRUE
, TRUE
, TRUE
, TRUE
, TRUE
,
2175 if (EventLogFilter
== NULL
)
2177 ShowWin32Error(ERROR_NOT_ENOUGH_MEMORY
);
2178 EventLog_Free(EventLog
);
2182 /* Add the event log and the filter into their lists */
2183 InsertTailList(&EventLogList
, &EventLog
->ListEntry
);
2184 InsertTailList(&EventLogFilterList
, &EventLogFilter
->ListEntry
);
2186 /* Retrieve and cache the event log file */
2187 cchFileName
= wcslen(lpszFileName
) + 1;
2188 EventLog
->FileName
= HeapAlloc(GetProcessHeap(), 0, cchFileName
* sizeof(WCHAR
));
2189 if (EventLog
->FileName
)
2190 StringCchCopyW(EventLog
->FileName
, cchFileName
, lpszFileName
);
2192 hItem
= TreeViewAddItem(hwndTreeView
, htiUserLogs
,
2193 (LPWSTR
)lpszFileName
,
2194 2, 3, (LPARAM
)EventLogFilter
);
2196 /* Select the event log */
2199 // TreeView_Expand(hwndTreeView, htiUserLogs, TVE_EXPAND);
2200 TreeView_SelectItem(hwndTreeView
, hItem
);
2201 TreeView_EnsureVisible(hwndTreeView
, hItem
);
2203 InvalidateRect(hwndTreeView
, NULL
, FALSE
);
2204 SetFocus(hwndTreeView
);
2208 OpenUserEventLog(VOID
)
2210 WCHAR szFileName
[MAX_PATH
];
2212 ZeroMemory(szFileName
, sizeof(szFileName
));
2214 sfn
.lpstrFile
= szFileName
;
2215 sfn
.nMaxFile
= ARRAYSIZE(szFileName
);
2217 if (!GetOpenFileNameW(&sfn
))
2219 sfn
.lpstrFile
[sfn
.nMaxFile
-1] = UNICODE_NULL
;
2221 OpenUserEventLogFile(sfn
.lpstrFile
);
2225 SaveEventLog(IN PEVENTLOGFILTER EventLogFilter
)
2229 WCHAR szFileName
[MAX_PATH
];
2231 /* Bail out if there is no available filter */
2232 if (!EventLogFilter
)
2235 ZeroMemory(szFileName
, sizeof(szFileName
));
2237 sfn
.lpstrFile
= szFileName
;
2238 sfn
.nMaxFile
= ARRAYSIZE(szFileName
);
2240 if (!GetSaveFileNameW(&sfn
))
2243 EventLogFilter_AddRef(EventLogFilter
);
2245 EventLog
= EventLogFilter
->EventLogs
[0];
2246 hEventLog
= OpenEventLogW(EventLog
->ComputerName
, EventLog
->LogName
);
2248 EventLogFilter_Release(EventLogFilter
);
2252 ShowWin32Error(GetLastError());
2256 if (!BackupEventLogW(hEventLog
, szFileName
))
2257 ShowWin32Error(GetLastError());
2259 CloseEventLog(hEventLog
);
2263 CloseUserEventLog(IN PEVENTLOGFILTER EventLogFilter
, IN HTREEITEM hti
)
2265 /* Bail out if there is no available filter */
2266 if (!EventLogFilter
)
2269 if (InterlockedCompareExchangePointer((PVOID
*)&ActiveFilter
, NULL
, NULL
) == EventLogFilter
)
2271 /* Signal the enumerator thread we want to stop enumerating events */
2272 // EnumEvents(NULL);
2273 InterlockedExchangePointer((PVOID
*)&EnumFilter
, NULL
);
2274 SetEvent(hStartEnumEvent
);
2278 * The deletion of the item automatically triggers a TVN_SELCHANGED
2279 * notification, that will reset the ActiveFilter (in case the item
2280 * selected is a filter). Otherwise we reset it there.
2282 TreeView_DeleteItem(hwndTreeView
, hti
);
2284 /* Remove the filter from the list */
2285 RemoveEntryList(&EventLogFilter
->ListEntry
);
2286 EventLogFilter_Release(EventLogFilter
);
2288 // /* Select the default event log */
2289 // // TreeView_Expand(hwndTreeView, htiUserLogs, TVE_EXPAND);
2290 // TreeView_SelectItem(hwndTreeView, hItem);
2291 // TreeView_EnsureVisible(hwndTreeView, hItem);
2292 InvalidateRect(hwndTreeView
, NULL
, FALSE
);
2293 SetFocus(hwndTreeView
);
2298 ClearEvents(IN PEVENTLOGFILTER EventLogFilter
)
2303 WCHAR szFileName
[MAX_PATH
];
2304 WCHAR szMessage
[MAX_LOADSTRING
];
2306 /* Bail out if there is no available filter */
2307 if (!EventLogFilter
)
2310 ZeroMemory(szFileName
, sizeof(szFileName
));
2311 ZeroMemory(szMessage
, sizeof(szMessage
));
2313 LoadStringW(hInst
, IDS_CLEAREVENTS_MSG
, szMessage
, ARRAYSIZE(szMessage
));
2315 sfn
.lpstrFile
= szFileName
;
2316 sfn
.nMaxFile
= ARRAYSIZE(szFileName
);
2318 switch (MessageBoxW(hwndMainWindow
, szMessage
, szTitle
, MB_YESNOCANCEL
| MB_ICONINFORMATION
))
2324 sfn
.lpstrFile
= NULL
;
2328 if (!GetSaveFileNameW(&sfn
))
2333 EventLogFilter_AddRef(EventLogFilter
);
2335 EventLog
= EventLogFilter
->EventLogs
[0];
2336 hEventLog
= OpenEventLogW(EventLog
->ComputerName
, EventLog
->LogName
);
2338 EventLogFilter_Release(EventLogFilter
);
2342 ShowWin32Error(GetLastError());
2346 Success
= ClearEventLogW(hEventLog
, sfn
.lpstrFile
);
2348 ShowWin32Error(GetLastError());
2350 CloseEventLog(hEventLog
);
2356 Refresh(IN PEVENTLOGFILTER EventLogFilter
)
2358 /* Bail out if there is no available filter */
2359 if (!EventLogFilter
)
2362 /* Reenumerate the events through the filter */
2363 EnumEvents(EventLogFilter
);
2368 MyRegisterClass(HINSTANCE hInstance
)
2372 wcex
.cbSize
= sizeof(wcex
);
2374 wcex
.lpfnWndProc
= WndProc
;
2375 wcex
.cbClsExtra
= 0;
2376 wcex
.cbWndExtra
= 0;
2377 wcex
.hInstance
= hInstance
;
2378 wcex
.hIcon
= LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_EVENTVWR
));
2379 wcex
.hCursor
= LoadCursorW(NULL
, MAKEINTRESOURCEW(IDC_ARROW
));
2380 wcex
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1); // COLOR_WINDOW + 1
2381 wcex
.lpszMenuName
= MAKEINTRESOURCEW(IDM_EVENTVWR
);
2382 wcex
.lpszClassName
= EVENTVWR_WNDCLASS
;
2383 wcex
.hIconSm
= (HICON
)LoadImageW(hInstance
,
2384 MAKEINTRESOURCEW(IDI_EVENTVWR
),
2390 return RegisterClassExW(&wcex
);
2395 GetDisplayNameFileAndID(IN LPCWSTR lpLogName
,
2396 OUT PWCHAR lpModuleName
, // TODO: Add IN DWORD BufLen
2397 OUT PDWORD pdwMessageID
)
2399 BOOL Success
= FALSE
;
2404 DWORD dwType
, cbData
;
2405 DWORD dwMessageID
= 0;
2406 WCHAR szModuleName
[MAX_PATH
];
2408 /* Use a default value for the message ID */
2411 cbKeyPath
= (wcslen(EVENTLOG_BASE_KEY
) + wcslen(lpLogName
) + 1) * sizeof(WCHAR
);
2412 KeyPath
= HeapAlloc(GetProcessHeap(), 0, cbKeyPath
);
2416 StringCbCopyW(KeyPath
, cbKeyPath
, EVENTLOG_BASE_KEY
);
2417 StringCbCatW(KeyPath
, cbKeyPath
, lpLogName
);
2419 Result
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, KeyPath
, 0, KEY_QUERY_VALUE
, &hLogKey
);
2420 HeapFree(GetProcessHeap(), 0, KeyPath
);
2421 if (Result
!= ERROR_SUCCESS
)
2424 cbData
= sizeof(szModuleName
);
2425 Result
= RegQueryValueExW(hLogKey
,
2429 (LPBYTE
)szModuleName
,
2431 if ((Result
!= ERROR_SUCCESS
) || (dwType
!= REG_EXPAND_SZ
&& dwType
!= REG_SZ
))
2433 szModuleName
[0] = UNICODE_NULL
;
2437 /* NULL-terminate the string and expand it */
2438 szModuleName
[cbData
/ sizeof(WCHAR
) - 1] = UNICODE_NULL
;
2439 ExpandEnvironmentStringsW(szModuleName
, lpModuleName
, ARRAYSIZE(szModuleName
));
2444 * If we have a 'DisplayNameFile', query for 'DisplayNameID';
2445 * otherwise it's not really useful. 'DisplayNameID' is optional.
2449 cbData
= sizeof(dwMessageID
);
2450 Result
= RegQueryValueExW(hLogKey
,
2454 (LPBYTE
)&dwMessageID
,
2456 if ((Result
!= ERROR_SUCCESS
) || (dwType
!= REG_DWORD
))
2459 *pdwMessageID
= dwMessageID
;
2462 RegCloseKey(hLogKey
);
2469 BuildLogListAndFilterList(IN LPCWSTR lpComputerName
)
2472 HKEY hEventLogKey
, hLogKey
;
2473 DWORD dwNumLogs
= 0;
2474 DWORD dwIndex
, dwMaxKeyLength
;
2477 PEVENTLOGFILTER EventLogFilter
;
2478 LPWSTR LogName
= NULL
;
2479 WCHAR szModuleName
[MAX_PATH
];
2482 LPWSTR lpDisplayName
;
2483 HTREEITEM hRootNode
= NULL
, hItem
= NULL
, hItemDefault
= NULL
;
2485 /* Open the EventLog key */
2486 // TODO: Implement connection to remote computer...
2487 // At the moment we only support the user local computer.
2488 // FIXME: Use local or remote computer
2489 Result
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, EVENTLOG_BASE_KEY
, 0, KEY_READ
, &hEventLogKey
);
2490 if (Result
!= ERROR_SUCCESS
)
2495 /* Retrieve the number of event logs enumerated as registry keys */
2496 Result
= RegQueryInfoKeyW(hEventLogKey
, NULL
, NULL
, NULL
, &dwNumLogs
, &dwMaxKeyLength
,
2497 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
2498 if (Result
!= ERROR_SUCCESS
)
2505 /* Take the NULL terminator into account */
2508 /* Allocate the temporary buffer */
2509 LogName
= HeapAlloc(GetProcessHeap(), 0, dwMaxKeyLength
* sizeof(WCHAR
));
2513 /* Enumerate and retrieve each event log name */
2514 for (dwIndex
= 0; dwIndex
< dwNumLogs
; dwIndex
++)
2516 lpcName
= dwMaxKeyLength
;
2517 Result
= RegEnumKeyExW(hEventLogKey
, dwIndex
, LogName
, &lpcName
, NULL
, NULL
, NULL
, NULL
);
2518 if (Result
!= ERROR_SUCCESS
)
2521 /* Take the NULL terminator into account */
2524 /* Allocate a new event log entry */
2525 EventLog
= AllocEventLog(lpComputerName
, LogName
, TRUE
);
2526 if (EventLog
== NULL
)
2529 /* Allocate a new event log filter entry for this event log */
2530 EventLogFilter
= AllocEventLogFilter(// LogName,
2531 TRUE
, TRUE
, TRUE
, TRUE
, TRUE
,
2534 if (EventLogFilter
== NULL
)
2536 EventLog_Free(EventLog
);
2540 /* Add the event log and the filter into their lists */
2541 InsertTailList(&EventLogList
, &EventLog
->ListEntry
);
2542 InsertTailList(&EventLogFilterList
, &EventLogFilter
->ListEntry
);
2544 EventLog
->FileName
= NULL
;
2546 /* Retrieve and cache the event log file */
2547 Result
= RegOpenKeyExW(hEventLogKey
,
2552 if (Result
== ERROR_SUCCESS
)
2555 Result
= RegQueryValueExW(hLogKey
,
2561 if ((Result
!= ERROR_SUCCESS
) || (dwType
!= REG_EXPAND_SZ
&& dwType
!= REG_SZ
))
2563 // Windows' EventLog uses some kind of default value, we do not.
2564 EventLog
->FileName
= NULL
;
2568 lpcName
= ROUND_DOWN(lpcName
, sizeof(WCHAR
));
2569 EventLog
->FileName
= HeapAlloc(GetProcessHeap(), 0, lpcName
);
2570 if (EventLog
->FileName
)
2572 Result
= RegQueryValueExW(hLogKey
,
2576 (LPBYTE
)EventLog
->FileName
,
2578 if (Result
!= ERROR_SUCCESS
)
2580 HeapFree(GetProcessHeap(), 0, EventLog
->FileName
);
2581 EventLog
->FileName
= NULL
;
2585 EventLog
->FileName
[lpcName
/ sizeof(WCHAR
) - 1] = UNICODE_NULL
;
2590 RegCloseKey(hLogKey
);
2593 /* Get the display name for the event log */
2594 lpDisplayName
= NULL
;
2596 ZeroMemory(szModuleName
, sizeof(szModuleName
));
2597 if (GetDisplayNameFileAndID(LogName
, szModuleName
, &dwMessageID
))
2599 /* Retrieve the message string without appending extra newlines */
2601 GetMessageStringFromDll(szModuleName
,
2602 FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_HMODULE
|
2603 FORMAT_MESSAGE_IGNORE_INSERTS
| FORMAT_MESSAGE_MAX_WIDTH_MASK
,
2610 * Select the correct tree root node, whether the log is a System
2611 * or an Application log. Default to Application log otherwise.
2613 hRootNode
= htiAppLogs
;
2614 for (lpcName
= 0; lpcName
< ARRAYSIZE(SystemLogs
); ++lpcName
)
2616 /* Check whether the log name is part of the system logs */
2617 if (wcsicmp(LogName
, SystemLogs
[lpcName
]) == 0)
2619 hRootNode
= htiSystemLogs
;
2624 hItem
= TreeViewAddItem(hwndTreeView
, hRootNode
,
2625 (lpDisplayName
? lpDisplayName
: LogName
),
2626 2, 3, (LPARAM
)EventLogFilter
);
2628 /* Try to get the default event log: "Application" */
2629 if ((hItemDefault
== NULL
) && (wcsicmp(LogName
, SystemLogs
[0]) == 0))
2631 hItemDefault
= hItem
;
2634 /* Free the buffer allocated by FormatMessage */
2636 LocalFree(lpDisplayName
);
2639 HeapFree(GetProcessHeap(), 0, LogName
);
2642 RegCloseKey(hEventLogKey
);
2644 /* Select the default event log */
2647 // TreeView_Expand(hwndTreeView, hRootNode, TVE_EXPAND);
2648 TreeView_SelectItem(hwndTreeView
, hItemDefault
);
2649 TreeView_EnsureVisible(hwndTreeView
, hItemDefault
);
2651 InvalidateRect(hwndTreeView
, NULL
, FALSE
);
2652 SetFocus(hwndTreeView
);
2663 while (!IsListEmpty(&EventLogList
))
2665 Entry
= RemoveHeadList(&EventLogList
);
2666 EventLog
= (PEVENTLOG
)CONTAINING_RECORD(Entry
, EVENTLOG
, ListEntry
);
2667 EventLog_Free(EventLog
);
2674 FreeLogFilterList(VOID
)
2677 PEVENTLOGFILTER EventLogFilter
;
2679 while (!IsListEmpty(&EventLogFilterList
))
2681 Entry
= RemoveHeadList(&EventLogFilterList
);
2682 EventLogFilter
= (PEVENTLOGFILTER
)CONTAINING_RECORD(Entry
, EVENTLOGFILTER
, ListEntry
);
2683 EventLogFilter_Free(EventLogFilter
);
2686 ActiveFilter
= NULL
;
2692 InitInstance(HINSTANCE hInstance
,
2698 LVCOLUMNW lvc
= {0};
2701 /* Create the main window */
2702 hwndMainWindow
= CreateWindowW(EVENTVWR_WNDCLASS
,
2704 WS_OVERLAPPEDWINDOW
| WS_CLIPCHILDREN
,
2705 CW_USEDEFAULT
, 0, CW_USEDEFAULT
, 0,
2710 if (!hwndMainWindow
)
2713 /* Create the status bar */
2714 hwndStatus
= CreateWindowExW(0, // no extended styles
2715 STATUSCLASSNAMEW
, // status bar
2717 WS_CHILD
| WS_VISIBLE
| CCS_BOTTOM
| SBARS_SIZEGRIP
, // styles
2718 0, 0, 0, 0, // x, y, cx, cy
2719 hwndMainWindow
, // parent window
2720 (HMENU
)100, // window ID
2721 hInstance
, // instance
2722 NULL
); // window data
2724 GetClientRect(hwndMainWindow
, &rcClient
);
2725 GetWindowRect(hwndStatus
, &rs
);
2726 StatusHeight
= rs
.bottom
- rs
.top
;
2728 /* Create a progress bar in the status bar (hidden by default) */
2729 StatusBar_GetItemRect(hwndStatus
, 0, &rs
);
2730 hwndStatusProgress
= CreateWindowExW(0, // no extended styles
2731 PROGRESS_CLASSW
, // status bar
2733 WS_CHILD
| PBS_SMOOTH
, // styles
2734 rs
.left
, rs
.top
, // x, y
2735 rs
.right
- rs
.left
, rs
.bottom
- rs
.top
, // cx, cy
2736 hwndStatus
, // parent window
2738 hInstance
, // instance
2739 NULL
); // window data
2740 ProgressBar_SetStep(hwndStatusProgress
, 1);
2742 /* Initialize the splitter default positions */
2746 /* Create the TreeView */
2747 hwndTreeView
= CreateWindowExW(WS_EX_CLIENTEDGE
,
2750 // WS_CHILD | WS_VISIBLE | TVS_HASLINES | TVS_SHOWSELALWAYS,
2751 WS_CHILD
| WS_VISIBLE
| /* WS_TABSTOP | */ TVS_HASLINES
| TVS_HASBUTTONS
| TVS_LINESATROOT
| TVS_EDITLABELS
| TVS_SHOWSELALWAYS
,
2753 nVSplitPos
- SPLIT_WIDTH
/2,
2754 (rcClient
.bottom
- rcClient
.top
) - StatusHeight
,
2760 /* Create the ImageList */
2761 hSmall
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
),
2762 GetSystemMetrics(SM_CYSMICON
),
2763 ILC_COLOR32
| ILC_MASK
, // ILC_COLOR24
2766 /* Add event type icons to the ImageList: closed/opened folder, event log (normal/viewed) */
2767 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_CLOSED_CATEGORY
)));
2768 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_OPENED_CATEGORY
)));
2769 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_EVENTLOG
)));
2770 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_EVENTVWR
)));
2772 /* Assign the ImageList to the Tree View */
2773 TreeView_SetImageList(hwndTreeView
, hSmall
, TVSIL_NORMAL
);
2775 /* Add the event logs nodes */
2777 LoadStringW(hInstance
, IDS_EVENTLOG_SYSTEM
, szTemp
, ARRAYSIZE(szTemp
));
2778 htiSystemLogs
= TreeViewAddItem(hwndTreeView
, NULL
, szTemp
, 0, 1, (LPARAM
)NULL
);
2779 // "Application Logs"
2780 LoadStringW(hInstance
, IDS_EVENTLOG_APP
, szTemp
, ARRAYSIZE(szTemp
));
2781 htiAppLogs
= TreeViewAddItem(hwndTreeView
, NULL
, szTemp
, 0, 1, (LPARAM
)NULL
);
2783 LoadStringW(hInstance
, IDS_EVENTLOG_USER
, szTemp
, ARRAYSIZE(szTemp
));
2784 htiUserLogs
= TreeViewAddItem(hwndTreeView
, NULL
, szTemp
, 0, 1, (LPARAM
)NULL
);
2786 /* Create the Event details pane (optional) */
2787 hwndEventDetails
= CreateEventDetailsCtrl(hInst
, hwndMainWindow
, (LPARAM
)NULL
);
2788 // hwndEventDetails = NULL;
2789 if (hwndEventDetails
)
2791 // SetWindowLongPtrW(hwndEventDetails, GWL_STYLE,
2792 // GetWindowLongPtrW(hwndEventDetails, GWL_STYLE) | WS_BORDER);
2793 SetWindowLongPtrW(hwndEventDetails
, GWL_EXSTYLE
,
2794 GetWindowLongPtrW(hwndEventDetails
, GWL_EXSTYLE
) | WS_EX_CLIENTEDGE
);
2795 SetWindowPos(hwndEventDetails
, NULL
,
2796 nVSplitPos
+ SPLIT_WIDTH
/2,
2797 nHSplitPos
+ SPLIT_WIDTH
/2,
2798 (rcClient
.right
- rcClient
.left
) - nVSplitPos
- SPLIT_WIDTH
/2,
2799 (rcClient
.bottom
- rcClient
.top
) - nHSplitPos
- SPLIT_WIDTH
/2 - StatusHeight
,
2800 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_SHOWWINDOW
);
2803 /* Create the ListView */
2804 hwndListView
= CreateWindowExW(WS_EX_CLIENTEDGE
,
2807 WS_CHILD
| WS_VISIBLE
| LVS_SHOWSELALWAYS
| LVS_REPORT
,
2808 nVSplitPos
+ SPLIT_WIDTH
/2,
2810 (rcClient
.right
- rcClient
.left
) - nVSplitPos
- SPLIT_WIDTH
/2,
2811 hwndEventDetails
? nHSplitPos
- SPLIT_WIDTH
/2
2812 : (rcClient
.bottom
- rcClient
.top
) - StatusHeight
,
2818 /* Add the extended ListView styles */
2819 ListView_SetExtendedListViewStyle(hwndListView
, LVS_EX_HEADERDRAGDROP
| LVS_EX_FULLROWSELECT
|LVS_EX_LABELTIP
);
2821 /* Create the ImageList */
2822 hSmall
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
),
2823 GetSystemMetrics(SM_CYSMICON
),
2824 ILC_COLOR32
| ILC_MASK
, // ILC_COLOR24
2827 /* Add event type icons to the ImageList */
2828 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_INFORMATIONICON
)));
2829 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_WARNINGICON
)));
2830 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_ERRORICON
)));
2831 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_AUDITSUCCESSICON
)));
2832 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_AUDITFAILUREICON
)));
2834 /* Assign the ImageList to the List View */
2835 ListView_SetImageList(hwndListView
, hSmall
, LVSIL_SMALL
);
2837 /* Now set up the listview with its columns */
2838 lvc
.mask
= LVCF_TEXT
| LVCF_WIDTH
;
2840 LoadStringW(hInstance
,
2844 lvc
.pszText
= szTemp
;
2845 ListView_InsertColumn(hwndListView
, 0, &lvc
);
2848 LoadStringW(hInstance
,
2852 lvc
.pszText
= szTemp
;
2853 ListView_InsertColumn(hwndListView
, 1, &lvc
);
2856 LoadStringW(hInstance
,
2860 lvc
.pszText
= szTemp
;
2861 ListView_InsertColumn(hwndListView
, 2, &lvc
);
2864 LoadStringW(hInstance
,
2868 lvc
.pszText
= szTemp
;
2869 ListView_InsertColumn(hwndListView
, 3, &lvc
);
2872 LoadStringW(hInstance
,
2876 lvc
.pszText
= szTemp
;
2877 ListView_InsertColumn(hwndListView
, 4, &lvc
);
2880 LoadStringW(hInstance
,
2884 lvc
.pszText
= szTemp
;
2885 ListView_InsertColumn(hwndListView
, 5, &lvc
);
2888 LoadStringW(hInstance
,
2892 lvc
.pszText
= szTemp
;
2893 ListView_InsertColumn(hwndListView
, 6, &lvc
);
2896 LoadStringW(hInstance
,
2900 lvc
.pszText
= szTemp
;
2901 ListView_InsertColumn(hwndListView
, 7, &lvc
);
2903 /* Initialize the save Dialog */
2904 ZeroMemory(&sfn
, sizeof(sfn
));
2905 ZeroMemory(szSaveFilter
, sizeof(szSaveFilter
));
2907 LoadStringW(hInst
, IDS_SAVE_FILTER
, szSaveFilter
, ARRAYSIZE(szSaveFilter
));
2909 sfn
.lStructSize
= sizeof(sfn
);
2910 sfn
.hwndOwner
= hwndMainWindow
;
2911 sfn
.hInstance
= hInstance
;
2912 sfn
.lpstrFilter
= szSaveFilter
;
2913 sfn
.lpstrInitialDir
= NULL
;
2914 sfn
.Flags
= OFN_HIDEREADONLY
| OFN_SHAREAWARE
;
2915 sfn
.lpstrDefExt
= NULL
;
2917 ShowWindow(hwndMainWindow
, nCmdShow
);
2918 UpdateWindow(hwndMainWindow
);
2923 VOID
ResizeWnd(INT cx
, INT cy
)
2929 /* Resize the status bar -- now done in WM_SIZE */
2930 // SendMessageW(hwndStatus, WM_SIZE, 0, 0);
2931 GetWindowRect(hwndStatus
, &rs
);
2932 StatusHeight
= rs
.bottom
- rs
.top
;
2934 /* Move the progress bar */
2935 StatusBar_GetItemRect(hwndStatus
, 0, &rs
);
2936 MoveWindow(hwndStatusProgress
,
2937 rs
.left
, rs
.top
, rs
.right
-rs
.left
, rs
.bottom
-rs
.top
,
2938 IsWindowVisible(hwndStatusProgress
) ? TRUE
: FALSE
);
2941 * TODO: Adjust the splitter positions:
2942 * - Vertical splitter (1) : fixed position from the left window side.
2943 * - Horizontal splitter (2): fixed position from the bottom window side.
2945 nVSplitPos
= min(max(nVSplitPos
, SPLIT_WIDTH
/2), cx
- SPLIT_WIDTH
/2); // OK
2946 nHSplitPos
= min(max(nHSplitPos
, SPLIT_WIDTH
/2), cy
- SPLIT_WIDTH
/2 - StatusHeight
); // FIXME!
2948 hdwp
= BeginDeferWindowPos(3);
2951 hdwp
= DeferWindowPos(hdwp
,
2955 nVSplitPos
- SPLIT_WIDTH
/2,
2957 SWP_NOZORDER
| SWP_NOACTIVATE
);
2960 hdwp
= DeferWindowPos(hdwp
,
2963 nVSplitPos
+ SPLIT_WIDTH
/2, 0,
2964 cx
- nVSplitPos
- SPLIT_WIDTH
/2,
2965 hwndEventDetails
? nHSplitPos
- SPLIT_WIDTH
/2
2966 : cy
- StatusHeight
,
2967 SWP_NOZORDER
| SWP_NOACTIVATE
);
2969 if (hwndEventDetails
&& hdwp
)
2970 hdwp
= DeferWindowPos(hdwp
,
2973 nVSplitPos
+ SPLIT_WIDTH
/2,
2974 nHSplitPos
+ SPLIT_WIDTH
/2,
2975 cx
- nVSplitPos
- SPLIT_WIDTH
/2,
2976 cy
- nHSplitPos
- SPLIT_WIDTH
/2 - StatusHeight
,
2977 SWP_NOZORDER
| SWP_NOACTIVATE
);
2980 EndDeferWindowPos(hdwp
);
2985 WndProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
2992 hMainMenu
= GetMenu(hWnd
);
3001 LPNMHDR hdr
= (LPNMHDR
)lParam
;
3003 if (hdr
->hwndFrom
== hwndListView
)
3007 case LVN_ITEMCHANGED
:
3009 LPNMLISTVIEW pnmv
= (LPNMLISTVIEW
)lParam
;
3011 if ( (pnmv
->uChanged
& LVIF_STATE
) && /* The state has changed */
3012 (pnmv
->uNewState
& LVIS_SELECTED
) /* The item has been (de)selected */ )
3014 if (hwndEventDetails
)
3015 SendMessageW(hwndEventDetails
, EVT_DISPLAY
, 0, 0);
3022 SendMessageW(hWnd
, WM_COMMAND
, IDM_EVENT_DETAILS
, 0);
3026 else if (hdr
->hwndFrom
== hwndTreeView
)
3030 case TVN_BEGINLABELEDIT
:
3032 HTREEITEM hItem
= ((LPNMTVDISPINFO
)lParam
)->item
.hItem
;
3034 /* Disable label editing for root nodes */
3035 return ((hItem
== htiSystemLogs
) ||
3036 (hItem
== htiAppLogs
) ||
3037 (hItem
== htiUserLogs
));
3040 case TVN_ENDLABELEDIT
:
3042 TVITEMW item
= ((LPNMTVDISPINFO
)lParam
)->item
;
3043 HTREEITEM hItem
= item
.hItem
;
3045 /* Disable label editing for root nodes */
3046 if ((hItem
== htiSystemLogs
) ||
3047 (hItem
== htiAppLogs
) ||
3048 (hItem
== htiUserLogs
))
3055 LPWSTR pszText
= item
.pszText
;
3057 /* Trim leading whitespace */
3058 while (*pszText
&& iswspace(*pszText
))
3072 case TVN_SELCHANGED
:
3074 PEVENTLOGFILTER EventLogFilter
=
3075 (PEVENTLOGFILTER
)((LPNMTREEVIEW
)lParam
)->itemNew
.lParam
;
3077 // FIXME: It might be nice to reference here the filter,
3078 // so that we don't have to reference/dereference it many times
3079 // in the other functions???
3081 // FIXME: This is a hack!!
3082 if (hwndEventDetails
&& EventLogFilter
)
3084 SendMessageW(hwndEventDetails
, EVT_SETFILTER
, 0, (LPARAM
)EventLogFilter
);
3090 * If we have selected a filter, enable the menu commands;
3091 * they will possibly be updated after events enumeration.
3093 EnableMenuItem(hMainMenu
, IDM_SAVE_EVENTLOG
, MF_BYCOMMAND
| MF_ENABLED
);
3094 EnableMenuItem(hMainMenu
, IDM_CLOSE_EVENTLOG
, MF_BYCOMMAND
| MF_ENABLED
);
3095 EnableMenuItem(hMainMenu
, IDM_CLEAR_EVENTS
, MF_BYCOMMAND
| MF_ENABLED
);
3096 EnableMenuItem(hMainMenu
, IDM_RENAME_EVENTLOG
, MF_BYCOMMAND
| MF_ENABLED
);
3097 EnableMenuItem(hMainMenu
, IDM_EVENTLOG_SETTINGS
, MF_BYCOMMAND
| MF_ENABLED
);
3101 EnableMenuItem(hMainMenu
, IDM_SAVE_EVENTLOG
, MF_BYCOMMAND
| MF_GRAYED
);
3102 EnableMenuItem(hMainMenu
, IDM_CLOSE_EVENTLOG
, MF_BYCOMMAND
| MF_GRAYED
);
3103 EnableMenuItem(hMainMenu
, IDM_CLEAR_EVENTS
, MF_BYCOMMAND
| MF_GRAYED
);
3104 EnableMenuItem(hMainMenu
, IDM_RENAME_EVENTLOG
, MF_BYCOMMAND
| MF_GRAYED
);
3105 EnableMenuItem(hMainMenu
, IDM_EVENTLOG_SETTINGS
, MF_BYCOMMAND
| MF_GRAYED
);
3109 * The enumeration thread that is triggered by EnumEvents
3110 * will set a new value for the 'ActiveFilter'.
3113 EnumEvents(EventLogFilter
);
3124 /* Parse the menu selections */
3125 switch (LOWORD(wParam
))
3127 case IDM_OPEN_EVENTLOG
:
3131 case IDM_SAVE_EVENTLOG
:
3132 SaveEventLog(GetSelectedFilter(NULL
));
3135 case IDM_CLOSE_EVENTLOG
:
3138 PEVENTLOGFILTER EventLogFilter
= GetSelectedFilter(&hti
);
3139 CloseUserEventLog(EventLogFilter
, hti
);
3143 case IDM_CLEAR_EVENTS
:
3145 PEVENTLOGFILTER EventLogFilter
= GetSelectedFilter(NULL
);
3146 if (EventLogFilter
&& ClearEvents(EventLogFilter
))
3147 Refresh(EventLogFilter
);
3151 case IDM_RENAME_EVENTLOG
:
3152 if (GetFocus() == hwndTreeView
)
3153 TreeView_EditLabel(hwndTreeView
, TreeView_GetSelection(hwndTreeView
));
3156 case IDM_EVENTLOG_SETTINGS
:
3158 PEVENTLOGFILTER EventLogFilter
= GetSelectedFilter(NULL
);
3159 // TODO: Check the returned value?
3161 EventLogProperties(hInst
, hWnd
, EventLogFilter
);
3165 case IDM_LIST_NEWEST
:
3167 CheckMenuRadioItem(hMainMenu
, IDM_LIST_NEWEST
, IDM_LIST_OLDEST
, IDM_LIST_NEWEST
, MF_BYCOMMAND
);
3168 if (!NewestEventsFirst
)
3170 NewestEventsFirst
= TRUE
;
3171 Refresh(GetSelectedFilter(NULL
));
3176 case IDM_LIST_OLDEST
:
3178 CheckMenuRadioItem(hMainMenu
, IDM_LIST_NEWEST
, IDM_LIST_OLDEST
, IDM_LIST_OLDEST
, MF_BYCOMMAND
);
3179 if (NewestEventsFirst
)
3181 NewestEventsFirst
= FALSE
;
3182 Refresh(GetSelectedFilter(NULL
));
3187 case IDM_EVENT_DETAILS
:
3189 // LPNMITEMACTIVATE lpnmitem = (LPNMITEMACTIVATE)lParam;
3190 PEVENTLOGFILTER EventLogFilter
= GetSelectedFilter(NULL
);
3191 if (/*lpnmitem->iItem != -1 &&*/ EventLogFilter
)
3193 EventLogFilter_AddRef(EventLogFilter
);
3194 DialogBoxParamW(hInst
,
3195 MAKEINTRESOURCEW(IDD_EVENTDETAILS_DLG
),
3198 (LPARAM
)EventLogFilter
);
3199 EventLogFilter_Release(EventLogFilter
);
3205 Refresh(GetSelectedFilter(NULL
));
3211 WCHAR szCopyright
[MAX_LOADSTRING
];
3213 hIcon
= LoadIconW(hInst
, MAKEINTRESOURCEW(IDI_EVENTVWR
));
3214 LoadStringW(hInst
, IDS_COPYRIGHT
, szCopyright
, ARRAYSIZE(szCopyright
));
3215 ShellAboutW(hWnd
, szTitle
, szCopyright
, hIcon
);
3216 DeleteObject(hIcon
);
3221 MessageBoxW(hwndMainWindow
,
3222 L
"Help not implemented yet!",
3224 MB_OK
| MB_ICONINFORMATION
);
3228 DestroyWindow(hWnd
);
3232 return DefWindowProcW(hWnd
, uMsg
, wParam
, lParam
);
3241 if (LOWORD(lParam
) != HTCLIENT
)
3245 ScreenToClient(hWnd
, &pt
);
3247 /* Set the cursor for the vertical splitter */
3248 if (pt
.x
>= nVSplitPos
- SPLIT_WIDTH
/2 && pt
.x
< nVSplitPos
+ SPLIT_WIDTH
/2 + 1)
3251 GetClientRect(hWnd
, &rect
);
3252 GetWindowRect(hwndStatus
, &rs
);
3253 if (pt
.y
>= rect
.top
&& pt
.y
< rect
.bottom
- (rs
.bottom
- rs
.top
))
3255 SetCursor(LoadCursorW(NULL
, IDC_SIZEWE
));
3260 /* Set the cursor for the horizontal splitter, if the Event details pane is displayed */
3261 if (hwndEventDetails
&&
3262 (pt
.y
>= nHSplitPos
- SPLIT_WIDTH
/2 && pt
.y
< nHSplitPos
+ SPLIT_WIDTH
/2 + 1))
3265 GetClientRect(hWnd
, &rect
);
3266 // GetWindowRect(hwndStatus, &rs);
3267 if (pt
.x
>= nVSplitPos
+ SPLIT_WIDTH
/2 + 1 /* rect.left + (rs.bottom - rs.top) */ &&
3270 SetCursor(LoadCursorW(NULL
, IDC_SIZENS
));
3277 case WM_LBUTTONDOWN
:
3279 INT x
= GET_X_LPARAM(lParam
);
3280 INT y
= GET_Y_LPARAM(lParam
);
3282 /* Reset the splitter state */
3285 /* Capture the cursor for the vertical splitter */
3286 if (x
>= nVSplitPos
- SPLIT_WIDTH
/2 && x
< nVSplitPos
+ SPLIT_WIDTH
/2 + 1)
3292 /* Capture the cursor for the horizontal splitter, if the Event details pane is displayed */
3293 if (hwndEventDetails
&&
3294 (y
>= nHSplitPos
- SPLIT_WIDTH
/2 && y
< nHSplitPos
+ SPLIT_WIDTH
/2 + 1))
3303 case WM_RBUTTONDOWN
:
3305 if (GetCapture() != hWnd
)
3308 /* Adjust the correct splitter position */
3310 nVSplitPos
= GET_X_LPARAM(lParam
);
3311 else if (bSplit
== 2)
3312 nHSplitPos
= GET_Y_LPARAM(lParam
);
3314 /* If we are splitting, resize the windows */
3317 GetClientRect(hWnd
, &rect
);
3318 ResizeWnd(rect
.right
- rect
.left
, rect
.bottom
- rect
.top
);
3321 /* Reset the splitter state */
3330 if (GetCapture() != hWnd
)
3333 /* Move the correct splitter */
3336 INT x
= GET_X_LPARAM(lParam
);
3338 GetClientRect(hWnd
, &rect
);
3340 x
= min(max(x
, SPLIT_WIDTH
/2), rect
.right
- rect
.left
- SPLIT_WIDTH
/2);
3341 if (nVSplitPos
!= x
)
3344 ResizeWnd(rect
.right
- rect
.left
, rect
.bottom
- rect
.top
);
3347 else if (bSplit
== 2)
3350 INT y
= GET_Y_LPARAM(lParam
);
3352 GetClientRect(hWnd
, &rect
);
3353 GetWindowRect(hwndStatus
, &rs
);
3355 y
= min(max(y
, SPLIT_WIDTH
/2), rect
.bottom
- rect
.top
- SPLIT_WIDTH
/2 - (rs
.bottom
- rs
.top
));
3356 if (nHSplitPos
!= y
)
3359 ResizeWnd(rect
.right
- rect
.left
, rect
.bottom
- rect
.top
);
3367 if (wParam
!= SIZE_MINIMIZED
)
3369 SendMessageW(hwndStatus
, WM_SIZE
, 0, 0);
3370 ResizeWnd(LOWORD(lParam
), HIWORD(lParam
));
3373 /* Fall through the default case */
3377 return DefWindowProcW(hWnd
, uMsg
, wParam
, lParam
);
3386 InitPropertiesDlg(HWND hDlg
, PEVENTLOG EventLog
)
3388 LPWSTR lpLogName
= EventLog
->LogName
;
3390 DWORD Result
, dwType
;
3391 DWORD dwMaxSize
= 0, dwRetention
= 0;
3393 WIN32_FIND_DATAW FileInfo
; // WIN32_FILE_ATTRIBUTE_DATA
3394 ULARGE_INTEGER FileSize
;
3395 WCHAR wszBuf
[MAX_PATH
];
3396 WCHAR szTemp
[MAX_LOADSTRING
];
3404 if (EventLog
->Permanent
)
3407 cbKeyPath
= (wcslen(EVENTLOG_BASE_KEY
) + wcslen(lpLogName
) + 1) * sizeof(WCHAR
);
3408 KeyPath
= HeapAlloc(GetProcessHeap(), 0, cbKeyPath
);
3414 StringCbCopyW(KeyPath
, cbKeyPath
, EVENTLOG_BASE_KEY
);
3415 StringCbCatW(KeyPath
, cbKeyPath
, lpLogName
);
3417 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE
, KeyPath
, 0, KEY_QUERY_VALUE
, &hLogKey
) != ERROR_SUCCESS
)
3419 HeapFree(GetProcessHeap(), 0, KeyPath
);
3422 HeapFree(GetProcessHeap(), 0, KeyPath
);
3425 cbData
= sizeof(dwMaxSize
);
3426 Result
= RegQueryValueExW(hLogKey
,
3432 if ((Result
!= ERROR_SUCCESS
) || (dwType
!= REG_DWORD
))
3434 // dwMaxSize = 512 * 1024; /* 512 kBytes */
3440 cbData
= sizeof(dwRetention
);
3441 Result
= RegQueryValueExW(hLogKey
,
3445 (LPBYTE
)&dwRetention
,
3447 if ((Result
!= ERROR_SUCCESS
) || (dwType
!= REG_DWORD
))
3449 /* On Windows 2003 it is 604800 (secs) == 7 days */
3452 /* Convert in days, rounded up */ // ROUND_UP
3453 // dwRetention = ROUND_UP(dwRetention, 24*3600) / (24*3600);
3454 dwRetention
= (dwRetention
+ 24*3600 - 1) / (24*3600);
3457 RegCloseKey(hLogKey
);
3464 SetDlgItemTextW(hDlg
, IDC_DISPLAYNAME
, lpLogName
); // FIXME!
3465 SetDlgItemTextW(hDlg
, IDC_LOGNAME
, lpLogName
);
3467 FileName
= EventLog
->FileName
;
3468 if (FileName
&& *FileName
)
3470 ExpandEnvironmentStringsW(FileName
, wszBuf
, ARRAYSIZE(wszBuf
));
3477 SetDlgItemTextW(hDlg
, IDC_LOGFILE
, FileName
);
3479 if (FileName
&& *FileName
)
3482 * The general problem here (and in the shell as well) is that
3483 * GetFileAttributesEx fails for files that are opened without
3484 * shared access. To retrieve file information for those we need
3485 * to use something else: FindFirstFile, on the full file name.
3487 Success
= GetFileAttributesExW(FileName
,
3488 GetFileExInfoStandard
,
3489 (LPWIN32_FILE_ATTRIBUTE_DATA
)&FileInfo
);
3492 HANDLE hFind
= FindFirstFileW(FileName
, &FileInfo
);
3493 Success
= (hFind
!= INVALID_HANDLE_VALUE
);
3503 /* Starting there, FileName becomes invalid because we are reusing wszBuf */
3507 FileSize
.u
.LowPart
= FileInfo
.nFileSizeLow
;
3508 FileSize
.u
.HighPart
= FileInfo
.nFileSizeHigh
;
3509 if (FormatFileSizeWithBytes(&FileSize
, wszBuf
, ARRAYSIZE(wszBuf
)))
3510 SetDlgItemTextW(hDlg
, IDC_SIZE_LABEL
, wszBuf
);
3512 LoadStringW(hInst
, IDS_NOT_AVAILABLE
, szTemp
, ARRAYSIZE(szTemp
));
3514 if (GetFileTimeString(&FileInfo
.ftCreationTime
, wszBuf
, ARRAYSIZE(wszBuf
)))
3515 SetDlgItemTextW(hDlg
, IDC_CREATED_LABEL
, wszBuf
);
3517 SetDlgItemTextW(hDlg
, IDC_CREATED_LABEL
, szTemp
);
3519 if (GetFileTimeString(&FileInfo
.ftLastWriteTime
, wszBuf
, ARRAYSIZE(wszBuf
)))
3520 SetDlgItemTextW(hDlg
, IDC_MODIFIED_LABEL
, wszBuf
);
3522 SetDlgItemTextW(hDlg
, IDC_MODIFIED_LABEL
, szTemp
);
3524 if (GetFileTimeString(&FileInfo
.ftLastAccessTime
, wszBuf
, ARRAYSIZE(wszBuf
)))
3525 SetDlgItemTextW(hDlg
, IDC_ACCESSED_LABEL
, wszBuf
);
3527 SetDlgItemTextW(hDlg
, IDC_MODIFIED_LABEL
, szTemp
);
3531 LoadStringW(hInst
, IDS_NOT_AVAILABLE
, szTemp
, ARRAYSIZE(szTemp
));
3533 SetDlgItemTextW(hDlg
, IDC_SIZE_LABEL
, szTemp
);
3534 SetDlgItemTextW(hDlg
, IDC_CREATED_LABEL
, szTemp
);
3535 SetDlgItemTextW(hDlg
, IDC_MODIFIED_LABEL
, szTemp
);
3536 SetDlgItemTextW(hDlg
, IDC_ACCESSED_LABEL
, szTemp
);
3539 if (EventLog
->Permanent
)
3541 SendDlgItemMessageW(hDlg
, IDC_UPDOWN_MAXLOGSIZE
, UDM_SETRANGE32
, (WPARAM
)1, (LPARAM
)0x3FFFC0);
3542 SendDlgItemMessageW(hDlg
, IDC_UPDOWN_EVENTS_AGE
, UDM_SETRANGE
, 0, (LPARAM
)MAKELONG(365, 1));
3544 SetDlgItemInt(hDlg
, IDC_EDIT_MAXLOGSIZE
, dwMaxSize
, FALSE
);
3545 SetDlgItemInt(hDlg
, IDC_EDIT_EVENTS_AGE
, dwRetention
, FALSE
);
3547 if (dwRetention
== 0)
3549 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_OVERWRITE_AS_NEEDED
);
3550 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, FALSE
);
3551 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, FALSE
);
3553 else if (dwRetention
== INFINITE
)
3555 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_NO_OVERWRITE
);
3556 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, FALSE
);
3557 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, FALSE
);
3561 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_OVERWRITE_OLDER_THAN
);
3562 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, TRUE
);
3563 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, TRUE
);
3568 // TODO: Hide the unused controls! Or, just use another type of property sheet!
3572 /* Message handler for EventLog Properties dialog */
3574 EventLogPropProc(HWND hDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
3578 EventLog
= (PEVENTLOG
)GetWindowLongPtrW(hDlg
, DWLP_USER
);
3584 EventLog
= (PEVENTLOG
)((LPPROPSHEETPAGE
)lParam
)->lParam
;
3585 SetWindowLongPtrW(hDlg
, DWLP_USER
, (LONG_PTR
)EventLog
);
3587 InitPropertiesDlg(hDlg
, EventLog
);
3589 PropSheet_UnChanged(GetParent(hDlg
), hDlg
);
3590 return (INT_PTR
)TRUE
;
3594 return (INT_PTR
)TRUE
;
3597 switch (LOWORD(wParam
))
3601 EndDialog(hDlg
, LOWORD(wParam
));
3602 return (INT_PTR
)TRUE
;
3604 case IDC_OVERWRITE_AS_NEEDED
:
3606 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_OVERWRITE_AS_NEEDED
);
3607 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, FALSE
);
3608 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, FALSE
);
3612 case IDC_OVERWRITE_OLDER_THAN
:
3614 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_OVERWRITE_OLDER_THAN
);
3615 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, TRUE
);
3616 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, TRUE
);
3620 case IDC_NO_OVERWRITE
:
3622 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_NO_OVERWRITE
);
3623 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, FALSE
);
3624 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, FALSE
);
3630 L
"Help not implemented yet!",
3632 MB_OK
| MB_ICONINFORMATION
);
3633 return (INT_PTR
)TRUE
;
3641 return (INT_PTR
)FALSE
;
3645 EventLogProperties(HINSTANCE hInstance
, HWND hWndParent
, PEVENTLOGFILTER EventLogFilter
)
3648 PROPSHEETHEADERW psh
;
3649 PROPSHEETPAGEW psp
[1]; // 2
3652 * Bail out if there is no available filter, or if the filter
3653 * contains more than one log.
3655 if (!EventLogFilter
)
3658 EventLogFilter_AddRef(EventLogFilter
);
3660 if (EventLogFilter
->NumOfEventLogs
> 1 ||
3661 EventLogFilter
->EventLogs
[0] == NULL
)
3667 psh
.dwSize
= sizeof(psh
);
3668 psh
.dwFlags
= PSH_PROPSHEETPAGE
/*| PSH_USEICONID */ | PSH_PROPTITLE
| PSH_HASHELP
/*| PSH_NOCONTEXTHELP */ /*| PSH_USECALLBACK */;
3669 psh
.hInstance
= hInstance
;
3670 psh
.hwndParent
= hWndParent
;
3671 // psh.pszIcon = MAKEINTRESOURCEW(IDI_APPICON); // Disabled because it only sets the small icon; the big icon is a stretched version of the small one.
3672 psh
.pszCaption
= EventLogFilter
->EventLogs
[0]->LogName
;
3675 psh
.nPages
= ARRAYSIZE(psp
);
3676 // psh.pfnCallback = PropSheetCallback;
3678 /* Log properties page */
3679 psp
[0].dwSize
= sizeof(psp
[0]);
3680 psp
[0].dwFlags
= PSP_HASHELP
;
3681 psp
[0].hInstance
= hInstance
;
3682 psp
[0].pszTemplate
= MAKEINTRESOURCEW(IDD_LOGPROPERTIES_GENERAL
);
3683 psp
[0].pfnDlgProc
= EventLogPropProc
;
3684 psp
[0].lParam
= (LPARAM
)EventLogFilter
->EventLogs
[0];
3687 /* TODO: Log sources page */
3688 psp
[1].dwSize
= sizeof(psp
[1]);
3689 psp
[1].dwFlags
= PSP_HASHELP
;
3690 psp
[1].hInstance
= hInstance
;
3691 psp
[1].pszTemplate
= MAKEINTRESOURCEW(IDD_GENERAL_PAGE
);
3692 psp
[1].pfnDlgProc
= GeneralPageWndProc
;
3693 psp
[0].lParam
= (LPARAM
)EventLogFilter
->EventLogs
[0];
3696 /* Create the property sheet */
3697 ret
= PropertySheetW(&psh
);
3700 EventLogFilter_Release(EventLogFilter
);
3704 /* Message handler for Event Details dialog */
3705 static HWND hWndDetailsCtrl
= NULL
; // May go into the DWLP_USER
3706 static HWND hWndGrip
= NULL
;
3707 static INT cxMin
, cyMin
; // In window coordinates
3708 static INT cxOld
, cyOld
; // In client coordinates
3711 EventDetails(HWND hDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
3718 INT sbVXSize
, sbHYSize
;
3721 hWndDetailsCtrl
= CreateEventDetailsCtrl(hInst
, hDlg
, lParam
);
3722 if (!hWndDetailsCtrl
)
3725 return (INT_PTR
)TRUE
;
3728 /* Create a size grip if the dialog has a sizing border */
3729 GetClientRect(hDlg
, &rcWnd
);
3730 dwStyle
= GetWindowLongPtrW(hDlg
, GWL_STYLE
);
3731 sbVXSize
= GetSystemMetrics(SM_CXVSCROLL
);
3732 sbHYSize
= GetSystemMetrics(SM_CYHSCROLL
);
3733 if (dwStyle
& WS_THICKFRAME
/* == WS_SIZEBOX */)
3735 hWndGrip
= CreateWindowW(WC_SCROLLBARW
,
3737 WS_CHILD
| WS_VISIBLE
| /**/ WS_CLIPSIBLINGS
| /**/ SBS_SIZEGRIP
| SBS_SIZEBOXBOTTOMRIGHTALIGN
,
3738 rcWnd
.right
- sbVXSize
,
3739 rcWnd
.bottom
- sbHYSize
,
3747 // SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)hWndDetailsCtrl);
3750 * Compute the minimum window size (in window coordinates) by
3751 * adding the widths/heights of the "Help" and "Close" buttons,
3752 * together with the margins, and add some minimal spacing
3753 * between the buttons.
3755 GetWindowRect(hDlg
, &rcWnd
);
3758 GetWindowRect(GetDlgItem(hDlg
, IDHELP
), &rect
);
3759 cxMin
+= (rect
.right
- rect
.left
) + (rect
.left
- rcWnd
.left
); // == (rect.right - rcWnd.left);
3760 cyMin
+= (rect
.bottom
- rect
.top
) + (rcWnd
.bottom
- rect
.bottom
); // == (rcWnd.bottom - rect.top);
3762 GetWindowRect(GetDlgItem(hDlg
, IDOK
), &rect
);
3763 cxMin
+= (rect
.right
- rect
.left
) + (rcWnd
.right
- rect
.right
); // == (rcWnd.right - rect.left);
3764 cyMin
+= (rect
.bottom
- rect
.top
) + (rcWnd
.bottom
- rect
.bottom
); // == (rcWnd.bottom - rect.top);
3767 * Convert the window rect from window to client coordinates
3768 * in order to retrieve the sizes of the left and top margins,
3769 * and add some extra space.
3771 MapWindowPoints(HWND_DESKTOP
/*NULL*/, hDlg
, (LPPOINT
)&rcWnd
, sizeof(RECT
)/sizeof(POINT
));
3773 cxMin
+= -2*rcWnd
.left
; // Minimal spacing between the buttons == 2 * left margin
3774 cyMin
+= -rcWnd
.top
+ 12; // Add some space on top
3776 GetClientRect(hDlg
, &rcWnd
);
3777 cxOld
= rcWnd
.right
- rcWnd
.left
;
3778 cyOld
= rcWnd
.bottom
- rcWnd
.top
;
3780 /* Show event info on dialog control */
3781 SendMessageW(hWndDetailsCtrl
, EVT_DISPLAY
, 0, 0);
3783 // SetWindowPos(hWndDetailsCtrl, NULL,
3785 // (rcWnd.right - rcWnd.left),
3786 // (rcWnd.bottom - rcWnd.top),
3787 // SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
3790 * Hide the placeholder static control and show the event details
3791 * control instead. Note that the placeholder is here so far just
3792 * to get the dimensions right in the dialog resource editor.
3793 * I plan to remove it and use a custom control with a suitable
3794 * window class for it, that would create the event details control
3797 ShowWindow(GetDlgItem(hDlg
, IDC_STATIC
), SW_HIDE
);
3798 ShowWindow(hWndDetailsCtrl
, SW_SHOW
);
3799 return (INT_PTR
)TRUE
;
3803 if (IsWindow(hWndDetailsCtrl
))
3804 DestroyWindow(hWndDetailsCtrl
);
3805 hWndDetailsCtrl
= NULL
;
3806 return (INT_PTR
)TRUE
;
3809 switch (LOWORD(wParam
))
3813 EndDialog(hDlg
, LOWORD(wParam
));
3814 return (INT_PTR
)TRUE
;
3818 L
"Help not implemented yet!",
3820 MB_OK
| MB_ICONINFORMATION
);
3821 return (INT_PTR
)TRUE
;
3829 if (((HWND
)wParam
== hWndGrip
) && (LOWORD(lParam
) == HTCLIENT
))
3831 SetCursor(LoadCursorW(NULL
, IDC_SIZENWSE
));
3832 SetWindowLongPtrW(hDlg
, DWLP_MSGRESULT
, TRUE
);
3833 return (INT_PTR
)TRUE
;
3839 /* Forbid resizing the dialog smaller than its minimal size */
3840 PRECT dragRect
= (PRECT
)lParam
;
3842 if ((wParam
== WMSZ_LEFT
) || (wParam
== WMSZ_TOPLEFT
) || (wParam
== WMSZ_BOTTOMLEFT
))
3844 if (dragRect
->right
- dragRect
->left
< cxMin
)
3845 dragRect
->left
= dragRect
->right
- cxMin
;
3848 if ((wParam
== WMSZ_RIGHT
) || (wParam
== WMSZ_TOPRIGHT
) || (wParam
== WMSZ_BOTTOMRIGHT
))
3850 if (dragRect
->right
- dragRect
->left
< cxMin
)
3851 dragRect
->right
= dragRect
->left
+ cxMin
;
3854 if ((wParam
== WMSZ_TOP
) || (wParam
== WMSZ_TOPLEFT
) || (wParam
== WMSZ_TOPRIGHT
))
3856 if (dragRect
->bottom
- dragRect
->top
< cyMin
)
3857 dragRect
->top
= dragRect
->bottom
- cyMin
;
3860 if ((wParam
== WMSZ_BOTTOM
) || (wParam
== WMSZ_BOTTOMLEFT
) || (wParam
== WMSZ_BOTTOMRIGHT
))
3862 if (dragRect
->bottom
- dragRect
->top
< cyMin
)
3863 dragRect
->bottom
= dragRect
->top
+ cyMin
;
3866 SetWindowLongPtrW(hDlg
, DWLP_MSGRESULT
, TRUE
);
3867 return (INT_PTR
)TRUE
;
3872 INT cx
= LOWORD(lParam
);
3873 INT cy
= HIWORD(lParam
);
3879 hdwp
= BeginDeferWindowPos(4);
3881 /* Resize the event details control window */
3883 hItemWnd
= hWndDetailsCtrl
;
3884 GetWindowRect(hItemWnd
, &rect
);
3885 MapWindowPoints(HWND_DESKTOP
/*NULL*/, hDlg
, (LPPOINT
)&rect
, sizeof(RECT
)/sizeof(POINT
));
3888 hdwp
= DeferWindowPos(hdwp
,
3892 (rect
.right
- rect
.left
) + (cx
- cxOld
),
3893 (rect
.bottom
- rect
.top
) + (cy
- cyOld
),
3894 SWP_NOMOVE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
3896 /* Move the buttons */
3898 hItemWnd
= GetDlgItem(hDlg
, IDHELP
);
3899 GetWindowRect(hItemWnd
, &rect
);
3900 MapWindowPoints(HWND_DESKTOP
/*NULL*/, hDlg
, (LPPOINT
)&rect
, sizeof(RECT
)/sizeof(POINT
));
3903 hdwp
= DeferWindowPos(hdwp
,
3907 rect
.top
+ (cy
- cyOld
),
3909 SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
3911 hItemWnd
= GetDlgItem(hDlg
, IDOK
);
3912 GetWindowRect(hItemWnd
, &rect
);
3913 MapWindowPoints(HWND_DESKTOP
/*NULL*/, hDlg
, (LPPOINT
)&rect
, sizeof(RECT
)/sizeof(POINT
));
3916 hdwp
= DeferWindowPos(hdwp
,
3919 rect
.left
+ (cx
- cxOld
),
3920 rect
.top
+ (cy
- cyOld
),
3922 SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
3924 /* Move the size grip */
3925 if (hWndGrip
&& hdwp
)
3927 GetWindowRect(hWndGrip
, &rect
);
3928 MapWindowPoints(HWND_DESKTOP
/*NULL*/, hDlg
, (LPPOINT
)&rect
, sizeof(RECT
)/sizeof(POINT
));
3930 hdwp
= DeferWindowPos(hdwp
,
3933 rect
.left
+ (cx
- cxOld
),
3934 rect
.top
+ (cy
- cyOld
),
3936 SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
3940 EndDeferWindowPos(hdwp
);
3942 /* Hide the size grip if we are in maximized mode */
3944 ShowWindow(hWndGrip
, (wParam
== SIZE_MAXIMIZED
) ? SW_HIDE
: SW_SHOW
);
3949 SetWindowLongPtrW(hDlg
, DWLP_MSGRESULT
, 0);
3950 return (INT_PTR
)TRUE
;
3954 return (INT_PTR
)FALSE
;