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: Colors dialog
24 * PROGRAMMERS: Marc Piulachs (marc.piulachs at codexchange [dot] net)
26 * Hermes Belusca-Maito
32 #define WIN32_NO_STATUS
41 #include <ndk/rtlfuncs.h>
43 #define ROUND_DOWN(n, align) (((ULONG)n) & ~((align) - 1l))
44 #define ROUND_UP(n, align) ROUND_DOWN(((ULONG)n) + (align) - 1, (align))
55 * windowsx.h extensions
57 #define EnableDlgItem(hDlg, nID, bEnable) \
58 EnableWindow(GetDlgItem((hDlg), (nID)), (bEnable))
60 #define ProgressBar_SetPos(hwndCtl,pos) \
61 ((int)SNDMSG((hwndCtl),PBM_SETPOS,(WPARAM)(int)(pos),(LPARAM)0))
62 #define ProgressBar_SetRange(hwndCtl,range) \
63 ((int)SNDMSG((hwndCtl),PBM_SETRANGE,(WPARAM)0,(LPARAM)(range)))
64 #define ProgressBar_SetStep(hwndCtl,inc) \
65 ((int)SNDMSG((hwndCtl),PBM_SETSTEP,(WPARAM)(int)(inc),(LPARAM)0))
66 #define ProgressBar_StepIt(hwndCtl) \
67 ((int)SNDMSG((hwndCtl),PBM_STEPIT,(WPARAM)0,(LPARAM)0))
69 #define StatusBar_GetItemRect(hwndCtl,index,lprc) \
70 ((BOOL)SNDMSG((hwndCtl),SB_GETRECT,(WPARAM)(int)(index),(LPARAM)(RECT*)(lprc)))
71 #define StatusBar_SetText(hwndCtl,index,data) \
72 ((BOOL)SNDMSG((hwndCtl),SB_SETTEXT,(WPARAM)(index),(LPARAM)(data)))
77 /* Missing RichEdit flags in our richedit.h */
78 #define AURL_ENABLEURL 1
79 #define AURL_ENABLEEMAILADDR 2
80 #define AURL_ENABLETELNO 4
81 #define AURL_ENABLEEAURLS 8
82 #define AURL_ENABLEDRIVELETTERS 16
89 #define LVM_PROGRESS (WM_APP + 1)
92 static const LPCWSTR szWindowClass
= L
"EVENTVWR"; /* The main window class name */
93 static const WCHAR EVENTLOG_BASE_KEY
[] = L
"SYSTEM\\CurrentControlSet\\Services\\EventLog\\";
95 /* The 3 system logs that should always exist in the user's system */
96 static const LPCWSTR SystemLogs
[] =
103 /* MessageFile message buffer size */
104 #define EVENT_MESSAGE_EVENTTEXT_BUFFER 1024*10
105 #define EVENT_MESSAGE_FILE_BUFFER 1024*10
106 #define EVENT_DLL_SEPARATOR L";"
107 #define EVENT_CATEGORY_MESSAGE_FILE L"CategoryMessageFile"
108 #define EVENT_MESSAGE_FILE L"EventMessageFile"
109 #define EVENT_PARAMETER_MESSAGE_FILE L"ParameterMessageFile"
111 #define MAX_LOADSTRING 255
112 #define ENTRY_SIZE 2056
114 #define SPLIT_WIDTH 4
117 HINSTANCE hInst
; /* Current instance */
118 WCHAR szTitle
[MAX_LOADSTRING
]; /* The title bar text */
119 WCHAR szTitleTemplate
[MAX_LOADSTRING
]; /* The logged-on title bar text */
120 WCHAR szStatusBarTemplate
[MAX_LOADSTRING
]; /* The status bar text */
121 WCHAR szLoadingWait
[MAX_LOADSTRING
]; /* The "Loading, please wait..." text */
122 WCHAR szEmptyList
[MAX_LOADSTRING
]; /* The "There are no items to show in this view" text */
123 WCHAR szSaveFilter
[MAX_LOADSTRING
]; /* Filter Mask for the save Dialog */
124 INT nSplitPos
; /* Splitter position */
125 HWND hwndMainWindow
; /* Main window */
126 HWND hwndTreeView
; /* TreeView control */
127 HWND hwndListView
; /* ListView control */
128 HWND hwndStatus
; /* Status bar */
129 HWND hwndStatusProgress
; /* Progress bar in the status bar */
130 HMENU hMainMenu
; /* The application's main menu */
132 HTREEITEM htiSystemLogs
= NULL
, htiAppLogs
= NULL
, htiUserLogs
= NULL
;
136 * Structure that caches information about an opened event log.
138 typedef struct _EVENTLOG
140 LIST_ENTRY ListEntry
;
142 // HANDLE hEventLog; // At least for user logs, a handle is kept opened (by eventlog service) as long as the event viewer has the focus on this log.
144 PWSTR ComputerName
; // Computer where the log resides
146 /** Cached information **/
147 PWSTR LogName
; // Internal name (from registry, or file path for user logs)
148 PWSTR FileName
; // Cached, for user logs; retrieved once (at startup) from registry for system logs (i.e. may be different from the one opened by the eventlog service)
149 // PWSTR DisplayName; // The default value is the one computed; can be modified by the user for this local session only.
150 // We can use the TreeView' item name for the DisplayName...
151 BOOL Permanent
; // TRUE: system log; FALSE: user log
153 /** Volatile information **/
155 // ULONG MaxSize; // Always retrieved from registry (only valid for system logs)
156 // ULONG Retention; // Always retrieved from registry (only valid for system logs)
157 } EVENTLOG
, *PEVENTLOG
;
159 typedef struct _EVENTLOGFILTER
161 LIST_ENTRY ListEntry
;
165 // HANDLE hEnumEventsThread;
166 // HANDLE hStopEnumEvent;
168 // PWSTR DisplayName; // The default value is the one computed; can be modified by the user for this local session only.
169 // We can use the TreeView' item name for the DisplayName...
181 * The following three string filters are multi-strings that enumerate
182 * the list of sources/users/computers to be shown. If a string points
183 * to an empty string: "\0", it filters for an empty source/user/computer.
184 * If a string points to NULL, it filters for all sources/users/computers.
190 /* List of event logs maintained by this filter */
191 ULONG NumOfEventLogs
;
192 PEVENTLOG EventLogs
[ANYSIZE_ARRAY
];
193 } EVENTLOGFILTER
, *PEVENTLOGFILTER
;
195 /* Global event records cache for the current active event log filter */
196 DWORD g_TotalRecords
= 0;
197 PEVENTLOGRECORD
*g_RecordPtrs
= NULL
;
199 /* Lists of event logs and event log filters */
200 LIST_ENTRY EventLogList
;
201 LIST_ENTRY EventLogFilterList
;
202 PEVENTLOGFILTER ActiveFilter
= NULL
;
203 BOOL NewestEventsFirst
= TRUE
;
205 HANDLE hEnumEventsThread
= NULL
;
206 HANDLE hStopEnumEvent
= NULL
;
209 * Setting EnumFilter to a valid pointer and raising the hStartEnumEvent event
210 * triggers the event-enumerator thread to perform a new enumeration.
212 PEVENTLOGFILTER EnumFilter
= NULL
;
213 HANDLE hStartStopEnumEvent
= NULL
; // End-of-application event
214 HANDLE hStartEnumEvent
= NULL
; // Command event
216 /* Default Open/Save-As dialog box */
219 typedef struct _DETAILDATA
221 PEVENTLOGFILTER EventLogFilter
;
223 HFONT hMonospaceFont
;
224 } DETAILDATA
, *PDETAILDATA
;
227 /* Forward declarations of functions included in this code module */
230 StartStopEnumEventsThread(IN LPVOID lpParameter
);
232 VOID
BuildLogListAndFilterList(IN LPCWSTR lpComputerName
);
233 VOID
FreeLogList(VOID
);
234 VOID
FreeLogFilterList(VOID
);
236 ATOM
MyRegisterClass(HINSTANCE
);
237 BOOL
InitInstance(HINSTANCE
, int);
238 VOID
ExitInstance(HINSTANCE
);
239 LRESULT CALLBACK
WndProc(HWND
, UINT
, WPARAM
, LPARAM
);
240 INT_PTR
EventLogProperties(HINSTANCE
, HWND
, PEVENTLOGFILTER
);
241 INT_PTR CALLBACK
EventDetails(HWND
, UINT
, WPARAM
, LPARAM
);
245 wWinMain(HINSTANCE hInstance
,
246 HINSTANCE hPrevInstance
,
251 INITCOMMONCONTROLSEX iccx
;
256 UNREFERENCED_PARAMETER(hPrevInstance
);
257 UNREFERENCED_PARAMETER(lpCmdLine
);
259 /* Whenever any of the common controls are used in your app,
260 * you must call InitCommonControlsEx() to register the classes
261 * for those controls. */
262 iccx
.dwSize
= sizeof(iccx
);
263 iccx
.dwICC
= ICC_LISTVIEW_CLASSES
;
264 InitCommonControlsEx(&iccx
);
266 /* Load the RichEdit DLL to add support for RichEdit controls */
267 hRichEdit
= LoadLibraryW(L
"riched20.dll");
271 msg
.wParam
= (WPARAM
)-1;
273 /* Initialize global strings */
274 LoadStringW(hInstance
, IDS_APP_TITLE
, szTitle
, ARRAYSIZE(szTitle
));
275 LoadStringW(hInstance
, IDS_APP_TITLE_EX
, szTitleTemplate
, ARRAYSIZE(szTitleTemplate
));
276 LoadStringW(hInstance
, IDS_STATUS_MSG
, szStatusBarTemplate
, ARRAYSIZE(szStatusBarTemplate
));
277 LoadStringW(hInstance
, IDS_LOADING_WAIT
, szLoadingWait
, ARRAYSIZE(szLoadingWait
));
278 LoadStringW(hInstance
, IDS_NO_ITEMS
, szEmptyList
, ARRAYSIZE(szEmptyList
));
280 if (!MyRegisterClass(hInstance
))
283 /* Perform application initialization */
284 if (!InitInstance(hInstance
, nCmdShow
))
287 hAccelTable
= LoadAcceleratorsW(hInstance
, MAKEINTRESOURCEW(IDA_EVENTVWR
));
289 /* Create the Start/Stop enumerator thread */
290 // Manual-reset event
291 hStartStopEnumEvent
= CreateEventW(NULL
, TRUE
, FALSE
, NULL
);
292 if (!hStartStopEnumEvent
)
296 hStartEnumEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
297 if (!hStartEnumEvent
)
300 hThread
= CreateThread(NULL
, 0,
301 StartStopEnumEventsThread
,
306 /* Retrieve the available event logs on this computer and create filters for them */
307 InitializeListHead(&EventLogList
);
308 InitializeListHead(&EventLogFilterList
);
309 // TODO: Implement connection to remote computer...
310 // At the moment we only support the user local computer.
311 BuildLogListAndFilterList(NULL
);
313 // TODO: If the user wants to open an external event log with the Event Log Viewer
314 // (via the command line), it's here that the log should be opened.
316 /* Main message loop */
317 while (GetMessageW(&msg
, NULL
, 0, 0))
319 if (!TranslateAcceleratorW(hwndMainWindow
, hAccelTable
, &msg
))
321 TranslateMessage(&msg
);
322 DispatchMessageW(&msg
);
326 SetEvent(hStartStopEnumEvent
);
327 WaitForSingleObject(hThread
, INFINITE
);
328 CloseHandle(hThread
);
330 /* Free the filters list and the event logs list */
337 CloseHandle(hStartEnumEvent
);
338 if (hStartStopEnumEvent
)
339 CloseHandle(hStartStopEnumEvent
);
341 ExitInstance(hInstance
);
344 FreeLibrary(hRichEdit
);
346 return (int)msg
.wParam
;
350 /* GENERIC HELPER FUNCTIONS ***************************************************/
353 ShowLastWin32Error(VOID
)
356 LPWSTR lpMessageBuffer
;
358 dwError
= GetLastError();
359 FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS
,
363 (LPWSTR
)&lpMessageBuffer
,
367 MessageBoxW(hwndMainWindow
, lpMessageBuffer
, szTitle
, MB_OK
| MB_ICONERROR
);
368 LocalFree(lpMessageBuffer
);
372 EventTimeToSystemTime(IN DWORD EventTime
,
373 OUT PSYSTEMTIME pSystemTime
)
375 SYSTEMTIME st1970
= { 1970, 1, 0, 1, 0, 0, 0, 0 };
383 uUCT
.ft
.dwHighDateTime
= 0;
384 uUCT
.ft
.dwLowDateTime
= EventTime
;
385 SystemTimeToFileTime(&st1970
, &u1970
.ft
);
386 uUCT
.ll
= uUCT
.ll
* 10000000 + u1970
.ll
;
387 FileTimeToLocalFileTime(&uUCT
.ft
, &ftLocal
);
388 FileTimeToSystemTime(&ftLocal
, pSystemTime
);
392 * This function takes in entry a path to a single DLL, in which
393 * the message string of ID dwMessageId has to be searched.
394 * The other parameters are similar to those of the FormatMessageW API.
397 GetMessageStringFromDll(
398 IN LPCWSTR lpMessageDll
,
399 IN DWORD dwFlags
, // If we always use the same flags, just remove this param...
400 IN DWORD dwMessageId
,
402 IN
va_list* Arguments OPTIONAL
)
406 LPWSTR lpMsgBuf
= NULL
;
408 hLibrary
= LoadLibraryExW(lpMessageDll
, NULL
,
409 /* LOAD_LIBRARY_AS_IMAGE_RESOURCE | */ LOAD_LIBRARY_AS_DATAFILE
);
410 if (hLibrary
== NULL
)
416 * Retrieve the message string without appending extra newlines.
417 * Wrap in SEH to protect from invalid string parameters.
421 dwLength
= FormatMessageW(dwFlags
,
422 /* FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
423 FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, */
426 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
),
431 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
436 * An exception occurred while calling FormatMessage, this is usually
437 * the sign that a parameter was invalid, either 'lpMsgBuf' was NULL
438 * but we did not pass the flag FORMAT_MESSAGE_ALLOCATE_BUFFER, or the
439 * array pointer 'Arguments' was NULL or did not contain enough elements,
440 * and we did not pass the flag FORMAT_MESSAGE_IGNORE_INSERTS, and the
441 * message string expected too many inserts.
442 * In this last case only, we can call again FormatMessage but ignore
443 * explicitely the inserts. The string that we will return to the user
444 * will not be pre-formatted.
446 if (((dwFlags
& FORMAT_MESSAGE_ALLOCATE_BUFFER
) || lpMsgBuf
) &&
447 !(dwFlags
& FORMAT_MESSAGE_IGNORE_INSERTS
))
449 /* Remove any possible harmful flags and always ignore inserts */
450 dwFlags
&= ~FORMAT_MESSAGE_ARGUMENT_ARRAY
;
451 dwFlags
|= FORMAT_MESSAGE_IGNORE_INSERTS
;
453 /* If this call also throws an exception, we are really dead */
454 dwLength
= FormatMessageW(dwFlags
,
457 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
),
467 FreeLibrary(hLibrary
);
473 ASSERT(lpMsgBuf
== NULL
);
485 * This function takes in entry a comma-separated list of DLLs, in which
486 * the message string of ID dwMessageId has to be searched.
487 * The other parameters are similar to those of the FormatMessageW API.
490 GetMessageStringFromDllList(
491 IN LPCWSTR lpMessageDllList
,
492 IN DWORD dwFlags
, // If we always use the same flags, just remove this param...
493 IN DWORD dwMessageId
,
495 IN
va_list* Arguments OPTIONAL
)
497 BOOL Success
= FALSE
;
499 LPWSTR szMessageDllList
;
501 LPWSTR lpMsgBuf
= NULL
;
503 /* Allocate a local buffer for the DLL list that can be tokenized */
504 // TODO: Optimize that!! Maybe we can cleverly use lpMessageDllList in read/write mode
505 // and cleverly temporarily replace the ';' by UNICODE_NULL, do our job, then reverse the change.
506 cbLength
= (wcslen(lpMessageDllList
) + 1) * sizeof(WCHAR
);
507 szMessageDllList
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, cbLength
);
508 if (!szMessageDllList
)
510 RtlCopyMemory(szMessageDllList
, lpMessageDllList
, cbLength
);
512 /* Loop through the list of message DLLs */
513 szDll
= wcstok(szMessageDllList
, EVENT_DLL_SEPARATOR
);
514 while ((szDll
!= NULL
) && !Success
)
516 // Uses MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)
517 lpMsgBuf
= GetMessageStringFromDll(szDll
,
524 /* The ID was found and the message was formatted */
530 * The DLL could not be loaded, or the message could not be found,
531 * try the next DLL, if any.
533 szDll
= wcstok(NULL
, EVENT_DLL_SEPARATOR
);
536 HeapFree(GetProcessHeap(), 0, szMessageDllList
);
544 LPWSTR pStartingAddress
; // Pointer to the beginning of a parameter string in pMessage
545 LPWSTR pEndingAddress
; // Pointer to the end of a parameter string in pMessage
546 DWORD pParameterID
; // Parameter identifier found in pMessage
547 LPWSTR pParameter
; // Actual parameter string
548 } param_strings_format_data
;
551 ApplyParameterStringsToMessage(
552 IN LPCWSTR lpMessageDllList
,
553 IN BOOL bMessagePreFormatted
,
554 IN CONST LPCWSTR pMessage
,
555 OUT LPWSTR
* pFinalMessage
)
558 * This code is heavily adapted from the MSDN example:
559 * https://msdn.microsoft.com/en-us/library/windows/desktop/bb427356.aspx
563 DWORD Status
= ERROR_SUCCESS
;
564 DWORD dwParamCount
= 0; // Number of insertion strings found in pMessage
565 size_t cchBuffer
= 0; // Size of the buffer in characters
566 size_t cchParams
= 0; // Number of characters in all the parameter strings
569 param_strings_format_data
* pParamData
= NULL
; // Array of pointers holding information about each parameter string in pMessage
570 LPWSTR pTempMessage
= (LPWSTR
)pMessage
;
571 LPWSTR pTempFinalMessage
= NULL
;
573 *pFinalMessage
= NULL
;
575 /* Determine the number of parameter insertion strings in pMessage */
576 if (bMessagePreFormatted
)
578 while ((pTempMessage
= wcschr(pTempMessage
, L
'%')))
581 if (isdigit(*pTempMessage
))
584 while (isdigit(*++pTempMessage
)) ;
590 while ((pTempMessage
= wcsstr(pTempMessage
, L
"%%")))
593 if (isdigit(*pTempMessage
))
596 while (isdigit(*++pTempMessage
)) ;
601 /* If there are no parameter insertion strings in pMessage, just return */
602 if (dwParamCount
== 0)
604 // *pFinalMessage = NULL;
608 /* Allocate the array of parameter string format data */
609 pParamData
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, dwParamCount
* sizeof(param_strings_format_data
));
612 Status
= ERROR_OUTOFMEMORY
;
617 * Retrieve each parameter in pMessage and the beginning and end of the
618 * insertion string, as well as the message identifier of the parameter.
620 pTempMessage
= (LPWSTR
)pMessage
;
621 if (bMessagePreFormatted
)
623 while ((pTempMessage
= wcschr(pTempMessage
, L
'%')) && (i
< dwParamCount
))
626 if (isdigit(*pTempMessage
))
628 pParamData
[i
].pStartingAddress
= pTempMessage
-1;
629 pParamData
[i
].pParameterID
= (DWORD
)_wtol(pTempMessage
);
631 while (isdigit(*++pTempMessage
)) ;
633 pParamData
[i
].pEndingAddress
= pTempMessage
;
640 while ((pTempMessage
= wcsstr(pTempMessage
, L
"%%")) && (i
< dwParamCount
))
643 if (isdigit(*pTempMessage
))
645 pParamData
[i
].pStartingAddress
= pTempMessage
-2;
646 pParamData
[i
].pParameterID
= (DWORD
)_wtol(pTempMessage
);
648 while (isdigit(*++pTempMessage
)) ;
650 pParamData
[i
].pEndingAddress
= pTempMessage
;
656 /* Retrieve each parameter string */
657 for (i
= 0; i
< dwParamCount
; i
++)
659 // pParamData[i].pParameter = GetMessageString(pParamData[i].pParameterID, 0, NULL);
660 pParamData
[i
].pParameter
=
661 GetMessageStringFromDllList(lpMessageDllList
,
662 FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_HMODULE
|
663 FORMAT_MESSAGE_IGNORE_INSERTS
| FORMAT_MESSAGE_MAX_WIDTH_MASK
,
664 pParamData
[i
].pParameterID
,
666 if (!pParamData
[i
].pParameter
)
668 /* Skip the insertion string */
672 cchParams
+= wcslen(pParamData
[i
].pParameter
);
676 * Allocate the final message buffer, the size of which is based on the
677 * length of the original message and the length of each parameter string.
679 cchBuffer
= wcslen(pMessage
) + cchParams
+ 1;
680 *pFinalMessage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, cchBuffer
* sizeof(WCHAR
));
683 Status
= ERROR_OUTOFMEMORY
;
687 pTempFinalMessage
= *pFinalMessage
;
689 /* Build the final message string */
690 pTempMessage
= (LPWSTR
)pMessage
;
691 for (i
= 0; i
< dwParamCount
; i
++)
693 /* Append the segment from pMessage */
694 cch
= pParamData
[i
].pStartingAddress
- pTempMessage
;
695 StringCchCopyNW(pTempFinalMessage
, cchBuffer
, pTempMessage
, cch
);
696 pTempMessage
= pParamData
[i
].pEndingAddress
;
698 pTempFinalMessage
+= cch
;
700 /* Append the parameter string */
701 if (pParamData
[i
].pParameter
)
703 StringCchCopyW(pTempFinalMessage
, cchBuffer
, pParamData
[i
].pParameter
);
704 cch
= wcslen(pParamData
[i
].pParameter
); // pTempFinalMessage
709 * We failed to retrieve the parameter string before, so just
710 * place back the original string placeholder.
712 cch
= pParamData
[i
].pEndingAddress
/* == pTempMessage */ - pParamData
[i
].pStartingAddress
;
713 StringCchCopyNW(pTempFinalMessage
, cchBuffer
, pParamData
[i
].pStartingAddress
, cch
);
714 // cch = wcslen(pTempFinalMessage);
717 pTempFinalMessage
+= cch
;
720 /* Append the last segment from pMessage */
721 StringCchCopyW(pTempFinalMessage
, cchBuffer
, pTempMessage
);
725 // if (Status != ERROR_SUCCESS)
726 // *pFinalMessage = NULL;
730 for (i
= 0; i
< dwParamCount
; i
++)
732 if (pParamData
[i
].pParameter
)
733 LocalFree(pParamData
[i
].pParameter
);
736 HeapFree(GetProcessHeap(), 0, pParamData
);
744 * The following functions were adapted from
745 * shell32!dialogs/filedefext.cpp:``SH_...'' functions.
749 FormatInteger(LONGLONG Num
, LPWSTR pwszResult
, UINT cchResultMax
)
752 WCHAR wszDecimalSep
[8], wszThousandSep
[8];
754 WCHAR wszGrouping
[12];
759 // Print the number in uniform mode
760 swprintf(wszNumber
, L
"%I64u", Num
);
762 // Get system strings for decimal and thousand separators.
763 GetLocaleInfoW(LOCALE_USER_DEFAULT
, LOCALE_SDECIMAL
, wszDecimalSep
, _countof(wszDecimalSep
));
764 GetLocaleInfoW(LOCALE_USER_DEFAULT
, LOCALE_STHOUSAND
, wszThousandSep
, _countof(wszThousandSep
));
766 // Initialize format for printing the number in bytes
767 ZeroMemory(&nf
, sizeof(nf
));
768 nf
.lpDecimalSep
= wszDecimalSep
;
769 nf
.lpThousandSep
= wszThousandSep
;
771 // Get system string for groups separator
772 cchGrouping
= GetLocaleInfoW(LOCALE_USER_DEFAULT
,
775 _countof(wszGrouping
));
777 // Convert grouping specs from string to integer
778 for (i
= 0; i
< cchGrouping
; i
++)
780 WCHAR wch
= wszGrouping
[i
];
782 if (wch
>= L
'0' && wch
<= L
'9')
783 nf
.Grouping
= nf
.Grouping
* 10 + (wch
- L
'0');
784 else if (wch
!= L
';')
788 if ((nf
.Grouping
% 10) == 0)
794 cchResult
= GetNumberFormatW(LOCALE_USER_DEFAULT
,
804 // GetNumberFormatW returns number of characters including UNICODE_NULL
805 return cchResult
- 1;
809 FormatByteSize(LONGLONG cbSize
, LPWSTR pwszResult
, UINT cchResultMax
)
815 /* Write formated bytes count */
816 cchWritten
= FormatInteger(cbSize
, pwszResult
, cchResultMax
);
820 /* Copy " bytes" to buffer */
821 pwszEnd
= pwszResult
+ cchWritten
;
822 cchRemaining
= cchResultMax
- cchWritten
;
823 StringCchCopyExW(pwszEnd
, cchRemaining
, L
" ", &pwszEnd
, &cchRemaining
, 0);
824 cchWritten
= LoadStringW(hInst
, IDS_BYTES_FORMAT
, pwszEnd
, cchRemaining
);
825 cchRemaining
-= cchWritten
;
827 return cchResultMax
- cchRemaining
;
831 FormatFileSizeWithBytes(const PULARGE_INTEGER lpQwSize
, LPWSTR pwszResult
, UINT cchResultMax
)
837 /* Format bytes in KBs, MBs etc */
838 if (StrFormatByteSizeW(lpQwSize
->QuadPart
, pwszResult
, cchResultMax
) == NULL
)
841 /* If there is less bytes than 1KB, we have nothing to do */
842 if (lpQwSize
->QuadPart
< 1024)
845 /* Concatenate " (" */
846 cchWritten
= wcslen(pwszResult
);
847 pwszEnd
= pwszResult
+ cchWritten
;
848 cchRemaining
= cchResultMax
- cchWritten
;
849 StringCchCopyExW(pwszEnd
, cchRemaining
, L
" (", &pwszEnd
, &cchRemaining
, 0);
851 /* Write formated bytes count */
852 cchWritten
= FormatByteSize(lpQwSize
->QuadPart
, pwszEnd
, cchRemaining
);
853 pwszEnd
+= cchWritten
;
854 cchRemaining
-= cchWritten
;
856 /* Copy ")" to the buffer */
857 StringCchCopyW(pwszEnd
, cchRemaining
, L
")");
862 /* Adapted from shell32!dialogs/filedefext.cpp:``CFileDefExt::GetFileTimeString'' */
864 GetFileTimeString(LPFILETIME lpFileTime
, LPWSTR pwszResult
, UINT cchResult
)
869 size_t cchRemaining
= cchResult
;
870 LPWSTR pwszEnd
= pwszResult
;
872 if (!FileTimeToLocalFileTime(lpFileTime
, &ft
) || !FileTimeToSystemTime(&ft
, &st
))
875 cchWritten
= GetDateFormatW(LOCALE_USER_DEFAULT
, DATE_LONGDATE
, &st
, NULL
, pwszEnd
, cchRemaining
);
877 --cchWritten
; // GetDateFormatW returns count with terminating zero
879 // ERR("GetDateFormatW failed\n");
881 cchRemaining
-= cchWritten
;
882 pwszEnd
+= cchWritten
;
884 StringCchCopyExW(pwszEnd
, cchRemaining
, L
", ", &pwszEnd
, &cchRemaining
, 0);
886 cchWritten
= GetTimeFormatW(LOCALE_USER_DEFAULT
, 0, &st
, NULL
, pwszEnd
, cchRemaining
);
888 --cchWritten
; // GetTimeFormatW returns count with terminating zero
890 // ERR("GetTimeFormatW failed\n");
897 TreeViewAddItem(IN HWND hTreeView
,
898 IN HTREEITEM hParent
,
901 IN INT SelectedImage
,
904 TV_INSERTSTRUCTW Insert
;
906 ZeroMemory(&Insert
, sizeof(Insert
));
908 Insert
.item
.mask
= TVIF_TEXT
| TVIF_PARAM
| TVIF_IMAGE
| TVIF_SELECTEDIMAGE
;
909 Insert
.hInsertAfter
= TVI_LAST
;
910 Insert
.hParent
= hParent
;
911 Insert
.item
.pszText
= lpText
;
912 Insert
.item
.iImage
= Image
;
913 Insert
.item
.iSelectedImage
= SelectedImage
;
914 Insert
.item
.lParam
= lParam
;
916 Insert
.item
.mask
|= TVIF_STATE
;
917 Insert
.item
.stateMask
= TVIS_OVERLAYMASK
;
918 Insert
.item
.state
= INDEXTOOVERLAYMASK(1);
920 return TreeView_InsertItem(hTreeView
, &Insert
);
924 /* LOG HELPER FUNCTIONS *******************************************************/
927 AllocEventLog(IN PCWSTR ComputerName OPTIONAL
,
934 /* Allocate a new event log entry */
935 EventLog
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*EventLog
));
939 /* Allocate the computer name string (optional) and copy it */
942 cchName
= wcslen(ComputerName
) + 1;
943 EventLog
->ComputerName
= HeapAlloc(GetProcessHeap(), 0, cchName
* sizeof(WCHAR
));
944 if (EventLog
->ComputerName
)
945 StringCchCopyW(EventLog
->ComputerName
, cchName
, ComputerName
);
948 /* Allocate the event log name string and copy it */
949 cchName
= wcslen(LogName
) + 1;
950 EventLog
->LogName
= HeapAlloc(GetProcessHeap(), 0, cchName
* sizeof(WCHAR
));
951 if (!EventLog
->LogName
)
953 HeapFree(GetProcessHeap(), 0, EventLog
);
956 StringCchCopyW(EventLog
->LogName
, cchName
, LogName
);
958 EventLog
->Permanent
= Permanent
;
964 EventLog_Free(IN PEVENTLOG EventLog
)
966 if (EventLog
->LogName
)
967 HeapFree(GetProcessHeap(), 0, EventLog
->LogName
);
969 if (EventLog
->FileName
)
970 HeapFree(GetProcessHeap(), 0, EventLog
->FileName
);
972 HeapFree(GetProcessHeap(), 0, EventLog
);
977 AllocAndCopyMultiStr(IN PCWSTR MultiStr OPTIONAL
)
985 pStr
= (PWSTR
)MultiStr
;
986 while (*pStr
) pStr
+= (wcslen(pStr
) + 1);
987 Length
= MultiStr
- pStr
+ 2;
989 pStr
= HeapAlloc(GetProcessHeap(), 0, Length
* sizeof(WCHAR
));
990 // NOTE: If we failed allocating the string, then fall back into no filter!
992 RtlCopyMemory(pStr
, MultiStr
, Length
* sizeof(WCHAR
));
998 AllocEventLogFilter(// IN PCWSTR FilterName,
1002 IN BOOL AuditSuccess
,
1003 IN BOOL AuditFailure
,
1004 IN PCWSTR Sources OPTIONAL
,
1005 IN PCWSTR Users OPTIONAL
,
1006 IN PCWSTR ComputerNames OPTIONAL
,
1007 IN ULONG NumOfEventLogs
,
1008 IN PEVENTLOG
* EventLogs
)
1010 PEVENTLOGFILTER EventLogFilter
;
1012 /* Allocate a new event log filter entry, big enough to accommodate the list of logs */
1013 EventLogFilter
= HeapAlloc(GetProcessHeap(),
1015 FIELD_OFFSET(EVENTLOGFILTER
, EventLogs
[NumOfEventLogs
]));
1016 if (!EventLogFilter
)
1019 EventLogFilter
->Information
= Information
;
1020 EventLogFilter
->Warning
= Warning
;
1021 EventLogFilter
->Error
= Error
;
1022 EventLogFilter
->AuditSuccess
= AuditSuccess
;
1023 EventLogFilter
->AuditFailure
= AuditFailure
;
1025 /* Allocate and copy the sources, users, and computers multi-strings */
1026 EventLogFilter
->Sources
= AllocAndCopyMultiStr(Sources
);
1027 EventLogFilter
->Users
= AllocAndCopyMultiStr(Users
);
1028 EventLogFilter
->ComputerNames
= AllocAndCopyMultiStr(ComputerNames
);
1030 /* Copy the list of event logs */
1031 EventLogFilter
->NumOfEventLogs
= NumOfEventLogs
;
1032 RtlCopyMemory(EventLogFilter
->EventLogs
, EventLogs
, NumOfEventLogs
* sizeof(PEVENTLOG
));
1034 /* Initialize the filter reference count */
1035 EventLogFilter
->ReferenceCount
= 1;
1037 return EventLogFilter
;
1041 EventLogFilter_Free(IN PEVENTLOGFILTER EventLogFilter
)
1043 if (EventLogFilter
->Sources
)
1044 HeapFree(GetProcessHeap(), 0, EventLogFilter
->Sources
);
1046 if (EventLogFilter
->Users
)
1047 HeapFree(GetProcessHeap(), 0, EventLogFilter
->Users
);
1049 if (EventLogFilter
->ComputerNames
)
1050 HeapFree(GetProcessHeap(), 0, EventLogFilter
->ComputerNames
);
1052 HeapFree(GetProcessHeap(), 0, EventLogFilter
);
1055 LONG
EventLogFilter_AddRef(IN PEVENTLOGFILTER EventLogFilter
)
1057 ASSERT(EventLogFilter
);
1058 return InterlockedIncrement(&EventLogFilter
->ReferenceCount
);
1061 LONG
EventLogFilter_Release(IN PEVENTLOGFILTER EventLogFilter
)
1065 ASSERT(EventLogFilter
);
1067 /* When the reference count reaches zero, delete the filter */
1068 RefCount
= InterlockedDecrement(&EventLogFilter
->ReferenceCount
);
1071 /* Remove the filter from the list */
1072 /** RemoveEntryList(&EventLogFilter->ListEntry); **/
1073 EventLogFilter_Free(EventLogFilter
);
1086 c
= s
+ wcslen(s
) - 1;
1087 while (c
>= s
&& iswspace(*c
))
1094 GetEventMessageFileDLL(IN LPCWSTR lpLogName
,
1095 IN LPCWSTR SourceName
,
1096 IN LPCWSTR EntryName
,
1097 OUT PWCHAR lpModuleName
) // TODO: Add IN DWORD BufLen
1099 BOOL Success
= FALSE
;
1102 WCHAR szModuleName
[MAX_PATH
];
1103 WCHAR szKeyName
[MAX_PATH
];
1104 HKEY hLogKey
= NULL
;
1105 HKEY hSourceKey
= NULL
;
1107 StringCbCopyW(szKeyName
, sizeof(szKeyName
), EVENTLOG_BASE_KEY
);
1108 StringCbCatW(szKeyName
, sizeof(szKeyName
), lpLogName
);
1110 Result
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
,
1115 if (Result
!= ERROR_SUCCESS
)
1118 Result
= RegOpenKeyExW(hLogKey
,
1123 if (Result
!= ERROR_SUCCESS
)
1125 RegCloseKey(hLogKey
);
1129 dwSize
= sizeof(szModuleName
);
1130 Result
= RegQueryValueExW(hSourceKey
,
1134 (LPBYTE
)szModuleName
,
1136 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_EXPAND_SZ
&& Type
!= REG_SZ
))
1138 szModuleName
[0] = UNICODE_NULL
;
1142 /* NULL-terminate the string and expand it */
1143 szModuleName
[dwSize
/ sizeof(WCHAR
) - 1] = UNICODE_NULL
;
1144 ExpandEnvironmentStringsW(szModuleName
, lpModuleName
, ARRAYSIZE(szModuleName
));
1148 RegCloseKey(hSourceKey
);
1149 RegCloseKey(hLogKey
);
1155 GetEventCategory(IN LPCWSTR KeyName
,
1156 IN LPCWSTR SourceName
,
1157 IN PEVENTLOGRECORD pevlr
,
1158 OUT PWCHAR CategoryName
) // TODO: Add IN DWORD BufLen
1160 BOOL Success
= FALSE
;
1161 WCHAR szMessageDLL
[MAX_PATH
];
1162 LPWSTR lpMsgBuf
= NULL
;
1164 if (!GetEventMessageFileDLL(KeyName
, SourceName
, EVENT_CATEGORY_MESSAGE_FILE
, szMessageDLL
))
1167 /* Retrieve the message string without appending extra newlines */
1169 GetMessageStringFromDllList(szMessageDLL
,
1170 FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_HMODULE
|
1171 FORMAT_MESSAGE_IGNORE_INSERTS
| FORMAT_MESSAGE_MAX_WIDTH_MASK
,
1172 pevlr
->EventCategory
,
1173 EVENT_MESSAGE_FILE_BUFFER
,
1177 /* Trim the string */
1178 TrimNulls(lpMsgBuf
);
1180 /* Copy the category name */
1181 StringCchCopyW(CategoryName
, MAX_PATH
, lpMsgBuf
);
1183 /* Free the buffer allocated by FormatMessage */
1184 LocalFree(lpMsgBuf
);
1186 /* The ID was found and the message was formatted */
1193 if (pevlr
->EventCategory
!= 0)
1194 StringCchPrintfW(CategoryName
, MAX_PATH
, L
"(%lu)", pevlr
->EventCategory
);
1196 LoadStringW(hInst
, IDS_NONE
, CategoryName
, MAX_PATH
);
1204 GetEventMessage(IN LPCWSTR KeyName
,
1205 IN LPCWSTR SourceName
,
1206 IN PEVENTLOGRECORD pevlr
,
1207 OUT PWCHAR EventText
) // TODO: Add IN DWORD BufLen
1209 BOOL Success
= FALSE
;
1212 WCHAR SourceModuleName
[1024];
1213 WCHAR ParameterModuleName
[1024];
1214 BOOL IsParamModNameCached
= FALSE
;
1215 LPWSTR lpMsgBuf
= NULL
;
1216 LPWSTR szStringArray
, szMessage
;
1217 LPWSTR
*szArguments
;
1219 /* Get the event string array */
1220 szStringArray
= (LPWSTR
)((LPBYTE
)pevlr
+ pevlr
->StringOffset
);
1222 /* NOTE: GetEventMessageFileDLL can return a comma-separated list of DLLs */
1223 if (!GetEventMessageFileDLL(KeyName
, SourceName
, EVENT_MESSAGE_FILE
, SourceModuleName
))
1226 /* Allocate space for insertion strings */
1227 szArguments
= HeapAlloc(GetProcessHeap(), 0, pevlr
->NumStrings
* sizeof(LPVOID
));
1231 if (!IsParamModNameCached
)
1233 /* Now that the parameter file list is loaded, no need to reload it at the next run! */
1234 IsParamModNameCached
= GetEventMessageFileDLL(KeyName
, SourceName
, EVENT_PARAMETER_MESSAGE_FILE
, ParameterModuleName
);
1235 // FIXME: If the string loading failed the first time, no need to retry it just after???
1238 if (IsParamModNameCached
)
1240 /* Not yet support for reading messages from parameter message DLL */
1243 szMessage
= szStringArray
;
1246 * We do some hackish preformatting of the cached event strings...
1247 * That's because after we pass the string to FormatMessage
1248 * (via GetMessageStringFromDllList) with the FORMAT_MESSAGE_ARGUMENT_ARRAY
1249 * flag, instead of ignoring the insertion parameters and do the formatting
1250 * by ourselves. Therefore, the resulting string should have the parameter
1251 * string placeholders starting with a single '%' instead of a mix of one
1254 /* HACK part 1: Compute the full length of the string array */
1256 for (i
= 0; i
< pevlr
->NumStrings
; i
++)
1258 szMessage
+= wcslen(szMessage
) + 1;
1260 cch
= szMessage
- szStringArray
;
1262 /* HACK part 2: Now do the HACK proper! */
1263 szMessage
= szStringArray
;
1264 for (i
= 0; i
< pevlr
->NumStrings
; i
++)
1266 lpMsgBuf
= szMessage
;
1267 while ((lpMsgBuf
= wcsstr(lpMsgBuf
, L
"%%")))
1269 if (isdigit(lpMsgBuf
[2]))
1271 RtlMoveMemory(lpMsgBuf
, lpMsgBuf
+1, ((szStringArray
+ cch
) - lpMsgBuf
- 1) * sizeof(WCHAR
));
1275 szArguments
[i
] = szMessage
;
1276 szMessage
+= wcslen(szMessage
) + 1;
1279 /* Retrieve the message string without appending extra newlines */
1281 GetMessageStringFromDllList(SourceModuleName
,
1282 FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_HMODULE
|
1283 FORMAT_MESSAGE_ARGUMENT_ARRAY
| FORMAT_MESSAGE_MAX_WIDTH_MASK
,
1286 (va_list*)szArguments
);
1289 /* Trim the string */
1290 TrimNulls(lpMsgBuf
);
1293 Success
= (ApplyParameterStringsToMessage(ParameterModuleName
,
1296 &szMessage
) == ERROR_SUCCESS
);
1297 if (Success
&& szMessage
)
1299 /* Free the buffer allocated by FormatMessage */
1300 LocalFree(lpMsgBuf
);
1301 lpMsgBuf
= szMessage
;
1304 /* Copy the event text */
1305 StringCchCopyW(EventText
, EVENT_MESSAGE_EVENTTEXT_BUFFER
, lpMsgBuf
);
1307 /* Free the buffer allocated by FormatMessage */
1308 LocalFree(lpMsgBuf
);
1311 HeapFree(GetProcessHeap(), 0, szArguments
);
1316 /* Get a read-only pointer to the "event-not-found" string */
1317 lpMsgBuf
= HeapAlloc(GetProcessHeap(), 0, EVENT_MESSAGE_EVENTTEXT_BUFFER
* sizeof(WCHAR
));
1318 LoadStringW(hInst
, IDS_EVENTSTRINGIDNOTFOUND
, lpMsgBuf
, EVENT_MESSAGE_EVENTTEXT_BUFFER
);
1319 StringCchPrintfW(EventText
, EVENT_MESSAGE_EVENTTEXT_BUFFER
, lpMsgBuf
, (pevlr
->EventID
& 0xFFFF), SourceName
);
1321 /* Append the strings */
1322 szMessage
= szStringArray
;
1323 for (i
= 0; i
< pevlr
->NumStrings
; i
++)
1325 StringCchCatW(EventText
, EVENT_MESSAGE_EVENTTEXT_BUFFER
, szMessage
);
1326 StringCchCatW(EventText
, EVENT_MESSAGE_EVENTTEXT_BUFFER
, L
"\n");
1327 szMessage
+= wcslen(szMessage
) + 1;
1335 GetEventType(IN WORD dwEventType
,
1336 OUT PWCHAR eventTypeText
) // TODO: Add IN DWORD BufLen
1338 switch (dwEventType
)
1340 case EVENTLOG_ERROR_TYPE
:
1341 LoadStringW(hInst
, IDS_EVENTLOG_ERROR_TYPE
, eventTypeText
, MAX_LOADSTRING
);
1343 case EVENTLOG_WARNING_TYPE
:
1344 LoadStringW(hInst
, IDS_EVENTLOG_WARNING_TYPE
, eventTypeText
, MAX_LOADSTRING
);
1346 case EVENTLOG_INFORMATION_TYPE
:
1347 LoadStringW(hInst
, IDS_EVENTLOG_INFORMATION_TYPE
, eventTypeText
, MAX_LOADSTRING
);
1349 case EVENTLOG_SUCCESS
:
1350 LoadStringW(hInst
, IDS_EVENTLOG_SUCCESS
, eventTypeText
, MAX_LOADSTRING
);
1352 case EVENTLOG_AUDIT_SUCCESS
:
1353 LoadStringW(hInst
, IDS_EVENTLOG_AUDIT_SUCCESS
, eventTypeText
, MAX_LOADSTRING
);
1355 case EVENTLOG_AUDIT_FAILURE
:
1356 LoadStringW(hInst
, IDS_EVENTLOG_AUDIT_FAILURE
, eventTypeText
, MAX_LOADSTRING
);
1359 LoadStringW(hInst
, IDS_EVENTLOG_UNKNOWN_TYPE
, eventTypeText
, MAX_LOADSTRING
);
1365 GetEventUserName(IN PEVENTLOGRECORD pelr
,
1366 OUT PWCHAR pszUser
) // TODO: Add IN DWORD BufLen
1370 WCHAR szDomain
[1024];
1372 DWORD cchName
= ARRAYSIZE(szName
);
1373 DWORD cchDomain
= ARRAYSIZE(szDomain
);
1375 /* Point to the SID */
1376 lpSid
= (PSID
)((LPBYTE
)pelr
+ pelr
->UserSidOffset
);
1379 if (pelr
->UserSidLength
> 0)
1381 if (LookupAccountSidW(NULL
, // FIXME: Use computer name? From the particular event?
1389 StringCchCopyW(pszUser
, MAX_PATH
, szName
);
1398 static VOID
FreeRecords(VOID
)
1405 for (iIndex
= 0; iIndex
< g_TotalRecords
; iIndex
++)
1407 if (g_RecordPtrs
[iIndex
])
1408 HeapFree(GetProcessHeap(), 0, g_RecordPtrs
[iIndex
]);
1410 HeapFree(GetProcessHeap(), 0, g_RecordPtrs
);
1411 g_RecordPtrs
= NULL
;
1416 FilterByType(IN PEVENTLOGFILTER EventLogFilter
,
1417 IN PEVENTLOGRECORD pevlr
)
1419 if ((pevlr
->EventType
== EVENTLOG_SUCCESS
&& !EventLogFilter
->Information
) ||
1420 (pevlr
->EventType
== EVENTLOG_INFORMATION_TYPE
&& !EventLogFilter
->Information
) ||
1421 (pevlr
->EventType
== EVENTLOG_WARNING_TYPE
&& !EventLogFilter
->Warning
) ||
1422 (pevlr
->EventType
== EVENTLOG_ERROR_TYPE
&& !EventLogFilter
->Error
) ||
1423 (pevlr
->EventType
== EVENTLOG_AUDIT_SUCCESS
&& !EventLogFilter
->AuditSuccess
) ||
1424 (pevlr
->EventType
== EVENTLOG_AUDIT_FAILURE
&& !EventLogFilter
->AuditFailure
))
1432 FilterByString(IN PCWSTR FilterString
, // This is a multi-string
1437 /* The filter string is NULL so it does not filter anything */
1442 * If the filter string filters for an empty string AND the source string
1443 * is an empty string, we have a match (particular case of the last one).
1445 if (!*FilterString
&& !*String
)
1448 // if (*FilterString || *String)
1451 * If the filter string is empty BUT the source string is not empty,
1452 * OR vice-versa, we cannot have a match.
1454 if ( (!*FilterString
&& *String
) || (*FilterString
&& !*String
) )
1458 * If the filter string filters for at least a non-empty string,
1459 * browse it and search for a string that matches the source string.
1461 // else if (*FilterString && *String)
1463 pStr
= FilterString
;
1466 if (wcsicmp(pStr
, String
) == 0)
1468 /* We have a match, break the loop */
1472 pStr
+= (wcslen(pStr
) + 1);
1474 if (!*pStr
) // && *String
1476 /* We do not have a match */
1481 /* We have a match */
1486 * The events enumerator thread.
1489 EnumEventsThread(IN LPVOID lpParameter
)
1491 PEVENTLOGFILTER EventLogFilter
= (PEVENTLOGFILTER
)lpParameter
;
1492 LPWSTR lpMachineName
= NULL
; // EventLogFilter->ComputerName;
1497 PEVENTLOGRECORD pevlr
, pevlrTmp
= NULL
;
1498 DWORD dwRead
, dwNeeded
; // , dwThisRecord;
1499 DWORD dwTotalRecords
= 0, dwCurrentRecord
= 0;
1500 DWORD dwFlags
, dwMaxLength
;
1501 size_t cchRemaining
;
1502 LPWSTR lpszSourceName
;
1503 LPWSTR lpszComputerName
;
1504 BOOL bResult
= TRUE
; /* Read succeeded */
1506 UINT uStep
= 0, uStepAt
= 0, uPos
= 0;
1508 WCHAR szWindowTitle
[MAX_PATH
];
1509 WCHAR szStatusText
[MAX_PATH
];
1510 WCHAR szLocalDate
[MAX_PATH
];
1511 WCHAR szLocalTime
[MAX_PATH
];
1512 WCHAR szEventID
[MAX_PATH
];
1513 WCHAR szEventTypeText
[MAX_LOADSTRING
];
1514 WCHAR szCategoryID
[MAX_PATH
];
1515 WCHAR szUsername
[MAX_PATH
];
1516 WCHAR szCategory
[MAX_PATH
];
1517 PWCHAR lpTitleTemplateEnd
;
1520 LVITEMW lviEventItem
;
1522 /* Save the current event log filter globally */
1523 EventLogFilter_AddRef(EventLogFilter
);
1524 ActiveFilter
= EventLogFilter
;
1526 /* Disable list view redraw */
1527 SendMessageW(hwndListView
, WM_SETREDRAW
, FALSE
, 0);
1529 /* Clear the list view and free the cached records */
1530 ListView_DeleteAllItems(hwndListView
);
1533 SendMessageW(hwndListView
, LVM_PROGRESS
, 0, TRUE
);
1534 ProgressBar_SetRange(hwndStatusProgress
, 0);
1535 StatusBar_SetText(hwndStatus
, 0, NULL
);
1536 ShowWindow(hwndStatusProgress
, SW_SHOW
);
1538 /* Do a loop over the logs enumerated in the filter */
1539 // FIXME: For now we only support 1 event log per filter!
1541 // for (LogIndex = 0; LogIndex < EventLogFilter->NumOfEventLogs; ++LogIndex)
1544 EventLog
= EventLogFilter
->EventLogs
[LogIndex
];
1546 /* Open the event log */
1547 if (EventLog
->Permanent
)
1548 hEventLog
= OpenEventLogW(EventLog
->ComputerName
, EventLog
->LogName
);
1550 hEventLog
= OpenBackupEventLogW(EventLog
->ComputerName
, EventLog
->LogName
); // FileName
1552 if (hEventLog
== NULL
)
1554 ShowLastWin32Error();
1558 // GetOldestEventLogRecord(hEventLog, &dwThisRecord);
1560 /* Get the total number of event log records */
1561 GetNumberOfEventLogRecords(hEventLog
, &dwTotalRecords
);
1563 if (dwTotalRecords
> 0)
1565 EnableMenuItem(hMainMenu
, IDM_CLEAR_EVENTS
, MF_BYCOMMAND
| MF_ENABLED
);
1566 EnableMenuItem(hMainMenu
, IDM_SAVE_EVENTLOG
, MF_BYCOMMAND
| MF_ENABLED
);
1570 EnableMenuItem(hMainMenu
, IDM_CLEAR_EVENTS
, MF_BYCOMMAND
| MF_GRAYED
);
1571 EnableMenuItem(hMainMenu
, IDM_SAVE_EVENTLOG
, MF_BYCOMMAND
| MF_GRAYED
);
1574 /* Set up the event records cache */
1575 g_RecordPtrs
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, dwTotalRecords
* sizeof(*g_RecordPtrs
));
1578 // ShowLastWin32Error();
1581 g_TotalRecords
= dwTotalRecords
;
1583 if (WaitForSingleObject(hStopEnumEvent
, 0) == WAIT_OBJECT_0
)
1586 ProgressBar_SetRange(hwndStatusProgress
, MAKELPARAM(0, 100));
1587 uStepAt
= (dwTotalRecords
/ 100) + 1;
1589 dwFlags
= EVENTLOG_SEQUENTIAL_READ
|
1590 (NewestEventsFirst
? EVENTLOG_FORWARDS_READ
1591 : EVENTLOG_BACKWARDS_READ
);
1593 while (dwCurrentRecord
< dwTotalRecords
)
1596 // NOTE: We always allocate the minimum size for 1 record, and if
1597 // the ReadEventLog call fails (it always will anyway), we reallocate
1598 // the record pointer to be able to hold just 1 record, and then we
1599 // redo that for each event.
1600 // This is obviously not at all efficient (in terms of numbers of
1601 // ReadEventLog calls), since ReadEventLog can fill the buffer with
1602 // as many records they can fit completely in the buffer.
1605 pevlr
= HeapAlloc(GetProcessHeap(), 0, sizeof(*pevlr
));
1608 /* Cannot allocate, just skip the event */
1609 g_RecordPtrs
[dwCurrentRecord
] = NULL
;
1610 // --dwTotalRecords;
1613 g_RecordPtrs
[dwCurrentRecord
] = pevlr
;
1615 bResult
= ReadEventLogW(hEventLog
, // Event log handle
1616 dwFlags
, // Sequential read
1617 0, // Ignored for sequential read
1618 pevlr
, // Pointer to buffer
1619 sizeof(*pevlr
), // Size of buffer
1620 &dwRead
, // Number of bytes read
1621 &dwNeeded
); // Bytes in the next record
1622 if (!bResult
&& (GetLastError () == ERROR_INSUFFICIENT_BUFFER
))
1624 pevlrTmp
= HeapReAlloc(GetProcessHeap(), 0, pevlr
, dwNeeded
);
1627 /* Cannot reallocate, just skip the event */
1628 HeapFree(GetProcessHeap(), 0, pevlr
);
1629 g_RecordPtrs
[dwCurrentRecord
] = NULL
;
1630 // --dwTotalRecords;
1634 g_RecordPtrs
[dwCurrentRecord
] = pevlr
;
1636 ReadEventLogW(hEventLog
, // event log handle
1637 dwFlags
, // read flags
1638 0, // offset; default is 0
1639 pevlr
, // pointer to buffer
1640 dwNeeded
, // size of buffer
1641 &dwRead
, // number of bytes read
1642 &dwNeeded
); // bytes in next record
1647 // ProgressBar_StepIt(hwndStatusProgress);
1649 if(uStep
% uStepAt
== 0)
1652 ProgressBar_SetPos(hwndStatusProgress
, uPos
);
1655 if (WaitForSingleObject(hStopEnumEvent
, 0) == WAIT_OBJECT_0
)
1658 /* Filter by event type */
1659 if (!FilterByType(EventLogFilter
, pevlr
))
1662 /* Get the event source name and filter it */
1663 lpszSourceName
= (LPWSTR
)((LPBYTE
)pevlr
+ sizeof(EVENTLOGRECORD
));
1664 if (!FilterByString(EventLogFilter
->Sources
, lpszSourceName
))
1667 /* Get the computer name and filter it */
1668 lpszComputerName
= (LPWSTR
)((LPBYTE
)pevlr
+ sizeof(EVENTLOGRECORD
) + (wcslen(lpszSourceName
) + 1) * sizeof(WCHAR
));
1669 if (!FilterByString(EventLogFilter
->ComputerNames
, lpszComputerName
))
1672 /* Compute the event time */
1673 EventTimeToSystemTime(pevlr
->TimeWritten
, &time
);
1674 GetDateFormatW(LOCALE_USER_DEFAULT
, DATE_SHORTDATE
, &time
, NULL
, szLocalDate
, ARRAYSIZE(szLocalDate
));
1675 GetTimeFormatW(LOCALE_USER_DEFAULT
, 0, &time
, NULL
, szLocalTime
, ARRAYSIZE(szLocalTime
));
1677 LoadStringW(hInst
, IDS_NOT_AVAILABLE
, szUsername
, ARRAYSIZE(szUsername
));
1678 LoadStringW(hInst
, IDS_NONE
, szCategory
, ARRAYSIZE(szCategory
));
1680 /* Get the username that generated the event, and filter it */
1681 GetEventUserName(pevlr
, szUsername
);
1682 if (!FilterByString(EventLogFilter
->Users
, szUsername
))
1685 // TODO: Filter by event ID and category
1687 GetEventType(pevlr
->EventType
, szEventTypeText
);
1688 GetEventCategory(EventLog
->LogName
, lpszSourceName
, pevlr
, szCategory
);
1690 StringCbPrintfW(szEventID
, sizeof(szEventID
), L
"%u", (pevlr
->EventID
& 0xFFFF));
1691 StringCbPrintfW(szCategoryID
, sizeof(szCategoryID
), L
"%u", pevlr
->EventCategory
);
1693 lviEventItem
.mask
= LVIF_IMAGE
| LVIF_TEXT
| LVIF_PARAM
;
1694 lviEventItem
.iItem
= 0;
1695 lviEventItem
.iSubItem
= 0;
1696 lviEventItem
.lParam
= (LPARAM
)pevlr
;
1697 lviEventItem
.pszText
= szEventTypeText
;
1699 switch (pevlr
->EventType
)
1701 case EVENTLOG_SUCCESS
:
1702 case EVENTLOG_INFORMATION_TYPE
:
1703 lviEventItem
.iImage
= 0;
1706 case EVENTLOG_WARNING_TYPE
:
1707 lviEventItem
.iImage
= 1;
1710 case EVENTLOG_ERROR_TYPE
:
1711 lviEventItem
.iImage
= 2;
1714 case EVENTLOG_AUDIT_SUCCESS
:
1715 lviEventItem
.iImage
= 3;
1718 case EVENTLOG_AUDIT_FAILURE
:
1719 lviEventItem
.iImage
= 4;
1723 lviEventItem
.iItem
= ListView_InsertItem(hwndListView
, &lviEventItem
);
1725 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 1, szLocalDate
);
1726 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 2, szLocalTime
);
1727 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 3, lpszSourceName
);
1728 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 4, szCategory
);
1729 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 5, szEventID
);
1730 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 6, szUsername
);
1731 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 7, lpszComputerName
);
1734 dwRead
-= pevlr
->Length
;
1735 pevlr
= (PEVENTLOGRECORD
)((LPBYTE
) pevlr
+ pevlr
->Length
);
1743 /* Close the event log */
1744 CloseEventLog(hEventLog
);
1746 } // end-for (LogIndex)
1748 /* All events loaded */
1752 ShowWindow(hwndStatusProgress
, SW_HIDE
);
1753 SendMessageW(hwndListView
, LVM_PROGRESS
, 0, FALSE
);
1755 // FIXME: Use something else instead of EventLog->LogName !!
1758 * Use a different formatting, whether the event log filter holds
1759 * only one log, or many logs (the latter case is WIP TODO!)
1761 if (EventLogFilter
->NumOfEventLogs
<= 1)
1763 StringCchPrintfExW(szWindowTitle
,
1764 ARRAYSIZE(szWindowTitle
),
1765 &lpTitleTemplateEnd
,
1768 szTitleTemplate
, szTitle
, EventLog
->LogName
); /* i = number of characters written */
1769 dwMaxLength
= (DWORD
)cchRemaining
;
1771 GetComputerNameW(lpTitleTemplateEnd
, &dwMaxLength
);
1773 StringCchCopyW(lpTitleTemplateEnd
, dwMaxLength
, lpMachineName
);
1775 StringCbPrintfW(szStatusText
,
1776 sizeof(szStatusText
),
1777 szStatusBarTemplate
,
1783 // TODO: Use a different title & implement filtering for multi-log filters !!
1784 // (EventLogFilter->NumOfEventLogs > 1)
1785 MessageBoxW(hwndMainWindow
,
1786 L
"Many-logs filtering is not implemented yet!!",
1788 MB_OK
| MB_ICONINFORMATION
);
1791 /* Update the status bar */
1792 StatusBar_SetText(hwndStatus
, 0, szStatusText
);
1794 /* Set the window title */
1795 SetWindowTextW(hwndMainWindow
, szWindowTitle
);
1797 /* Resume list view redraw */
1798 SendMessageW(hwndListView
, WM_SETREDRAW
, TRUE
, 0);
1800 EventLogFilter_Release(EventLogFilter
);
1802 CloseHandle(hStopEnumEvent
);
1803 InterlockedExchangePointer((PVOID
*)&hStopEnumEvent
, NULL
);
1809 * The purpose of this thread is to serialize the creation of the events
1810 * enumeration thread, since the Event Log Viewer currently only supports
1811 * one view, one event list, one enumeration.
1814 StartStopEnumEventsThread(IN LPVOID lpParameter
)
1816 HANDLE WaitHandles
[2];
1819 WaitHandles
[0] = hStartStopEnumEvent
; // End-of-application event
1820 WaitHandles
[1] = hStartEnumEvent
; // Command event
1824 WaitResult
= WaitForMultipleObjects(ARRAYSIZE(WaitHandles
),
1830 case WAIT_OBJECT_0
+ 0:
1832 /* End-of-application event signaled, quit this thread */
1834 /* Stop the previous enumeration */
1835 if (hEnumEventsThread
)
1839 SetEvent(hStopEnumEvent
);
1840 WaitForSingleObject(hEnumEventsThread
, INFINITE
);
1841 // NOTE: The following is done by the enumeration thread just before terminating.
1842 // hStopEnumEvent = NULL;
1845 CloseHandle(hEnumEventsThread
);
1846 hEnumEventsThread
= NULL
;
1849 /* Clear the list view and free the cached records */
1850 ListView_DeleteAllItems(hwndListView
);
1853 /* Reset the active filter */
1854 ActiveFilter
= NULL
;
1859 case WAIT_OBJECT_0
+ 1:
1861 /* Restart a new enumeration if needed */
1862 PEVENTLOGFILTER EventLogFilter
;
1864 /* Stop the previous enumeration */
1865 if (hEnumEventsThread
)
1869 SetEvent(hStopEnumEvent
);
1870 WaitForSingleObject(hEnumEventsThread
, INFINITE
);
1871 // NOTE: The following is done by the enumeration thread just before terminating.
1872 // hStopEnumEvent = NULL;
1875 CloseHandle(hEnumEventsThread
);
1876 hEnumEventsThread
= NULL
;
1879 /* Clear the list view and free the cached records */
1880 ListView_DeleteAllItems(hwndListView
);
1883 /* Reset the active filter */
1884 ActiveFilter
= NULL
;
1886 EventLogFilter
= InterlockedExchangePointer((PVOID
*)&EnumFilter
, NULL
);
1887 if (!EventLogFilter
)
1890 // Manual-reset event
1891 hStopEnumEvent
= CreateEventW(NULL
, TRUE
, FALSE
, NULL
);
1892 if (!hStopEnumEvent
)
1895 hEnumEventsThread
= CreateThread(NULL
,
1898 (LPVOID
)EventLogFilter
,
1901 if (!hEnumEventsThread
)
1903 CloseHandle(hStopEnumEvent
);
1904 hStopEnumEvent
= NULL
;
1907 // CloseHandle(hEnumEventsThread);
1908 ResumeThread(hEnumEventsThread
);
1915 /* Unknown command, must never go there! */
1916 return GetLastError();
1925 EnumEvents(IN PEVENTLOGFILTER EventLogFilter
)
1927 /* Signal the enumerator thread we want to enumerate events */
1928 InterlockedExchangePointer((PVOID
*)&EnumFilter
, EventLogFilter
);
1929 SetEvent(hStartEnumEvent
);
1935 GetSelectedFilter(OUT HTREEITEM
* phti OPTIONAL
)
1943 /* Get index of selected item */
1944 hti
= TreeView_GetSelection(hwndTreeView
);
1946 return NULL
; // No filter
1948 tvItemEx
.mask
= TVIF_PARAM
;
1949 tvItemEx
.hItem
= hti
;
1951 TreeView_GetItem(hwndTreeView
, &tvItemEx
);
1954 *phti
= tvItemEx
.hItem
;
1956 return (PEVENTLOGFILTER
)tvItemEx
.lParam
;
1961 OpenUserEventLog(VOID
)
1964 PEVENTLOGFILTER EventLogFilter
;
1965 HTREEITEM hItem
= NULL
;
1966 WCHAR szFileName
[MAX_PATH
];
1968 ZeroMemory(szFileName
, sizeof(szFileName
));
1970 sfn
.lpstrFile
= szFileName
;
1971 sfn
.nMaxFile
= ARRAYSIZE(szFileName
);
1973 if (!GetOpenFileNameW(&sfn
))
1975 sfn
.lpstrFile
[sfn
.nMaxFile
-1] = UNICODE_NULL
;
1977 /* Allocate a new event log entry */
1978 EventLog
= AllocEventLog(NULL
, sfn
.lpstrFile
, FALSE
);
1979 if (EventLog
== NULL
)
1982 /* Allocate a new event log filter entry for this event log */
1983 EventLogFilter
= AllocEventLogFilter(// LogName,
1984 TRUE
, TRUE
, TRUE
, TRUE
, TRUE
,
1987 if (EventLogFilter
== NULL
)
1989 HeapFree(GetProcessHeap(), 0, EventLog
);
1993 /* Add the event log and the filter into their lists */
1994 InsertTailList(&EventLogList
, &EventLog
->ListEntry
);
1995 InsertTailList(&EventLogFilterList
, &EventLogFilter
->ListEntry
);
1997 /* Retrieve and cache the event log file */
1998 EventLog
->FileName
= HeapAlloc(GetProcessHeap(), 0, sfn
.nMaxFile
* sizeof(WCHAR
));
1999 if (EventLog
->FileName
)
2000 StringCchCopyW(EventLog
->FileName
, sfn
.nMaxFile
, sfn
.lpstrFile
);
2002 hItem
= TreeViewAddItem(hwndTreeView
, htiUserLogs
,
2004 2, 3, (LPARAM
)EventLogFilter
);
2006 /* Select the event log */
2009 // TreeView_Expand(hwndTreeView, htiUserLogs, TVE_EXPAND);
2010 TreeView_SelectItem(hwndTreeView
, hItem
);
2011 TreeView_EnsureVisible(hwndTreeView
, hItem
);
2013 SetFocus(hwndTreeView
);
2017 SaveEventLog(IN PEVENTLOGFILTER EventLogFilter
)
2021 WCHAR szFileName
[MAX_PATH
];
2023 /* Bail out if there is no available filter */
2024 if (!EventLogFilter
)
2027 ZeroMemory(szFileName
, sizeof(szFileName
));
2029 sfn
.lpstrFile
= szFileName
;
2030 sfn
.nMaxFile
= ARRAYSIZE(szFileName
);
2032 if (!GetSaveFileNameW(&sfn
))
2035 EventLogFilter_AddRef(EventLogFilter
);
2037 EventLog
= EventLogFilter
->EventLogs
[0];
2038 hEventLog
= OpenEventLogW(EventLog
->ComputerName
, EventLog
->LogName
);
2040 EventLogFilter_Release(EventLogFilter
);
2044 ShowLastWin32Error();
2048 if (!BackupEventLogW(hEventLog
, szFileName
))
2049 ShowLastWin32Error();
2051 CloseEventLog(hEventLog
);
2055 CloseUserEventLog(IN PEVENTLOGFILTER EventLogFilter
, IN HTREEITEM hti
)
2057 /* Bail out if there is no available filter */
2058 if (!EventLogFilter
)
2061 if (InterlockedCompareExchangePointer((PVOID
*)&ActiveFilter
, NULL
, NULL
) == EventLogFilter
)
2063 /* Signal the enumerator thread we want to stop enumerating events */
2064 // EnumEvents(NULL);
2065 InterlockedExchangePointer((PVOID
*)&EnumFilter
, NULL
);
2066 SetEvent(hStartEnumEvent
);
2070 * The deletion of the item automatically triggers a TVN_SELCHANGED
2071 * notification, that will reset the ActiveFilter (in case the item
2072 * selected is a filter). Otherwise we reset it there.
2074 TreeView_DeleteItem(hwndTreeView
, hti
);
2076 /* Remove the filter from the list */
2077 RemoveEntryList(&EventLogFilter
->ListEntry
);
2078 EventLogFilter_Release(EventLogFilter
);
2080 // /* Select the default event log */
2081 // // TreeView_Expand(hwndTreeView, htiUserLogs, TVE_EXPAND);
2082 // TreeView_SelectItem(hwndTreeView, hItem);
2083 // TreeView_EnsureVisible(hwndTreeView, hItem);
2084 SetFocus(hwndTreeView
);
2089 ClearEvents(IN PEVENTLOGFILTER EventLogFilter
)
2094 WCHAR szFileName
[MAX_PATH
];
2095 WCHAR szMessage
[MAX_LOADSTRING
];
2097 /* Bail out if there is no available filter */
2098 if (!EventLogFilter
)
2101 ZeroMemory(szFileName
, sizeof(szFileName
));
2102 ZeroMemory(szMessage
, sizeof(szMessage
));
2104 LoadStringW(hInst
, IDS_CLEAREVENTS_MSG
, szMessage
, ARRAYSIZE(szMessage
));
2106 sfn
.lpstrFile
= szFileName
;
2107 sfn
.nMaxFile
= ARRAYSIZE(szFileName
);
2109 switch (MessageBoxW(hwndMainWindow
, szMessage
, szTitle
, MB_YESNOCANCEL
| MB_ICONINFORMATION
))
2115 sfn
.lpstrFile
= NULL
;
2119 if (!GetSaveFileNameW(&sfn
))
2124 EventLogFilter_AddRef(EventLogFilter
);
2126 EventLog
= EventLogFilter
->EventLogs
[0];
2127 hEventLog
= OpenEventLogW(EventLog
->ComputerName
, EventLog
->LogName
);
2129 EventLogFilter_Release(EventLogFilter
);
2133 ShowLastWin32Error();
2137 Success
= ClearEventLogW(hEventLog
, sfn
.lpstrFile
);
2139 ShowLastWin32Error();
2141 CloseEventLog(hEventLog
);
2147 Refresh(IN PEVENTLOGFILTER EventLogFilter
)
2149 /* Bail out if there is no available filter */
2150 if (!EventLogFilter
)
2153 /* Reenumerate the events through the filter */
2154 EnumEvents(EventLogFilter
);
2159 MyRegisterClass(HINSTANCE hInstance
)
2163 wcex
.cbSize
= sizeof(wcex
);
2165 wcex
.lpfnWndProc
= WndProc
;
2166 wcex
.cbClsExtra
= 0;
2167 wcex
.cbWndExtra
= 0;
2168 wcex
.hInstance
= hInstance
;
2169 wcex
.hIcon
= LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_EVENTVWR
));
2170 wcex
.hCursor
= LoadCursorW(NULL
, MAKEINTRESOURCEW(IDC_ARROW
));
2171 wcex
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1); // COLOR_WINDOW + 1
2172 wcex
.lpszMenuName
= MAKEINTRESOURCEW(IDM_EVENTVWR
);
2173 wcex
.lpszClassName
= szWindowClass
;
2174 wcex
.hIconSm
= (HICON
)LoadImageW(hInstance
,
2175 MAKEINTRESOURCEW(IDI_EVENTVWR
),
2181 return RegisterClassExW(&wcex
);
2186 GetDisplayNameFileAndID(IN LPCWSTR lpLogName
,
2187 OUT PWCHAR lpModuleName
, // TODO: Add IN DWORD BufLen
2188 OUT PDWORD pdwMessageID
)
2190 BOOL Success
= FALSE
;
2196 DWORD dwMessageID
= 0;
2197 WCHAR szModuleName
[MAX_PATH
];
2199 /* Use a default value for the message ID */
2202 cbKeyPath
= (wcslen(EVENTLOG_BASE_KEY
) + wcslen(lpLogName
) + 1) * sizeof(WCHAR
);
2203 KeyPath
= HeapAlloc(GetProcessHeap(), 0, cbKeyPath
);
2207 StringCbCopyW(KeyPath
, cbKeyPath
, EVENTLOG_BASE_KEY
);
2208 StringCbCatW(KeyPath
, cbKeyPath
, lpLogName
);
2210 Result
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, KeyPath
, 0, KEY_QUERY_VALUE
, &hLogKey
);
2211 HeapFree(GetProcessHeap(), 0, KeyPath
);
2212 if (Result
!= ERROR_SUCCESS
)
2215 cbData
= sizeof(szModuleName
);
2216 Result
= RegQueryValueExW(hLogKey
,
2220 (LPBYTE
)szModuleName
,
2222 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_EXPAND_SZ
&& Type
!= REG_SZ
))
2224 szModuleName
[0] = UNICODE_NULL
;
2228 /* NULL-terminate the string and expand it */
2229 szModuleName
[cbData
/ sizeof(WCHAR
) - 1] = UNICODE_NULL
;
2230 ExpandEnvironmentStringsW(szModuleName
, lpModuleName
, ARRAYSIZE(szModuleName
));
2235 * If we have a 'DisplayNameFile', query for 'DisplayNameID';
2236 * otherwise it's not really useful. 'DisplayNameID' is optional.
2240 cbData
= sizeof(dwMessageID
);
2241 Result
= RegQueryValueExW(hLogKey
,
2245 (LPBYTE
)&dwMessageID
,
2247 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_DWORD
))
2250 *pdwMessageID
= dwMessageID
;
2253 RegCloseKey(hLogKey
);
2260 BuildLogListAndFilterList(IN LPCWSTR lpComputerName
)
2263 HKEY hEventLogKey
, hLogKey
;
2264 DWORD dwNumLogs
= 0;
2265 DWORD dwIndex
, dwMaxKeyLength
;
2268 PEVENTLOGFILTER EventLogFilter
;
2269 LPWSTR LogName
= NULL
;
2270 WCHAR szModuleName
[MAX_PATH
];
2273 LPWSTR lpDisplayName
;
2274 HTREEITEM hRootNode
= NULL
, hItem
= NULL
, hItemDefault
= NULL
;
2276 /* Open the EventLog key */
2277 // FIXME: Use local or remote computer
2278 Result
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, EVENTLOG_BASE_KEY
, 0, KEY_READ
, &hEventLogKey
);
2279 if (Result
!= ERROR_SUCCESS
)
2284 /* Retrieve the number of event logs enumerated as registry keys */
2285 Result
= RegQueryInfoKeyW(hEventLogKey
, NULL
, NULL
, NULL
, &dwNumLogs
, &dwMaxKeyLength
,
2286 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
2287 if (Result
!= ERROR_SUCCESS
)
2294 /* Take the NULL terminator into account */
2297 /* Allocate the temporary buffer */
2298 LogName
= HeapAlloc(GetProcessHeap(), 0, dwMaxKeyLength
* sizeof(WCHAR
));
2302 /* Enumerate and retrieve each event log name */
2303 for (dwIndex
= 0; dwIndex
< dwNumLogs
; dwIndex
++)
2305 lpcName
= dwMaxKeyLength
;
2306 Result
= RegEnumKeyExW(hEventLogKey
, dwIndex
, LogName
, &lpcName
, NULL
, NULL
, NULL
, NULL
);
2307 if (Result
!= ERROR_SUCCESS
)
2310 /* Take the NULL terminator into account */
2313 /* Allocate a new event log entry */
2314 EventLog
= AllocEventLog(lpComputerName
, LogName
, TRUE
);
2315 if (EventLog
== NULL
)
2318 /* Allocate a new event log filter entry for this event log */
2319 EventLogFilter
= AllocEventLogFilter(// LogName,
2320 TRUE
, TRUE
, TRUE
, TRUE
, TRUE
,
2323 if (EventLogFilter
== NULL
)
2325 HeapFree(GetProcessHeap(), 0, EventLog
);
2329 /* Add the event log and the filter into their lists */
2330 InsertTailList(&EventLogList
, &EventLog
->ListEntry
);
2331 InsertTailList(&EventLogFilterList
, &EventLogFilter
->ListEntry
);
2333 EventLog
->FileName
= NULL
;
2335 /* Retrieve and cache the event log file */
2336 Result
= RegOpenKeyExW(hEventLogKey
,
2341 if (Result
== ERROR_SUCCESS
)
2344 Result
= RegQueryValueExW(hLogKey
,
2350 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_EXPAND_SZ
&& Type
!= REG_SZ
))
2352 // Windows' EventLog uses some kind of default value, we do not.
2353 EventLog
->FileName
= NULL
;
2357 lpcName
= ROUND_DOWN(lpcName
, sizeof(WCHAR
));
2358 EventLog
->FileName
= HeapAlloc(GetProcessHeap(), 0, lpcName
);
2359 if (EventLog
->FileName
)
2361 Result
= RegQueryValueExW(hLogKey
,
2365 (LPBYTE
)EventLog
->FileName
,
2367 if (Result
!= ERROR_SUCCESS
)
2369 HeapFree(GetProcessHeap(), 0, EventLog
->FileName
);
2370 EventLog
->FileName
= NULL
;
2374 EventLog
->FileName
[lpcName
/ sizeof(WCHAR
) - 1] = UNICODE_NULL
;
2379 RegCloseKey(hLogKey
);
2382 /* Get the display name for the event log */
2383 lpDisplayName
= NULL
;
2385 ZeroMemory(szModuleName
, sizeof(szModuleName
));
2386 if (GetDisplayNameFileAndID(LogName
, szModuleName
, &dwMessageID
))
2388 /* Retrieve the message string without appending extra newlines */
2390 GetMessageStringFromDll(szModuleName
,
2391 FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_HMODULE
|
2392 FORMAT_MESSAGE_IGNORE_INSERTS
| FORMAT_MESSAGE_MAX_WIDTH_MASK
,
2399 * Select the correct tree root node, whether the log is a System
2400 * or an Application log. Default to Application log otherwise.
2402 hRootNode
= htiAppLogs
;
2403 for (lpcName
= 0; lpcName
< ARRAYSIZE(SystemLogs
); ++lpcName
)
2405 /* Check whether the log name is part of the system logs */
2406 if (wcsicmp(LogName
, SystemLogs
[lpcName
]) == 0)
2408 hRootNode
= htiSystemLogs
;
2413 hItem
= TreeViewAddItem(hwndTreeView
, hRootNode
,
2414 (lpDisplayName
? lpDisplayName
: LogName
),
2415 2, 3, (LPARAM
)EventLogFilter
);
2417 /* Try to get the default event log: "Application" */
2418 if ((hItemDefault
== NULL
) && (wcsicmp(LogName
, SystemLogs
[0]) == 0))
2420 hItemDefault
= hItem
;
2423 /* Free the buffer allocated by FormatMessage */
2425 LocalFree(lpDisplayName
);
2428 HeapFree(GetProcessHeap(), 0, LogName
);
2431 RegCloseKey(hEventLogKey
);
2433 /* Select the default event log */
2436 // TreeView_Expand(hwndTreeView, hRootNode, TVE_EXPAND);
2437 TreeView_SelectItem(hwndTreeView
, hItemDefault
);
2438 TreeView_EnsureVisible(hwndTreeView
, hItemDefault
);
2440 SetFocus(hwndTreeView
);
2451 while (!IsListEmpty(&EventLogList
))
2453 Entry
= RemoveHeadList(&EventLogList
);
2454 EventLog
= (PEVENTLOG
)CONTAINING_RECORD(Entry
, EVENTLOG
, ListEntry
);
2455 EventLog_Free(EventLog
);
2462 FreeLogFilterList(VOID
)
2465 PEVENTLOGFILTER EventLogFilter
;
2467 while (!IsListEmpty(&EventLogFilterList
))
2469 Entry
= RemoveHeadList(&EventLogFilterList
);
2470 EventLogFilter
= (PEVENTLOGFILTER
)CONTAINING_RECORD(Entry
, EVENTLOGFILTER
, ListEntry
);
2471 EventLogFilter_Free(EventLogFilter
);
2474 ActiveFilter
= NULL
;
2481 * ListView subclassing to handle WM_PAINT messages before and after they are
2482 * handled by the ListView window itself. We cannot use at this level the
2483 * custom-drawn notifications that are more suitable for drawing elements
2484 * inside the ListView.
2486 static WNDPROC orgListViewWndProc
= NULL
;
2487 static BOOL IsLoading
= FALSE
;
2490 ListViewWndProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
2496 /* TRUE: Create the dialog; FALSE: Destroy the dialog */
2497 IsLoading
= !!(BOOL
)lParam
;
2503 /* This code is adapted from: http://www.codeproject.com/Articles/216/Indicating-an-empty-ListView */
2511 COLORREF crTextOld
, crTextBkOld
;
2512 NONCLIENTMETRICSW ncm
;
2513 HFONT hFont
, hFontOld
;
2516 nItemCount
= ListView_GetItemCount(hWnd
);
2517 if (!IsLoading
&& nItemCount
> 0)
2522 * We could have used lpNMCustomDraw->nmcd.rc for the rectangle,
2523 * but this one actually holds the rectangle of the list view
2524 * that is being currently repainted, so that it can be smaller
2525 * than the list view proper. This is especially true when using
2526 * COMCTL32.DLL version <= 6.0 .
2529 GetClientRect(hWnd
, &rc
);
2530 hwndHeader
= ListView_GetHeader(hWnd
);
2533 /* Note that we could also use Header_GetItemRect() */
2534 GetClientRect(hwndHeader
, &rcH
);
2535 rc
.top
+= rcH
.bottom
;
2538 /* Add some space between the top of the list view and the text */
2541 BeginPaint(hWnd
, &ps
);
2543 * NOTE: Using a secondary hDC (and not the ps.hdc) gives the strange
2544 * property that the text is always recentered on the current view of
2545 * the window, instead of being scrolled together with the contents of
2552 * NOTE: We could have kept lpNMCustomDraw->clrText and
2553 * lpNMCustomDraw->clrTextBk, but they usually do not contain
2554 * the correct default colors for the items / default text.
2557 SetTextColor(hDC
, GetSysColor(COLOR_WINDOWTEXT
));
2559 SetBkColor(hDC
, GetSysColor(COLOR_WINDOW
));
2561 // FIXME: Cache the font?
2562 ncm
.cbSize
= sizeof(ncm
);
2564 if (SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
,
2565 sizeof(ncm
), &ncm
, 0))
2567 hFont
= CreateFontIndirectW(&ncm
.lfMessageFont
);
2570 hFont
= GetStockFont(DEFAULT_GUI_FONT
);
2572 hFontOld
= (HFONT
)SelectObject(hDC
, hFont
);
2574 FillRect(hDC
, &rc
, GetSysColorBrush(COLOR_WINDOW
));
2576 if (nItemCount
<= 0)
2577 lpszString
= szEmptyList
;
2578 else // if (IsLoading)
2579 lpszString
= szLoadingWait
;
2585 DT_CENTER
| DT_WORDBREAK
| DT_NOPREFIX
| DT_NOCLIP
);
2587 SelectObject(hDC
, hFontOld
);
2589 DeleteObject(hFont
);
2591 SetBkColor(hDC
, crTextBkOld
);
2592 SetTextColor(hDC
, crTextOld
);
2594 ReleaseDC(hWnd
, hDC
);
2595 EndPaint(hWnd
, &ps
);
2600 // case WM_ERASEBKGND:
2604 /* Continue with default message processing */
2605 return CallWindowProcW(orgListViewWndProc
, hWnd
, uMsg
, wParam
, lParam
);
2609 InitInstance(HINSTANCE hInstance
,
2615 LVCOLUMNW lvc
= {0};
2618 hInst
= hInstance
; // Store instance handle in our global variable
2620 /* Create the main window */
2621 hwndMainWindow
= CreateWindowW(szWindowClass
,
2623 WS_OVERLAPPEDWINDOW
| WS_CLIPCHILDREN
,
2624 CW_USEDEFAULT
, 0, CW_USEDEFAULT
, 0,
2629 if (!hwndMainWindow
)
2632 /* Create the status bar */
2633 hwndStatus
= CreateWindowExW(0, // no extended styles
2634 STATUSCLASSNAMEW
, // status bar
2636 WS_CHILD
| WS_VISIBLE
| CCS_BOTTOM
| SBARS_SIZEGRIP
, // styles
2637 0, 0, 0, 0, // x, y, cx, cy
2638 hwndMainWindow
, // parent window
2639 (HMENU
)100, // window ID
2640 hInstance
, // instance
2641 NULL
); // window data
2644 GetClientRect(hwndMainWindow
, &rcClient
);
2645 GetWindowRect(hwndStatus
, &rs
);
2646 StatusHeight
= rs
.bottom
- rs
.top
;
2648 /* Create a progress bar in the status bar (hidden by default) */
2649 StatusBar_GetItemRect(hwndStatus
, 0, &rs
);
2650 hwndStatusProgress
= CreateWindowExW(0, // no extended styles
2651 PROGRESS_CLASSW
, // status bar
2653 WS_CHILD
| PBS_SMOOTH
, // styles
2654 rs
.left
, rs
.top
, rs
.right
-rs
.left
, rs
.bottom
-rs
.top
, // x, y, cx, cy
2655 hwndStatus
, // parent window
2657 hInstance
, // instance
2658 NULL
); // window data
2659 ProgressBar_SetStep(hwndStatusProgress
, 1);
2661 /* Create the TreeView */
2662 hwndTreeView
= CreateWindowExW(WS_EX_CLIENTEDGE
,
2665 // WS_CHILD | WS_VISIBLE | TVS_HASLINES | TVS_SHOWSELALWAYS,
2666 WS_CHILD
| WS_VISIBLE
| /* WS_TABSTOP | */ TVS_HASLINES
| TVS_HASBUTTONS
| TVS_LINESATROOT
| TVS_EDITLABELS
| TVS_SHOWSELALWAYS
,
2669 nSplitPos
- SPLIT_WIDTH
/2,
2670 (rcClient
.bottom
- rcClient
.top
) - StatusHeight
,
2676 /* Create the ImageList */
2677 hSmall
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
),
2678 GetSystemMetrics(SM_CYSMICON
),
2679 ILC_COLOR32
| ILC_MASK
, // ILC_COLOR24
2682 /* Add event type icons to the ImageList: closed/opened folder, event log (normal/viewed) */
2683 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_CLOSED_CATEGORY
)));
2684 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_OPENED_CATEGORY
)));
2685 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_EVENTLOG
)));
2686 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_EVENTVWR
)));
2688 /* Assign the ImageList to the Tree View */
2689 TreeView_SetImageList(hwndTreeView
, hSmall
, TVSIL_NORMAL
);
2691 /* Add the event logs nodes */
2693 LoadStringW(hInstance
, IDS_EVENTLOG_SYSTEM
, szTemp
, ARRAYSIZE(szTemp
));
2694 htiSystemLogs
= TreeViewAddItem(hwndTreeView
, NULL
, szTemp
, 0, 1, (LPARAM
)NULL
);
2695 // "Application Logs"
2696 LoadStringW(hInstance
, IDS_EVENTLOG_APP
, szTemp
, ARRAYSIZE(szTemp
));
2697 htiAppLogs
= TreeViewAddItem(hwndTreeView
, NULL
, szTemp
, 0, 1, (LPARAM
)NULL
);
2699 LoadStringW(hInstance
, IDS_EVENTLOG_USER
, szTemp
, ARRAYSIZE(szTemp
));
2700 htiUserLogs
= TreeViewAddItem(hwndTreeView
, NULL
, szTemp
, 0, 1, (LPARAM
)NULL
);
2702 /* Create the ListView */
2703 hwndListView
= CreateWindowExW(WS_EX_CLIENTEDGE
,
2706 WS_CHILD
| WS_VISIBLE
| LVS_SHOWSELALWAYS
| LVS_REPORT
,
2707 nSplitPos
+ SPLIT_WIDTH
/2,
2709 (rcClient
.right
- rcClient
.left
) - nSplitPos
- SPLIT_WIDTH
/2,
2710 (rcClient
.bottom
- rcClient
.top
) - StatusHeight
,
2716 /* Add the extended ListView styles */
2717 ListView_SetExtendedListViewStyle(hwndListView
, LVS_EX_HEADERDRAGDROP
| LVS_EX_FULLROWSELECT
|LVS_EX_LABELTIP
);
2719 /* Create the ImageList */
2720 hSmall
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
),
2721 GetSystemMetrics(SM_CYSMICON
),
2722 ILC_COLOR32
| ILC_MASK
, // ILC_COLOR24
2725 /* Add event type icons to the ImageList */
2726 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_INFORMATIONICON
)));
2727 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_WARNINGICON
)));
2728 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_ERRORICON
)));
2729 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_AUDITSUCCESSICON
)));
2730 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_AUDITFAILUREICON
)));
2732 /* Assign the ImageList to the List View */
2733 ListView_SetImageList(hwndListView
, hSmall
, LVSIL_SMALL
);
2735 /* Now set up the listview with its columns */
2736 lvc
.mask
= LVCF_TEXT
| LVCF_WIDTH
;
2738 LoadStringW(hInstance
,
2742 lvc
.pszText
= szTemp
;
2743 ListView_InsertColumn(hwndListView
, 0, &lvc
);
2746 LoadStringW(hInstance
,
2750 lvc
.pszText
= szTemp
;
2751 ListView_InsertColumn(hwndListView
, 1, &lvc
);
2754 LoadStringW(hInstance
,
2758 lvc
.pszText
= szTemp
;
2759 ListView_InsertColumn(hwndListView
, 2, &lvc
);
2762 LoadStringW(hInstance
,
2766 lvc
.pszText
= szTemp
;
2767 ListView_InsertColumn(hwndListView
, 3, &lvc
);
2770 LoadStringW(hInstance
,
2774 lvc
.pszText
= szTemp
;
2775 ListView_InsertColumn(hwndListView
, 4, &lvc
);
2778 LoadStringW(hInstance
,
2782 lvc
.pszText
= szTemp
;
2783 ListView_InsertColumn(hwndListView
, 5, &lvc
);
2786 LoadStringW(hInstance
,
2790 lvc
.pszText
= szTemp
;
2791 ListView_InsertColumn(hwndListView
, 6, &lvc
);
2794 LoadStringW(hInstance
,
2798 lvc
.pszText
= szTemp
;
2799 ListView_InsertColumn(hwndListView
, 7, &lvc
);
2801 /* Subclass the ListView */
2802 // orgListViewWndProc = SubclassWindow(hwndListView, ListViewWndProc);
2803 orgListViewWndProc
= (WNDPROC
)(LONG_PTR
)GetWindowLongPtrW(hwndListView
, GWLP_WNDPROC
);
2804 SetWindowLongPtrW(hwndListView
, GWLP_WNDPROC
, (LONG_PTR
)ListViewWndProc
);
2806 /* Initialize the save Dialog */
2807 ZeroMemory(&sfn
, sizeof(sfn
));
2808 ZeroMemory(szSaveFilter
, sizeof(szSaveFilter
));
2810 LoadStringW(hInst
, IDS_SAVE_FILTER
, szSaveFilter
, ARRAYSIZE(szSaveFilter
));
2812 sfn
.lStructSize
= sizeof(sfn
);
2813 sfn
.hwndOwner
= hwndMainWindow
;
2814 sfn
.hInstance
= hInstance
;
2815 sfn
.lpstrFilter
= szSaveFilter
;
2816 sfn
.lpstrInitialDir
= NULL
;
2817 sfn
.Flags
= OFN_HIDEREADONLY
| OFN_SHAREAWARE
;
2818 sfn
.lpstrDefExt
= NULL
;
2820 ShowWindow(hwndMainWindow
, nCmdShow
);
2821 UpdateWindow(hwndMainWindow
);
2827 ExitInstance(HINSTANCE hInstance
)
2829 /* Restore the original ListView WndProc */
2830 // SubclassWindow(hwndListView, orgListViewWndProc);
2831 SetWindowLongPtrW(hwndListView
, GWLP_WNDPROC
, (LONG_PTR
)orgListViewWndProc
);
2832 orgListViewWndProc
= NULL
;
2835 VOID
ResizeWnd(INT cx
, INT cy
)
2841 /* Resize the status bar -- now done in WM_SIZE */
2842 // SendMessageW(hwndStatus, WM_SIZE, 0, 0);
2843 GetWindowRect(hwndStatus
, &rs
);
2844 StatusHeight
= rs
.bottom
- rs
.top
;
2846 /* Move the progress bar */
2847 StatusBar_GetItemRect(hwndStatus
, 0, &rs
);
2848 MoveWindow(hwndStatusProgress
,
2849 rs
.left
, rs
.top
, rs
.right
-rs
.left
, rs
.bottom
-rs
.top
,
2850 IsWindowVisible(hwndStatusProgress
) ? TRUE
: FALSE
);
2852 nSplitPos
= min(max(nSplitPos
, SPLIT_WIDTH
/2), cx
- SPLIT_WIDTH
/2);
2854 hdwp
= BeginDeferWindowPos(2);
2857 hdwp
= DeferWindowPos(hdwp
,
2861 nSplitPos
- SPLIT_WIDTH
/2, cy
- StatusHeight
,
2862 SWP_NOZORDER
| SWP_NOACTIVATE
);
2865 hdwp
= DeferWindowPos(hdwp
,
2868 nSplitPos
+ SPLIT_WIDTH
/2, 0,
2869 cx
- nSplitPos
- SPLIT_WIDTH
/2, cy
- StatusHeight
,
2870 SWP_NOZORDER
| SWP_NOACTIVATE
);
2873 EndDeferWindowPos(hdwp
);
2878 WndProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
2885 hMainMenu
= GetMenu(hWnd
);
2894 LPNMHDR hdr
= (LPNMHDR
)lParam
;
2896 if (hdr
->hwndFrom
== hwndListView
)
2902 LPNMITEMACTIVATE lpnmitem
= (LPNMITEMACTIVATE
)lParam
;
2903 PEVENTLOGFILTER EventLogFilter
= GetSelectedFilter(NULL
);
2904 if (lpnmitem
->iItem
!= -1 && EventLogFilter
)
2906 EventLogFilter_AddRef(EventLogFilter
);
2907 DialogBoxParamW(hInst
,
2908 MAKEINTRESOURCEW(IDD_EVENTPROPERTIES
),
2911 (LPARAM
)EventLogFilter
);
2912 EventLogFilter_Release(EventLogFilter
);
2918 else if (hdr
->hwndFrom
== hwndTreeView
)
2922 case TVN_BEGINLABELEDIT
:
2924 HTREEITEM hItem
= ((LPNMTVDISPINFO
)lParam
)->item
.hItem
;
2926 /* Disable label editing for root nodes */
2927 return ((hItem
== htiSystemLogs
) ||
2928 (hItem
== htiAppLogs
) ||
2929 (hItem
== htiUserLogs
));
2932 case TVN_ENDLABELEDIT
:
2934 TVITEMW item
= ((LPNMTVDISPINFO
)lParam
)->item
;
2935 HTREEITEM hItem
= item
.hItem
;
2937 /* Disable label editing for root nodes */
2938 if ((hItem
== htiSystemLogs
) ||
2939 (hItem
== htiAppLogs
) ||
2940 (hItem
== htiUserLogs
))
2947 LPWSTR pszText
= item
.pszText
;
2949 /* Trim all whitespace */
2950 while (*pszText
&& iswspace(*pszText
))
2964 case TVN_SELCHANGED
:
2966 PEVENTLOGFILTER EventLogFilter
=
2967 (PEVENTLOGFILTER
)((LPNMTREEVIEW
)lParam
)->itemNew
.lParam
;
2972 * If we have selected a filter, enable the menu commands;
2973 * they will possibly be updated after events enumeration.
2975 EnableMenuItem(hMainMenu
, IDM_SAVE_EVENTLOG
, MF_BYCOMMAND
| MF_ENABLED
);
2976 EnableMenuItem(hMainMenu
, IDM_CLOSE_EVENTLOG
, MF_BYCOMMAND
| MF_ENABLED
);
2977 EnableMenuItem(hMainMenu
, IDM_CLEAR_EVENTS
, MF_BYCOMMAND
| MF_ENABLED
);
2978 EnableMenuItem(hMainMenu
, IDM_RENAME_EVENTLOG
, MF_BYCOMMAND
| MF_ENABLED
);
2979 EnableMenuItem(hMainMenu
, IDM_EVENTLOG_SETTINGS
, MF_BYCOMMAND
| MF_ENABLED
);
2983 EnableMenuItem(hMainMenu
, IDM_SAVE_EVENTLOG
, MF_BYCOMMAND
| MF_GRAYED
);
2984 EnableMenuItem(hMainMenu
, IDM_CLOSE_EVENTLOG
, MF_BYCOMMAND
| MF_GRAYED
);
2985 EnableMenuItem(hMainMenu
, IDM_CLEAR_EVENTS
, MF_BYCOMMAND
| MF_GRAYED
);
2986 EnableMenuItem(hMainMenu
, IDM_RENAME_EVENTLOG
, MF_BYCOMMAND
| MF_GRAYED
);
2987 EnableMenuItem(hMainMenu
, IDM_EVENTLOG_SETTINGS
, MF_BYCOMMAND
| MF_GRAYED
);
2991 * The enumeration thread that is triggered by EnumEvents
2992 * will set a new value for the 'ActiveFilter'.
2995 EnumEvents(EventLogFilter
);
3006 /* Parse the menu selections */
3007 switch (LOWORD(wParam
))
3009 case IDM_OPEN_EVENTLOG
:
3013 case IDM_SAVE_EVENTLOG
:
3014 SaveEventLog(GetSelectedFilter(NULL
));
3017 case IDM_CLOSE_EVENTLOG
:
3020 PEVENTLOGFILTER EventLogFilter
= GetSelectedFilter(&hti
);
3021 CloseUserEventLog(EventLogFilter
, hti
);
3025 case IDM_CLEAR_EVENTS
:
3027 PEVENTLOGFILTER EventLogFilter
= GetSelectedFilter(NULL
);
3028 if (EventLogFilter
&& ClearEvents(EventLogFilter
))
3029 Refresh(EventLogFilter
);
3033 case IDM_RENAME_EVENTLOG
:
3034 if (GetFocus() == hwndTreeView
)
3035 TreeView_EditLabel(hwndTreeView
, TreeView_GetSelection(hwndTreeView
));
3038 case IDM_EVENTLOG_SETTINGS
:
3040 PEVENTLOGFILTER EventLogFilter
= GetSelectedFilter(NULL
);
3041 // TODO: Check the returned value?
3043 EventLogProperties(hInst
, hWnd
, EventLogFilter
);
3047 case IDM_LIST_NEWEST
:
3049 CheckMenuRadioItem(hMainMenu
, IDM_LIST_NEWEST
, IDM_LIST_OLDEST
, IDM_LIST_NEWEST
, MF_BYCOMMAND
);
3050 if (!NewestEventsFirst
)
3052 NewestEventsFirst
= TRUE
;
3053 Refresh(GetSelectedFilter(NULL
));
3058 case IDM_LIST_OLDEST
:
3060 CheckMenuRadioItem(hMainMenu
, IDM_LIST_NEWEST
, IDM_LIST_OLDEST
, IDM_LIST_OLDEST
, MF_BYCOMMAND
);
3061 if (NewestEventsFirst
)
3063 NewestEventsFirst
= FALSE
;
3064 Refresh(GetSelectedFilter(NULL
));
3070 Refresh(GetSelectedFilter(NULL
));
3076 WCHAR szCopyright
[MAX_LOADSTRING
];
3078 hIcon
= LoadIconW(hInst
, MAKEINTRESOURCEW(IDI_EVENTVWR
));
3079 LoadStringW(hInst
, IDS_COPYRIGHT
, szCopyright
, ARRAYSIZE(szCopyright
));
3080 ShellAboutW(hWnd
, szTitle
, szCopyright
, hIcon
);
3081 DeleteObject(hIcon
);
3086 MessageBoxW(hwndMainWindow
,
3087 L
"Help not implemented yet!",
3089 MB_OK
| MB_ICONINFORMATION
);
3093 DestroyWindow(hWnd
);
3097 return DefWindowProcW(hWnd
, uMsg
, wParam
, lParam
);
3103 if (LOWORD(lParam
) == HTCLIENT
)
3107 ScreenToClient(hWnd
, &pt
);
3108 if (pt
.x
>= nSplitPos
- SPLIT_WIDTH
/2 && pt
.x
< nSplitPos
+ SPLIT_WIDTH
/2 + 1)
3111 GetClientRect(hWnd
, &rect
);
3112 GetWindowRect(hwndStatus
, &rs
);
3113 if (pt
.y
>= rect
.top
&& pt
.y
< rect
.bottom
- (rs
.bottom
- rs
.top
))
3115 SetCursor(LoadCursorW(NULL
, IDC_SIZEWE
));
3122 case WM_LBUTTONDOWN
:
3124 INT x
= GET_X_LPARAM(lParam
);
3125 if (x
>= nSplitPos
- SPLIT_WIDTH
/2 && x
< nSplitPos
+ SPLIT_WIDTH
/2 + 1)
3133 case WM_RBUTTONDOWN
:
3134 if (GetCapture() == hWnd
)
3136 GetClientRect(hWnd
, &rect
);
3137 nSplitPos
= GET_X_LPARAM(lParam
);
3138 ResizeWnd(rect
.right
, rect
.bottom
);
3144 if (GetCapture() == hWnd
)
3146 INT x
= GET_X_LPARAM(lParam
);
3148 GetClientRect(hWnd
, &rect
);
3150 x
= min(max(x
, SPLIT_WIDTH
/2), rect
.right
- rect
.left
- SPLIT_WIDTH
/2);
3154 ResizeWnd(rect
.right
- rect
.left
, rect
.bottom
- rect
.top
);
3161 SendMessageW(hwndStatus
, WM_SIZE
, 0, 0);
3162 ResizeWnd(LOWORD(lParam
), HIWORD(lParam
));
3167 return DefWindowProcW(hWnd
, uMsg
, wParam
, lParam
);
3177 InitPropertiesDlg(HWND hDlg
, PEVENTLOG EventLog
)
3179 LPWSTR lpLogName
= EventLog
->LogName
;
3182 DWORD dwMaxSize
= 0, dwRetention
= 0;
3184 WIN32_FIND_DATAW FileInfo
; // WIN32_FILE_ATTRIBUTE_DATA
3185 ULARGE_INTEGER FileSize
;
3186 WCHAR wszBuf
[MAX_PATH
];
3187 WCHAR szTemp
[MAX_LOADSTRING
];
3195 if (EventLog
->Permanent
)
3198 cbKeyPath
= (wcslen(EVENTLOG_BASE_KEY
) + wcslen(lpLogName
) + 1) * sizeof(WCHAR
);
3199 KeyPath
= HeapAlloc(GetProcessHeap(), 0, cbKeyPath
);
3205 StringCbCopyW(KeyPath
, cbKeyPath
, EVENTLOG_BASE_KEY
);
3206 StringCbCatW(KeyPath
, cbKeyPath
, lpLogName
);
3208 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE
, KeyPath
, 0, KEY_QUERY_VALUE
, &hLogKey
) != ERROR_SUCCESS
)
3210 HeapFree(GetProcessHeap(), 0, KeyPath
);
3213 HeapFree(GetProcessHeap(), 0, KeyPath
);
3216 cbData
= sizeof(dwMaxSize
);
3217 Result
= RegQueryValueExW(hLogKey
,
3223 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_DWORD
))
3225 // dwMaxSize = 512 * 1024; /* 512 kBytes */
3231 cbData
= sizeof(dwRetention
);
3232 Result
= RegQueryValueExW(hLogKey
,
3236 (LPBYTE
)&dwRetention
,
3238 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_DWORD
))
3240 /* On Windows 2003 it is 604800 (secs) == 7 days */
3243 /* Convert in days, rounded up */ // ROUND_UP
3244 // dwRetention = ROUND_UP(dwRetention, 24*3600) / (24*3600);
3245 dwRetention
= (dwRetention
+ 24*3600 - 1) / (24*3600);
3248 RegCloseKey(hLogKey
);
3255 SetDlgItemTextW(hDlg
, IDC_DISPLAYNAME
, lpLogName
); // FIXME!
3256 SetDlgItemTextW(hDlg
, IDC_LOGNAME
, lpLogName
);
3258 FileName
= EventLog
->FileName
;
3259 if (FileName
&& *FileName
)
3261 ExpandEnvironmentStringsW(FileName
, wszBuf
, MAX_PATH
);
3264 SetDlgItemTextW(hDlg
, IDC_LOGFILE
, FileName
);
3267 * The general problem here (and in the shell as well) is that
3268 * GetFileAttributesEx fails for files that are opened without
3269 * shared access. To retrieve file information for those we need
3270 * to use something else: FindFirstFile, on the full file name.
3273 Success
= GetFileAttributesExW(FileName
,
3274 GetFileExInfoStandard
,
3275 (LPWIN32_FILE_ATTRIBUTE_DATA
)&FileInfo
);
3278 HANDLE hFind
= FindFirstFileW(FileName
, &FileInfo
);
3279 Success
= (hFind
!= INVALID_HANDLE_VALUE
);
3284 // Starting there, FileName is invalid (because it uses wszBuf)
3288 FileSize
.u
.LowPart
= FileInfo
.nFileSizeLow
;
3289 FileSize
.u
.HighPart
= FileInfo
.nFileSizeHigh
;
3290 if (FormatFileSizeWithBytes(&FileSize
, wszBuf
, ARRAYSIZE(wszBuf
)))
3291 SetDlgItemTextW(hDlg
, IDC_SIZE_LABEL
, wszBuf
);
3293 LoadStringW(hInst
, IDS_NOT_AVAILABLE
, szTemp
, ARRAYSIZE(szTemp
));
3295 if (GetFileTimeString(&FileInfo
.ftCreationTime
, wszBuf
, ARRAYSIZE(wszBuf
)))
3296 SetDlgItemTextW(hDlg
, IDC_CREATED_LABEL
, wszBuf
);
3298 SetDlgItemTextW(hDlg
, IDC_CREATED_LABEL
, szTemp
);
3300 if (GetFileTimeString(&FileInfo
.ftLastWriteTime
, wszBuf
, ARRAYSIZE(wszBuf
)))
3301 SetDlgItemTextW(hDlg
, IDC_MODIFIED_LABEL
, wszBuf
);
3303 SetDlgItemTextW(hDlg
, IDC_MODIFIED_LABEL
, szTemp
);
3305 if (GetFileTimeString(&FileInfo
.ftLastAccessTime
, wszBuf
, ARRAYSIZE(wszBuf
)))
3306 SetDlgItemTextW(hDlg
, IDC_ACCESSED_LABEL
, wszBuf
);
3308 SetDlgItemTextW(hDlg
, IDC_MODIFIED_LABEL
, szTemp
);
3312 LoadStringW(hInst
, IDS_NOT_AVAILABLE
, szTemp
, ARRAYSIZE(szTemp
));
3314 SetDlgItemTextW(hDlg
, IDC_SIZE_LABEL
, szTemp
);
3315 SetDlgItemTextW(hDlg
, IDC_CREATED_LABEL
, szTemp
);
3316 SetDlgItemTextW(hDlg
, IDC_MODIFIED_LABEL
, szTemp
);
3317 SetDlgItemTextW(hDlg
, IDC_ACCESSED_LABEL
, szTemp
);
3320 if (EventLog
->Permanent
)
3322 SendDlgItemMessageW(hDlg
, IDC_UPDOWN_MAXLOGSIZE
, UDM_SETRANGE32
, (WPARAM
)1, (LPARAM
)0x3FFFC0);
3323 SendDlgItemMessageW(hDlg
, IDC_UPDOWN_EVENTS_AGE
, UDM_SETRANGE
, 0, (LPARAM
)MAKELONG(365, 1));
3325 SetDlgItemInt(hDlg
, IDC_EDIT_MAXLOGSIZE
, dwMaxSize
, FALSE
);
3326 SetDlgItemInt(hDlg
, IDC_EDIT_EVENTS_AGE
, dwRetention
, FALSE
);
3328 if (dwRetention
== 0)
3330 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_OVERWRITE_AS_NEEDED
);
3331 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, FALSE
);
3332 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, FALSE
);
3334 else if (dwRetention
== INFINITE
)
3336 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_NO_OVERWRITE
);
3337 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, FALSE
);
3338 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, FALSE
);
3342 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_OVERWRITE_OLDER_THAN
);
3343 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, TRUE
);
3344 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, TRUE
);
3349 // TODO: Hide the unused controls! Or, just use another type of property sheet!
3353 /* Message handler for EventLog Properties dialog */
3355 EventLogPropProc(HWND hDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
3359 EventLog
= (PEVENTLOG
)GetWindowLongPtrW(hDlg
, DWLP_USER
);
3365 EventLog
= (PEVENTLOG
)((LPPROPSHEETPAGE
)lParam
)->lParam
;
3366 SetWindowLongPtrW(hDlg
, DWLP_USER
, (LONG_PTR
)EventLog
);
3368 InitPropertiesDlg(hDlg
, EventLog
);
3370 PropSheet_UnChanged(GetParent(hDlg
), hDlg
);
3371 return (INT_PTR
)TRUE
;
3375 return (INT_PTR
)TRUE
;
3378 switch (LOWORD(wParam
))
3382 EndDialog(hDlg
, LOWORD(wParam
));
3383 return (INT_PTR
)TRUE
;
3385 case IDC_OVERWRITE_AS_NEEDED
:
3387 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_OVERWRITE_AS_NEEDED
);
3388 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, FALSE
);
3389 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, FALSE
);
3393 case IDC_OVERWRITE_OLDER_THAN
:
3395 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_OVERWRITE_OLDER_THAN
);
3396 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, TRUE
);
3397 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, TRUE
);
3401 case IDC_NO_OVERWRITE
:
3403 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_NO_OVERWRITE
);
3404 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, FALSE
);
3405 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, FALSE
);
3411 L
"Help not implemented yet!",
3413 MB_OK
| MB_ICONINFORMATION
);
3414 return (INT_PTR
)TRUE
;
3422 return (INT_PTR
)FALSE
;
3426 EventLogProperties(HINSTANCE hInstance
, HWND hWndParent
, PEVENTLOGFILTER EventLogFilter
)
3429 PROPSHEETHEADERW psh
;
3430 PROPSHEETPAGEW psp
[1]; // 2
3433 * Bail out if there is no available filter, or if the filter
3434 * contains more than one log.
3436 if (!EventLogFilter
)
3439 EventLogFilter_AddRef(EventLogFilter
);
3441 if (EventLogFilter
->NumOfEventLogs
> 1 ||
3442 EventLogFilter
->EventLogs
[0] == NULL
)
3448 psh
.dwSize
= sizeof(psh
);
3449 psh
.dwFlags
= PSH_PROPSHEETPAGE
/*| PSH_USEICONID */ | PSH_PROPTITLE
| PSH_HASHELP
/*| PSH_NOCONTEXTHELP */ /*| PSH_USECALLBACK */;
3450 psh
.hInstance
= hInstance
;
3451 psh
.hwndParent
= hWndParent
;
3452 // psh.pszIcon = MAKEINTRESOURCEW(IDI_APPICON); // Disabled because it only sets the small icon; the big icon is a stretched version of the small one.
3453 psh
.pszCaption
= EventLogFilter
->EventLogs
[0]->LogName
;
3456 psh
.nPages
= ARRAYSIZE(psp
);
3457 // psh.pfnCallback = PropSheetCallback;
3459 /* Log properties page */
3460 psp
[0].dwSize
= sizeof(psp
[0]);
3461 psp
[0].dwFlags
= PSP_HASHELP
;
3462 psp
[0].hInstance
= hInstance
;
3463 psp
[0].pszTemplate
= MAKEINTRESOURCEW(IDD_LOGPROPERTIES_GENERAL
);
3464 psp
[0].pfnDlgProc
= EventLogPropProc
;
3465 psp
[0].lParam
= (LPARAM
)EventLogFilter
->EventLogs
[0];
3468 /* TODO: Log sources page */
3469 psp
[1].dwSize
= sizeof(psp
[1]);
3470 psp
[1].dwFlags
= PSP_HASHELP
;
3471 psp
[1].hInstance
= hInstance
;
3472 psp
[1].pszTemplate
= MAKEINTRESOURCEW(IDD_GENERAL_PAGE
);
3473 psp
[1].pfnDlgProc
= GeneralPageWndProc
;
3474 psp
[0].lParam
= (LPARAM
)EventLogFilter
->EventLogs
[0];
3477 /* Create the property sheet */
3478 ret
= PropertySheetW(&psh
);
3481 EventLogFilter_Release(EventLogFilter
);
3488 DisplayEvent(HWND hDlg
, PEVENTLOGFILTER EventLogFilter
)
3490 WCHAR szEventType
[MAX_PATH
];
3491 WCHAR szTime
[MAX_PATH
];
3492 WCHAR szDate
[MAX_PATH
];
3493 WCHAR szUser
[MAX_PATH
];
3494 WCHAR szComputer
[MAX_PATH
];
3495 WCHAR szSource
[MAX_PATH
];
3496 WCHAR szCategory
[MAX_PATH
];
3497 WCHAR szEventID
[MAX_PATH
];
3498 WCHAR szEventText
[EVENT_MESSAGE_EVENTTEXT_BUFFER
];
3499 BOOL bEventData
= FALSE
;
3501 PEVENTLOGRECORD pevlr
;
3504 /* Get index of selected item */
3505 iIndex
= ListView_GetNextItem(hwndListView
, -1, LVNI_SELECTED
| LVNI_FOCUSED
);
3509 L
"No Items in ListView",
3511 MB_OK
| MB_ICONINFORMATION
);
3515 li
.mask
= LVIF_PARAM
;
3519 ListView_GetItem(hwndListView
, &li
);
3521 pevlr
= (PEVENTLOGRECORD
)li
.lParam
;
3523 ListView_GetItemText(hwndListView
, iIndex
, 0, szEventType
, ARRAYSIZE(szEventType
));
3524 ListView_GetItemText(hwndListView
, iIndex
, 1, szDate
, ARRAYSIZE(szDate
));
3525 ListView_GetItemText(hwndListView
, iIndex
, 2, szTime
, ARRAYSIZE(szTime
));
3526 ListView_GetItemText(hwndListView
, iIndex
, 3, szSource
, ARRAYSIZE(szSource
));
3527 ListView_GetItemText(hwndListView
, iIndex
, 4, szCategory
, ARRAYSIZE(szCategory
));
3528 ListView_GetItemText(hwndListView
, iIndex
, 5, szEventID
, ARRAYSIZE(szEventID
));
3529 ListView_GetItemText(hwndListView
, iIndex
, 6, szUser
, ARRAYSIZE(szUser
));
3530 ListView_GetItemText(hwndListView
, iIndex
, 7, szComputer
, ARRAYSIZE(szComputer
));
3532 SetDlgItemTextW(hDlg
, IDC_EVENTDATESTATIC
, szDate
);
3533 SetDlgItemTextW(hDlg
, IDC_EVENTTIMESTATIC
, szTime
);
3534 SetDlgItemTextW(hDlg
, IDC_EVENTUSERSTATIC
, szUser
);
3535 SetDlgItemTextW(hDlg
, IDC_EVENTSOURCESTATIC
, szSource
);
3536 SetDlgItemTextW(hDlg
, IDC_EVENTCOMPUTERSTATIC
, szComputer
);
3537 SetDlgItemTextW(hDlg
, IDC_EVENTCATEGORYSTATIC
, szCategory
);
3538 SetDlgItemTextW(hDlg
, IDC_EVENTIDSTATIC
, szEventID
);
3539 SetDlgItemTextW(hDlg
, IDC_EVENTTYPESTATIC
, szEventType
);
3541 bEventData
= (pevlr
->DataLength
> 0);
3542 EnableDlgItem(hDlg
, IDC_BYTESRADIO
, bEventData
);
3543 EnableDlgItem(hDlg
, IDC_WORDRADIO
, bEventData
);
3545 // FIXME: At the moment we support only one event log in the filter
3546 GetEventMessage(EventLogFilter
->EventLogs
[0]->LogName
, szSource
, pevlr
, szEventText
);
3547 SetDlgItemTextW(hDlg
, IDC_EVENTTEXTEDIT
, szEventText
);
3551 PrintByteDataLine(PWCHAR pBuffer
, UINT uOffset
, PBYTE pData
, UINT uLength
)
3558 n
= swprintf(p
, L
"\r\n");
3563 n
= swprintf(p
, L
"%04lx:", uOffset
);
3567 for (i
= 0; i
< uLength
; i
++)
3569 n
= swprintf(p
, L
" %02x", pData
[i
]);
3574 for (i
= 0; i
< 9 - uLength
; i
++)
3576 n
= swprintf(p
, L
" ");
3581 for (i
= 0; i
< uLength
; i
++)
3583 // NOTE: Normally iswprint should return FALSE for tabs...
3584 n
= swprintf(p
, L
"%c", (iswprint(pData
[i
]) && (pData
[i
] != L
'\t')) ? pData
[i
] : L
'.');
3593 PrintWordDataLine(PWCHAR pBuffer
, UINT uOffset
, PULONG pData
, UINT uLength
)
3600 n
= swprintf(p
, L
"\r\n");
3605 n
= swprintf(p
, L
"%04lx:", uOffset
);
3609 for (i
= 0; i
< uLength
/ sizeof(ULONG
); i
++)
3611 n
= swprintf(p
, L
" %08lx", pData
[i
]);
3616 /* Display the remaining bytes if uLength was not a multiple of sizeof(ULONG) */
3617 for (i
= (uLength
/ sizeof(ULONG
)) * sizeof(ULONG
); i
< uLength
; i
++)
3619 n
= swprintf(p
, L
" %02x", ((PBYTE
)pData
)[i
]);
3629 DisplayEventData(HWND hDlg
, BOOL bDisplayWords
)
3632 PEVENTLOGRECORD pevlr
;
3637 UINT uBufferSize
, uLineLength
;
3638 PWCHAR pTextBuffer
, pLine
;
3640 /* Get index of selected item */
3641 iIndex
= ListView_GetNextItem(hwndListView
, -1, LVNI_SELECTED
| LVNI_FOCUSED
);
3645 L
"No Items in ListView",
3647 MB_OK
| MB_ICONINFORMATION
);
3651 li
.mask
= LVIF_PARAM
;
3655 ListView_GetItem(hwndListView
, &li
);
3657 pevlr
= (PEVENTLOGRECORD
)li
.lParam
;
3658 if (pevlr
->DataLength
== 0)
3660 SetDlgItemTextW(hDlg
, IDC_EVENTDATAEDIT
, L
"");
3665 uBufferSize
= ((pevlr
->DataLength
/ 8) + 1) * 26 * sizeof(WCHAR
);
3667 uBufferSize
= ((pevlr
->DataLength
/ 8) + 1) * 43 * sizeof(WCHAR
);
3669 pTextBuffer
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, uBufferSize
);
3673 pLine
= pTextBuffer
;
3676 for (i
= 0; i
< pevlr
->DataLength
/ 8; i
++)
3678 pData
= (LPBYTE
)((LPBYTE
)pevlr
+ pevlr
->DataOffset
+ uOffset
);
3681 uLineLength
= PrintWordDataLine(pLine
, uOffset
, (PULONG
)pData
, 8);
3683 uLineLength
= PrintByteDataLine(pLine
, uOffset
, pData
, 8);
3684 pLine
= pLine
+ uLineLength
;
3689 if (pevlr
->DataLength
% 8 != 0)
3691 pData
= (LPBYTE
)((LPBYTE
)pevlr
+ pevlr
->DataOffset
+ uOffset
);
3694 PrintWordDataLine(pLine
, uOffset
, (PULONG
)pData
, pevlr
->DataLength
% 8);
3696 PrintByteDataLine(pLine
, uOffset
, pData
, pevlr
->DataLength
% 8);
3699 SetDlgItemTextW(hDlg
, IDC_EVENTDATAEDIT
, pTextBuffer
);
3701 HeapFree(GetProcessHeap(), 0, pTextBuffer
);
3705 CreateMonospaceFont(VOID
)
3707 LOGFONTW tmpFont
= {0};
3713 tmpFont
.lfHeight
= -MulDiv(8, GetDeviceCaps(hDc
, LOGPIXELSY
), 72);
3714 tmpFont
.lfWeight
= FW_NORMAL
;
3715 wcscpy(tmpFont
.lfFaceName
, L
"Courier New");
3717 hFont
= CreateFontIndirectW(&tmpFont
);
3719 ReleaseDC(NULL
, hDc
);
3725 CopyEventEntry(HWND hWnd
)
3727 WCHAR output
[4130], tmpHeader
[512];
3728 WCHAR szEventType
[MAX_PATH
];
3729 WCHAR szSource
[MAX_PATH
];
3730 WCHAR szCategory
[MAX_PATH
];
3731 WCHAR szEventID
[MAX_PATH
];
3732 WCHAR szDate
[MAX_PATH
];
3733 WCHAR szTime
[MAX_PATH
];
3734 WCHAR szUser
[MAX_PATH
];
3735 WCHAR szComputer
[MAX_PATH
];
3736 WCHAR evtDesc
[ENTRY_SIZE
];
3739 if (!OpenClipboard(hWnd
))
3742 /* First, empty the clipboard before we begin to use it */
3745 /* Get the formatted text needed to place the content into */
3746 LoadStringW(hInst
, IDS_COPY
, tmpHeader
, ARRAYSIZE(tmpHeader
));
3748 /* Grab all the information and get it ready for the clipboard */
3749 GetDlgItemTextW(hWnd
, IDC_EVENTTYPESTATIC
, szEventType
, ARRAYSIZE(szEventType
));
3750 GetDlgItemTextW(hWnd
, IDC_EVENTSOURCESTATIC
, szSource
, ARRAYSIZE(szSource
));
3751 GetDlgItemTextW(hWnd
, IDC_EVENTCATEGORYSTATIC
, szCategory
, ARRAYSIZE(szCategory
));
3752 GetDlgItemTextW(hWnd
, IDC_EVENTIDSTATIC
, szEventID
, ARRAYSIZE(szEventID
));
3753 GetDlgItemTextW(hWnd
, IDC_EVENTDATESTATIC
, szDate
, ARRAYSIZE(szDate
));
3754 GetDlgItemTextW(hWnd
, IDC_EVENTTIMESTATIC
, szTime
, ARRAYSIZE(szTime
));
3755 GetDlgItemTextW(hWnd
, IDC_EVENTUSERSTATIC
, szUser
, ARRAYSIZE(szUser
));
3756 GetDlgItemTextW(hWnd
, IDC_EVENTCOMPUTERSTATIC
, szComputer
, ARRAYSIZE(szComputer
));
3757 GetDlgItemTextW(hWnd
, IDC_EVENTTEXTEDIT
, evtDesc
, ARRAYSIZE(evtDesc
));
3759 /* Consolidate the information into on big piece */
3760 wsprintfW(output
, tmpHeader
, szEventType
, szSource
, szCategory
, szEventID
, szDate
, szTime
, szUser
, szComputer
, evtDesc
);
3762 /* Sort out the memory needed to write to the clipboard */
3763 hMem
= GlobalAlloc(GMEM_MOVEABLE
, ENTRY_SIZE
);
3764 memcpy(GlobalLock(hMem
), output
, ENTRY_SIZE
);
3767 /* Write the final content to the clipboard */
3768 SetClipboardData(CF_UNICODETEXT
, hMem
);
3770 /* Close the clipboard once we're done with it */
3776 InitDetailsDlg(HWND hDlg
, PDETAILDATA pData
)
3780 HANDLE nextIcon
= LoadImageW(hInst
, MAKEINTRESOURCEW(IDI_NEXT
), IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
);
3781 HANDLE prevIcon
= LoadImageW(hInst
, MAKEINTRESOURCEW(IDI_PREV
), IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
);
3782 HANDLE copyIcon
= LoadImageW(hInst
, MAKEINTRESOURCEW(IDI_COPY
), IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
);
3784 SendDlgItemMessageW(hDlg
, IDC_NEXT
, BM_SETIMAGE
, (WPARAM
)IMAGE_ICON
, (LPARAM
)nextIcon
);
3785 SendDlgItemMessageW(hDlg
, IDC_PREVIOUS
, BM_SETIMAGE
, (WPARAM
)IMAGE_ICON
, (LPARAM
)prevIcon
);
3786 SendDlgItemMessageW(hDlg
, IDC_COPY
, BM_SETIMAGE
, (WPARAM
)IMAGE_ICON
, (LPARAM
)copyIcon
);
3788 /* Set the default read-only RichEdit color */
3789 SendDlgItemMessageW(hDlg
, IDC_EVENTTEXTEDIT
, EM_SETBKGNDCOLOR
, 0, GetSysColor(COLOR_3DFACE
));
3791 /* Enable RichEdit coloured and underlined links */
3792 dwMask
= SendDlgItemMessageW(hDlg
, IDC_EVENTTEXTEDIT
, EM_GETEVENTMASK
, 0, 0);
3793 SendDlgItemMessageW(hDlg
, IDC_EVENTTEXTEDIT
, EM_SETEVENTMASK
, 0, dwMask
| ENM_LINK
| ENM_MOUSEEVENTS
);
3796 * Activate automatic URL recognition by the RichEdit control. For more information, see:
3797 * https://blogs.msdn.microsoft.com/murrays/2009/08/31/automatic-richedit-hyperlinks/
3798 * https://blogs.msdn.microsoft.com/murrays/2009/09/24/richedit-friendly-name-hyperlinks/
3799 * https://msdn.microsoft.com/en-us/library/windows/desktop/bb787991(v=vs.85).aspx
3801 SendDlgItemMessageW(hDlg
, IDC_EVENTTEXTEDIT
, EM_AUTOURLDETECT
, AURL_ENABLEURL
/* | AURL_ENABLEEAURLS */, 0);
3803 /* Note that the RichEdit control never gets themed under WinXP+. One would have to write code to simulate Edit-control theming */
3805 SendDlgItemMessageW(hDlg
, pData
->bDisplayWords
? IDC_WORDRADIO
: IDC_BYTESRADIO
, BM_SETCHECK
, BST_CHECKED
, 0);
3806 SendDlgItemMessageW(hDlg
, IDC_EVENTDATAEDIT
, WM_SETFONT
, (WPARAM
)pData
->hMonospaceFont
, (LPARAM
)TRUE
);
3809 /* Message handler for Event Details box */
3811 EventDetails(HWND hDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
3815 pData
= (PDETAILDATA
)GetWindowLongPtrW(hDlg
, DWLP_USER
);
3820 pData
= (PDETAILDATA
)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*pData
));
3823 SetWindowLongPtrW(hDlg
, DWLP_USER
, (LONG_PTR
)pData
);
3825 pData
->EventLogFilter
= (PEVENTLOGFILTER
)lParam
;
3826 pData
->bDisplayWords
= FALSE
;
3827 pData
->hMonospaceFont
= CreateMonospaceFont();
3829 InitDetailsDlg(hDlg
, pData
);
3831 /* Show event info on dialog box */
3832 DisplayEvent(hDlg
, pData
->EventLogFilter
);
3833 DisplayEventData(hDlg
, pData
->bDisplayWords
);
3835 return (INT_PTR
)TRUE
;
3838 if (pData
->hMonospaceFont
)
3839 DeleteObject(pData
->hMonospaceFont
);
3840 HeapFree(GetProcessHeap(), 0, pData
);
3841 return (INT_PTR
)TRUE
;
3844 switch (LOWORD(wParam
))
3848 EndDialog(hDlg
, LOWORD(wParam
));
3849 return (INT_PTR
)TRUE
;
3852 SendMessageW(hwndListView
, WM_KEYDOWN
, VK_UP
, 0);
3854 /* Show event info on dialog box */
3857 DisplayEvent(hDlg
, pData
->EventLogFilter
);
3858 DisplayEventData(hDlg
, pData
->bDisplayWords
);
3860 return (INT_PTR
)TRUE
;
3863 SendMessageW(hwndListView
, WM_KEYDOWN
, VK_DOWN
, 0);
3865 /* Show event info on dialog box */
3868 DisplayEvent(hDlg
, pData
->EventLogFilter
);
3869 DisplayEventData(hDlg
, pData
->bDisplayWords
);
3871 return (INT_PTR
)TRUE
;
3874 CopyEventEntry(hDlg
);
3875 return (INT_PTR
)TRUE
;
3877 case IDC_BYTESRADIO
:
3880 pData
->bDisplayWords
= FALSE
;
3881 DisplayEventData(hDlg
, pData
->bDisplayWords
);
3883 return (INT_PTR
)TRUE
;
3888 pData
->bDisplayWords
= TRUE
;
3889 DisplayEventData(hDlg
, pData
->bDisplayWords
);
3891 return (INT_PTR
)TRUE
;
3895 L
"Help not implemented yet!",
3897 MB_OK
| MB_ICONINFORMATION
);
3898 return (INT_PTR
)TRUE
;
3906 switch (((LPNMHDR
)lParam
)->code
)
3909 // TODO: Act on the activated RichEdit link!
3915 return (INT_PTR
)FALSE
;