2 * ReactOS Win32 Applications
3 * Copyright (C) 2007 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 * PROJECT: ReactOS Event Log Viewer
21 * LICENSE: GPL - See COPYING in the top level directory
22 * FILE: base/applications/mscutils/eventvwr/eventvwr.c
23 * PURPOSE: Event Log Viewer main file
24 * PROGRAMMERS: Marc Piulachs (marc.piulachs at codexchange [dot] net)
26 * Hermes Belusca-Maito
30 #include "evtdetctl.h"
32 #include <sddl.h> // For ConvertSidToStringSidW
36 // #include "resource.h"
38 #define LVM_PROGRESS (WM_APP + 1) // Used by the subclassed ListView
40 static const LPCWSTR szWindowClass
= L
"EVENTVWR"; /* The main window class name */
41 static const WCHAR EVENTLOG_BASE_KEY
[] = L
"SYSTEM\\CurrentControlSet\\Services\\EventLog\\";
43 /* The 3 system logs that should always exist in the user's system */
44 static const LPCWSTR SystemLogs
[] =
51 /* MessageFile message buffer size */
52 #define EVENT_MESSAGE_EVENTTEXT_BUFFER 1024*10 // NOTE: Used by evtdetctl.c
53 #define EVENT_MESSAGE_FILE_BUFFER 1024*10
54 #define EVENT_DLL_SEPARATOR L";"
55 #define EVENT_CATEGORY_MESSAGE_FILE L"CategoryMessageFile"
56 #define EVENT_MESSAGE_FILE L"EventMessageFile"
57 #define EVENT_PARAMETER_MESSAGE_FILE L"ParameterMessageFile"
59 #define MAX_LOADSTRING 255
64 HINSTANCE hInst
; /* Current instance */
65 WCHAR szTitle
[MAX_LOADSTRING
]; /* The title bar text */
66 WCHAR szTitleTemplate
[MAX_LOADSTRING
]; /* The logged-on title bar text */
67 WCHAR szStatusBarTemplate
[MAX_LOADSTRING
]; /* The status bar text */
68 WCHAR szLoadingWait
[MAX_LOADSTRING
]; /* The "Loading, please wait..." text */
69 WCHAR szEmptyList
[MAX_LOADSTRING
]; /* The "There are no items to show in this view" text */
70 WCHAR szSaveFilter
[MAX_LOADSTRING
]; /* Filter Mask for the save Dialog */
72 INT nVSplitPos
; /* Vertical splitter (1) position */
73 INT nHSplitPos
; /* Horizontal splitter (2) position */
74 BYTE bSplit
= 0; /* Splitter state:
76 * 1: Vertical splitting;
77 * 2: Horizontal splitting.
80 HWND hwndMainWindow
; /* Main window */
81 HWND hwndTreeView
; /* TreeView control */
82 HWND hwndListView
; /* ListView control */ // NOTE: Used by evtdetctl.c
83 HWND hwndEventDetails
; /* Event details pane */
84 HWND hwndStatus
; /* Status bar */
85 HWND hwndStatusProgress
; /* Progress bar in the status bar */
86 HMENU hMainMenu
; /* The application's main menu */
88 HTREEITEM htiSystemLogs
= NULL
, htiAppLogs
= NULL
, htiUserLogs
= NULL
;
91 /* Global event records cache for the current active event log filter */
92 DWORD g_TotalRecords
= 0;
93 PEVENTLOGRECORD
*g_RecordPtrs
= NULL
;
95 /* Lists of event logs and event log filters */
96 LIST_ENTRY EventLogList
;
97 LIST_ENTRY EventLogFilterList
;
98 PEVENTLOGFILTER ActiveFilter
= NULL
;
99 BOOL NewestEventsFirst
= TRUE
;
101 HANDLE hEnumEventsThread
= NULL
;
102 HANDLE hStopEnumEvent
= NULL
;
105 * Setting EnumFilter to a valid pointer and raising the hStartEnumEvent event
106 * triggers the event-enumerator thread to perform a new enumeration.
108 PEVENTLOGFILTER EnumFilter
= NULL
;
109 HANDLE hStartStopEnumEvent
= NULL
; // End-of-application event
110 HANDLE hStartEnumEvent
= NULL
; // Command event
112 /* Default Open/Save-As dialog box */
116 /* Forward declarations of functions included in this code module */
119 StartStopEnumEventsThread(IN LPVOID lpParameter
);
121 VOID
BuildLogListAndFilterList(IN LPCWSTR lpComputerName
);
122 VOID
FreeLogList(VOID
);
123 VOID
FreeLogFilterList(VOID
);
125 ATOM
MyRegisterClass(HINSTANCE
);
126 BOOL
InitInstance(HINSTANCE
, int);
127 VOID
ExitInstance(HINSTANCE
);
128 LRESULT CALLBACK
WndProc(HWND
, UINT
, WPARAM
, LPARAM
);
129 INT_PTR
EventLogProperties(HINSTANCE
, HWND
, PEVENTLOGFILTER
);
130 INT_PTR CALLBACK
EventDetails(HWND
, UINT
, WPARAM
, LPARAM
);
134 wWinMain(HINSTANCE hInstance
,
135 HINSTANCE hPrevInstance
,
140 INITCOMMONCONTROLSEX iccx
;
145 UNREFERENCED_PARAMETER(hPrevInstance
);
146 UNREFERENCED_PARAMETER(lpCmdLine
);
148 /* Whenever any of the common controls are used in your app,
149 * you must call InitCommonControlsEx() to register the classes
150 * for those controls. */
151 iccx
.dwSize
= sizeof(iccx
);
152 iccx
.dwICC
= ICC_LISTVIEW_CLASSES
;
153 InitCommonControlsEx(&iccx
);
155 /* Load the RichEdit DLL to add support for RichEdit controls */
156 hRichEdit
= LoadLibraryW(L
"riched20.dll");
160 msg
.wParam
= (WPARAM
)-1;
162 /* Initialize global strings */
163 LoadStringW(hInstance
, IDS_APP_TITLE
, szTitle
, ARRAYSIZE(szTitle
));
164 LoadStringW(hInstance
, IDS_APP_TITLE_EX
, szTitleTemplate
, ARRAYSIZE(szTitleTemplate
));
165 LoadStringW(hInstance
, IDS_STATUS_MSG
, szStatusBarTemplate
, ARRAYSIZE(szStatusBarTemplate
));
166 LoadStringW(hInstance
, IDS_LOADING_WAIT
, szLoadingWait
, ARRAYSIZE(szLoadingWait
));
167 LoadStringW(hInstance
, IDS_NO_ITEMS
, szEmptyList
, ARRAYSIZE(szEmptyList
));
169 if (!MyRegisterClass(hInstance
))
172 /* Perform application initialization */
173 if (!InitInstance(hInstance
, nCmdShow
))
176 hAccelTable
= LoadAcceleratorsW(hInstance
, MAKEINTRESOURCEW(IDA_EVENTVWR
));
178 /* Create the Start/Stop enumerator thread */
179 // Manual-reset event
180 hStartStopEnumEvent
= CreateEventW(NULL
, TRUE
, FALSE
, NULL
);
181 if (!hStartStopEnumEvent
)
185 hStartEnumEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
186 if (!hStartEnumEvent
)
189 hThread
= CreateThread(NULL
, 0,
190 StartStopEnumEventsThread
,
195 /* Retrieve the available event logs on this computer and create filters for them */
196 InitializeListHead(&EventLogList
);
197 InitializeListHead(&EventLogFilterList
);
198 // TODO: Implement connection to remote computer...
199 // At the moment we only support the user local computer.
200 BuildLogListAndFilterList(NULL
);
202 // TODO: If the user wants to open an external event log with the Event Log Viewer
203 // (via the command line), it's here that the log should be opened.
205 /* Main message loop */
206 while (GetMessageW(&msg
, NULL
, 0, 0))
208 if (!TranslateAcceleratorW(hwndMainWindow
, hAccelTable
, &msg
))
210 TranslateMessage(&msg
);
211 DispatchMessageW(&msg
);
215 SetEvent(hStartStopEnumEvent
);
216 WaitForSingleObject(hThread
, INFINITE
);
217 CloseHandle(hThread
);
219 /* Free the filters list and the event logs list */
226 CloseHandle(hStartEnumEvent
);
227 if (hStartStopEnumEvent
)
228 CloseHandle(hStartStopEnumEvent
);
230 ExitInstance(hInstance
);
233 FreeLibrary(hRichEdit
);
235 return (int)msg
.wParam
;
239 /* GENERIC HELPER FUNCTIONS ***************************************************/
242 ShowLastWin32Error(VOID
)
245 LPWSTR lpMessageBuffer
;
247 dwError
= GetLastError();
248 if (dwError
== ERROR_SUCCESS
)
251 if (!FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER
|
252 FORMAT_MESSAGE_FROM_SYSTEM
|
253 FORMAT_MESSAGE_IGNORE_INSERTS
,
257 (LPWSTR
)&lpMessageBuffer
,
263 MessageBoxW(hwndMainWindow
, lpMessageBuffer
, szTitle
, MB_OK
| MB_ICONERROR
);
264 LocalFree(lpMessageBuffer
);
268 EventTimeToSystemTime(IN DWORD EventTime
,
269 OUT PSYSTEMTIME pSystemTime
)
271 SYSTEMTIME st1970
= { 1970, 1, 0, 1, 0, 0, 0, 0 };
279 uUCT
.ft
.dwHighDateTime
= 0;
280 uUCT
.ft
.dwLowDateTime
= EventTime
;
281 SystemTimeToFileTime(&st1970
, &u1970
.ft
);
282 uUCT
.ll
= uUCT
.ll
* 10000000 + u1970
.ll
;
283 FileTimeToLocalFileTime(&uUCT
.ft
, &ftLocal
);
284 FileTimeToSystemTime(&ftLocal
, pSystemTime
);
288 * This function takes in entry a path to a single DLL, in which
289 * the message string of ID dwMessageId has to be searched.
290 * The other parameters are similar to those of the FormatMessageW API.
293 GetMessageStringFromDll(
294 IN LPCWSTR lpMessageDll
,
295 IN DWORD dwFlags
, // If we always use the same flags, just remove this param...
296 IN DWORD dwMessageId
,
298 IN
va_list* Arguments OPTIONAL
)
302 LPWSTR lpMsgBuf
= NULL
;
304 hLibrary
= LoadLibraryExW(lpMessageDll
, NULL
,
305 /* LOAD_LIBRARY_AS_IMAGE_RESOURCE | */ LOAD_LIBRARY_AS_DATAFILE
);
306 if (hLibrary
== NULL
)
309 /* Sanitize dwFlags */
310 dwFlags
&= ~FORMAT_MESSAGE_FROM_STRING
;
311 dwFlags
|= FORMAT_MESSAGE_FROM_HMODULE
;
316 * Retrieve the message string without appending extra newlines.
317 * Wrap in SEH to protect from invalid string parameters.
321 dwLength
= FormatMessageW(dwFlags
,
322 /* FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
323 FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, */
331 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
336 * An exception occurred while calling FormatMessage, this is usually
337 * the sign that a parameter was invalid, either 'lpMsgBuf' was NULL
338 * but we did not pass the flag FORMAT_MESSAGE_ALLOCATE_BUFFER, or the
339 * array pointer 'Arguments' was NULL or did not contain enough elements,
340 * and we did not pass the flag FORMAT_MESSAGE_IGNORE_INSERTS, and the
341 * message string expected too many inserts.
342 * In this last case only, we can call again FormatMessage but ignore
343 * explicitely the inserts. The string that we will return to the user
344 * will not be pre-formatted.
346 if (((dwFlags
& FORMAT_MESSAGE_ALLOCATE_BUFFER
) || lpMsgBuf
) &&
347 !(dwFlags
& FORMAT_MESSAGE_IGNORE_INSERTS
))
349 /* Remove any possible harmful flags and always ignore inserts */
350 dwFlags
&= ~FORMAT_MESSAGE_ARGUMENT_ARRAY
;
351 dwFlags
|= FORMAT_MESSAGE_IGNORE_INSERTS
;
353 /* If this call also throws an exception, we are really dead */
354 dwLength
= FormatMessageW(dwFlags
,
360 NULL
/* Arguments */);
367 FreeLibrary(hLibrary
);
373 ASSERT(lpMsgBuf
== NULL
);
385 * This function takes in entry a comma-separated list of DLLs, in which
386 * the message string of ID dwMessageId has to be searched.
387 * The other parameters are similar to those of the FormatMessageW API.
390 GetMessageStringFromDllList(
391 IN LPCWSTR lpMessageDllList
,
392 IN DWORD dwFlags
, // If we always use the same flags, just remove this param...
393 IN DWORD dwMessageId
,
395 IN
va_list* Arguments OPTIONAL
)
397 BOOL Success
= FALSE
;
399 LPWSTR szMessageDllList
;
401 LPWSTR lpMsgBuf
= NULL
;
403 /* Allocate a local buffer for the DLL list that can be tokenized */
404 // TODO: Optimize that!! Maybe we can cleverly use lpMessageDllList in read/write mode
405 // and cleverly temporarily replace the ';' by UNICODE_NULL, do our job, then reverse the change.
406 cbLength
= (wcslen(lpMessageDllList
) + 1) * sizeof(WCHAR
);
407 szMessageDllList
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, cbLength
);
408 if (!szMessageDllList
)
410 RtlCopyMemory(szMessageDllList
, lpMessageDllList
, cbLength
);
412 /* Loop through the list of message DLLs */
413 szDll
= wcstok(szMessageDllList
, EVENT_DLL_SEPARATOR
);
414 while ((szDll
!= NULL
) && !Success
)
416 // Uses LANG_USER_DEFAULT
417 lpMsgBuf
= GetMessageStringFromDll(szDll
,
424 /* The ID was found and the message was formatted */
430 * The DLL could not be loaded, or the message could not be found,
431 * try the next DLL, if any.
433 szDll
= wcstok(NULL
, EVENT_DLL_SEPARATOR
);
436 HeapFree(GetProcessHeap(), 0, szMessageDllList
);
444 LPWSTR pStartingAddress
; // Pointer to the beginning of a parameter string in pMessage
445 LPWSTR pEndingAddress
; // Pointer to the end of a parameter string in pMessage
446 DWORD pParameterID
; // Parameter identifier found in pMessage
447 LPWSTR pParameter
; // Actual parameter string
448 } param_strings_format_data
;
451 ApplyParameterStringsToMessage(
452 IN LPCWSTR lpMessageDllList
,
453 IN BOOL bMessagePreFormatted
,
454 IN CONST LPCWSTR pMessage
,
455 OUT LPWSTR
* pFinalMessage
)
458 * This code is heavily adapted from the MSDN example:
459 * https://msdn.microsoft.com/en-us/library/windows/desktop/bb427356.aspx
463 DWORD Status
= ERROR_SUCCESS
;
464 DWORD dwParamCount
= 0; // Number of insertion strings found in pMessage
465 size_t cchBuffer
= 0; // Size of the buffer in characters
466 size_t cchParams
= 0; // Number of characters in all the parameter strings
469 param_strings_format_data
* pParamData
= NULL
; // Array of pointers holding information about each parameter string in pMessage
470 LPWSTR pTempMessage
= (LPWSTR
)pMessage
;
471 LPWSTR pTempFinalMessage
= NULL
;
473 *pFinalMessage
= NULL
;
475 /* Determine the number of parameter insertion strings in pMessage */
476 if (bMessagePreFormatted
)
478 while ((pTempMessage
= wcschr(pTempMessage
, L
'%')))
481 if (isdigit(*pTempMessage
))
484 while (isdigit(*++pTempMessage
)) ;
490 while ((pTempMessage
= wcsstr(pTempMessage
, L
"%%")))
493 if (isdigit(*pTempMessage
))
496 while (isdigit(*++pTempMessage
)) ;
501 /* If there are no parameter insertion strings in pMessage, just return */
502 if (dwParamCount
== 0)
504 // *pFinalMessage = NULL;
508 /* Allocate the array of parameter string format data */
509 pParamData
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, dwParamCount
* sizeof(param_strings_format_data
));
512 Status
= ERROR_OUTOFMEMORY
;
517 * Retrieve each parameter in pMessage and the beginning and end of the
518 * insertion string, as well as the message identifier of the parameter.
520 pTempMessage
= (LPWSTR
)pMessage
;
521 if (bMessagePreFormatted
)
523 while ((pTempMessage
= wcschr(pTempMessage
, L
'%')) && (i
< dwParamCount
))
526 if (isdigit(*pTempMessage
))
528 pParamData
[i
].pStartingAddress
= pTempMessage
-1;
529 pParamData
[i
].pParameterID
= (DWORD
)_wtol(pTempMessage
);
531 while (isdigit(*++pTempMessage
)) ;
533 pParamData
[i
].pEndingAddress
= pTempMessage
;
540 while ((pTempMessage
= wcsstr(pTempMessage
, L
"%%")) && (i
< dwParamCount
))
543 if (isdigit(*pTempMessage
))
545 pParamData
[i
].pStartingAddress
= pTempMessage
-2;
546 pParamData
[i
].pParameterID
= (DWORD
)_wtol(pTempMessage
);
548 while (isdigit(*++pTempMessage
)) ;
550 pParamData
[i
].pEndingAddress
= pTempMessage
;
556 /* Retrieve each parameter string */
557 for (i
= 0; i
< dwParamCount
; i
++)
559 // pParamData[i].pParameter = GetMessageString(pParamData[i].pParameterID, 0, NULL);
560 pParamData
[i
].pParameter
=
561 GetMessageStringFromDllList(lpMessageDllList
,
562 FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_HMODULE
|
563 FORMAT_MESSAGE_IGNORE_INSERTS
| FORMAT_MESSAGE_MAX_WIDTH_MASK
,
564 pParamData
[i
].pParameterID
,
566 if (!pParamData
[i
].pParameter
)
568 /* Skip the insertion string */
572 cchParams
+= wcslen(pParamData
[i
].pParameter
);
576 * Allocate the final message buffer, the size of which is based on the
577 * length of the original message and the length of each parameter string.
579 cchBuffer
= wcslen(pMessage
) + cchParams
+ 1;
580 *pFinalMessage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, cchBuffer
* sizeof(WCHAR
));
583 Status
= ERROR_OUTOFMEMORY
;
587 pTempFinalMessage
= *pFinalMessage
;
589 /* Build the final message string */
590 pTempMessage
= (LPWSTR
)pMessage
;
591 for (i
= 0; i
< dwParamCount
; i
++)
593 /* Append the segment from pMessage */
594 cch
= pParamData
[i
].pStartingAddress
- pTempMessage
;
595 StringCchCopyNW(pTempFinalMessage
, cchBuffer
, pTempMessage
, cch
);
596 pTempMessage
= pParamData
[i
].pEndingAddress
;
598 pTempFinalMessage
+= cch
;
600 /* Append the parameter string */
601 if (pParamData
[i
].pParameter
)
603 StringCchCopyW(pTempFinalMessage
, cchBuffer
, pParamData
[i
].pParameter
);
604 cch
= wcslen(pParamData
[i
].pParameter
); // pTempFinalMessage
609 * We failed to retrieve the parameter string before, so just
610 * place back the original string placeholder.
612 cch
= pParamData
[i
].pEndingAddress
/* == pTempMessage */ - pParamData
[i
].pStartingAddress
;
613 StringCchCopyNW(pTempFinalMessage
, cchBuffer
, pParamData
[i
].pStartingAddress
, cch
);
614 // cch = wcslen(pTempFinalMessage);
617 pTempFinalMessage
+= cch
;
620 /* Append the last segment from pMessage */
621 StringCchCopyW(pTempFinalMessage
, cchBuffer
, pTempMessage
);
625 // if (Status != ERROR_SUCCESS)
626 // *pFinalMessage = NULL;
630 for (i
= 0; i
< dwParamCount
; i
++)
632 if (pParamData
[i
].pParameter
)
633 LocalFree(pParamData
[i
].pParameter
);
636 HeapFree(GetProcessHeap(), 0, pParamData
);
644 * The following functions were adapted from
645 * shell32!dialogs/filedefext.cpp:``SH_...'' functions.
649 FormatInteger(LONGLONG Num
, LPWSTR pwszResult
, UINT cchResultMax
)
652 WCHAR wszDecimalSep
[8], wszThousandSep
[8];
654 WCHAR wszGrouping
[12];
659 // Print the number in uniform mode
660 swprintf(wszNumber
, L
"%I64u", Num
);
662 // Get system strings for decimal and thousand separators.
663 GetLocaleInfoW(LOCALE_USER_DEFAULT
, LOCALE_SDECIMAL
, wszDecimalSep
, _countof(wszDecimalSep
));
664 GetLocaleInfoW(LOCALE_USER_DEFAULT
, LOCALE_STHOUSAND
, wszThousandSep
, _countof(wszThousandSep
));
666 // Initialize format for printing the number in bytes
667 ZeroMemory(&nf
, sizeof(nf
));
668 nf
.lpDecimalSep
= wszDecimalSep
;
669 nf
.lpThousandSep
= wszThousandSep
;
671 // Get system string for groups separator
672 cchGrouping
= GetLocaleInfoW(LOCALE_USER_DEFAULT
,
675 _countof(wszGrouping
));
677 // Convert grouping specs from string to integer
678 for (i
= 0; i
< cchGrouping
; i
++)
680 WCHAR wch
= wszGrouping
[i
];
682 if (wch
>= L
'0' && wch
<= L
'9')
683 nf
.Grouping
= nf
.Grouping
* 10 + (wch
- L
'0');
684 else if (wch
!= L
';')
688 if ((nf
.Grouping
% 10) == 0)
694 cchResult
= GetNumberFormatW(LOCALE_USER_DEFAULT
,
704 // GetNumberFormatW returns number of characters including UNICODE_NULL
705 return cchResult
- 1;
709 FormatByteSize(LONGLONG cbSize
, LPWSTR pwszResult
, UINT cchResultMax
)
715 /* Write formated bytes count */
716 cchWritten
= FormatInteger(cbSize
, pwszResult
, cchResultMax
);
720 /* Copy " bytes" to buffer */
721 pwszEnd
= pwszResult
+ cchWritten
;
722 cchRemaining
= cchResultMax
- cchWritten
;
723 StringCchCopyExW(pwszEnd
, cchRemaining
, L
" ", &pwszEnd
, &cchRemaining
, 0);
724 cchWritten
= LoadStringW(hInst
, IDS_BYTES_FORMAT
, pwszEnd
, cchRemaining
);
725 cchRemaining
-= cchWritten
;
727 return cchResultMax
- cchRemaining
;
731 FormatFileSizeWithBytes(const PULARGE_INTEGER lpQwSize
, LPWSTR pwszResult
, UINT cchResultMax
)
737 /* Format bytes in KBs, MBs etc */
738 if (StrFormatByteSizeW(lpQwSize
->QuadPart
, pwszResult
, cchResultMax
) == NULL
)
741 /* If there is less bytes than 1KB, we have nothing to do */
742 if (lpQwSize
->QuadPart
< 1024)
745 /* Concatenate " (" */
746 cchWritten
= wcslen(pwszResult
);
747 pwszEnd
= pwszResult
+ cchWritten
;
748 cchRemaining
= cchResultMax
- cchWritten
;
749 StringCchCopyExW(pwszEnd
, cchRemaining
, L
" (", &pwszEnd
, &cchRemaining
, 0);
751 /* Write formated bytes count */
752 cchWritten
= FormatByteSize(lpQwSize
->QuadPart
, pwszEnd
, cchRemaining
);
753 pwszEnd
+= cchWritten
;
754 cchRemaining
-= cchWritten
;
756 /* Copy ")" to the buffer */
757 StringCchCopyW(pwszEnd
, cchRemaining
, L
")");
762 /* Adapted from shell32!dialogs/filedefext.cpp:``CFileDefExt::GetFileTimeString'' */
764 GetFileTimeString(LPFILETIME lpFileTime
, LPWSTR pwszResult
, UINT cchResult
)
769 size_t cchRemaining
= cchResult
;
770 LPWSTR pwszEnd
= pwszResult
;
772 if (!FileTimeToLocalFileTime(lpFileTime
, &ft
) || !FileTimeToSystemTime(&ft
, &st
))
775 cchWritten
= GetDateFormatW(LOCALE_USER_DEFAULT
, DATE_LONGDATE
, &st
, NULL
, pwszEnd
, cchRemaining
);
777 --cchWritten
; // GetDateFormatW returns count with terminating zero
779 // ERR("GetDateFormatW failed\n");
781 cchRemaining
-= cchWritten
;
782 pwszEnd
+= cchWritten
;
784 StringCchCopyExW(pwszEnd
, cchRemaining
, L
", ", &pwszEnd
, &cchRemaining
, 0);
786 cchWritten
= GetTimeFormatW(LOCALE_USER_DEFAULT
, 0, &st
, NULL
, pwszEnd
, cchRemaining
);
788 --cchWritten
; // GetTimeFormatW returns count with terminating zero
790 // ERR("GetTimeFormatW failed\n");
797 TreeViewAddItem(IN HWND hTreeView
,
798 IN HTREEITEM hParent
,
801 IN INT SelectedImage
,
804 TV_INSERTSTRUCTW Insert
;
806 ZeroMemory(&Insert
, sizeof(Insert
));
808 Insert
.item
.mask
= TVIF_TEXT
| TVIF_PARAM
| TVIF_IMAGE
| TVIF_SELECTEDIMAGE
;
809 Insert
.hInsertAfter
= TVI_LAST
;
810 Insert
.hParent
= hParent
;
811 Insert
.item
.pszText
= lpText
;
812 Insert
.item
.iImage
= Image
;
813 Insert
.item
.iSelectedImage
= SelectedImage
;
814 Insert
.item
.lParam
= lParam
;
816 Insert
.item
.mask
|= TVIF_STATE
;
817 Insert
.item
.stateMask
= TVIS_OVERLAYMASK
;
818 Insert
.item
.state
= INDEXTOOVERLAYMASK(1);
820 return TreeView_InsertItem(hTreeView
, &Insert
);
824 /* LOG HELPER FUNCTIONS *******************************************************/
827 AllocEventLog(IN PCWSTR ComputerName OPTIONAL
,
834 /* Allocate a new event log entry */
835 EventLog
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*EventLog
));
839 /* Allocate the computer name string (optional) and copy it */
842 cchName
= wcslen(ComputerName
) + 1;
843 EventLog
->ComputerName
= HeapAlloc(GetProcessHeap(), 0, cchName
* sizeof(WCHAR
));
844 if (EventLog
->ComputerName
)
845 StringCchCopyW(EventLog
->ComputerName
, cchName
, ComputerName
);
848 /* Allocate the event log name string and copy it */
849 cchName
= wcslen(LogName
) + 1;
850 EventLog
->LogName
= HeapAlloc(GetProcessHeap(), 0, cchName
* sizeof(WCHAR
));
851 if (!EventLog
->LogName
)
853 HeapFree(GetProcessHeap(), 0, EventLog
);
856 StringCchCopyW(EventLog
->LogName
, cchName
, LogName
);
858 EventLog
->Permanent
= Permanent
;
864 EventLog_Free(IN PEVENTLOG EventLog
)
866 if (EventLog
->LogName
)
867 HeapFree(GetProcessHeap(), 0, EventLog
->LogName
);
869 if (EventLog
->FileName
)
870 HeapFree(GetProcessHeap(), 0, EventLog
->FileName
);
872 HeapFree(GetProcessHeap(), 0, EventLog
);
877 AllocAndCopyMultiStr(IN PCWSTR MultiStr OPTIONAL
)
885 pStr
= (PWSTR
)MultiStr
;
886 while (*pStr
) pStr
+= (wcslen(pStr
) + 1);
887 Length
= MultiStr
- pStr
+ 2;
889 pStr
= HeapAlloc(GetProcessHeap(), 0, Length
* sizeof(WCHAR
));
890 // NOTE: If we failed allocating the string, then fall back into no filter!
892 RtlCopyMemory(pStr
, MultiStr
, Length
* sizeof(WCHAR
));
898 AllocEventLogFilter(// IN PCWSTR FilterName,
902 IN BOOL AuditSuccess
,
903 IN BOOL AuditFailure
,
904 IN PCWSTR Sources OPTIONAL
,
905 IN PCWSTR Users OPTIONAL
,
906 IN PCWSTR ComputerNames OPTIONAL
,
907 IN ULONG NumOfEventLogs
,
908 IN PEVENTLOG
* EventLogs
)
910 PEVENTLOGFILTER EventLogFilter
;
912 /* Allocate a new event log filter entry, big enough to accommodate the list of logs */
913 EventLogFilter
= HeapAlloc(GetProcessHeap(),
915 FIELD_OFFSET(EVENTLOGFILTER
, EventLogs
[NumOfEventLogs
]));
919 EventLogFilter
->Information
= Information
;
920 EventLogFilter
->Warning
= Warning
;
921 EventLogFilter
->Error
= Error
;
922 EventLogFilter
->AuditSuccess
= AuditSuccess
;
923 EventLogFilter
->AuditFailure
= AuditFailure
;
925 /* Allocate and copy the sources, users, and computers multi-strings */
926 EventLogFilter
->Sources
= AllocAndCopyMultiStr(Sources
);
927 EventLogFilter
->Users
= AllocAndCopyMultiStr(Users
);
928 EventLogFilter
->ComputerNames
= AllocAndCopyMultiStr(ComputerNames
);
930 /* Copy the list of event logs */
931 EventLogFilter
->NumOfEventLogs
= NumOfEventLogs
;
932 RtlCopyMemory(EventLogFilter
->EventLogs
, EventLogs
, NumOfEventLogs
* sizeof(PEVENTLOG
));
934 /* Initialize the filter reference count */
935 EventLogFilter
->ReferenceCount
= 1;
937 return EventLogFilter
;
941 EventLogFilter_Free(IN PEVENTLOGFILTER EventLogFilter
)
943 if (EventLogFilter
->Sources
)
944 HeapFree(GetProcessHeap(), 0, EventLogFilter
->Sources
);
946 if (EventLogFilter
->Users
)
947 HeapFree(GetProcessHeap(), 0, EventLogFilter
->Users
);
949 if (EventLogFilter
->ComputerNames
)
950 HeapFree(GetProcessHeap(), 0, EventLogFilter
->ComputerNames
);
952 HeapFree(GetProcessHeap(), 0, EventLogFilter
);
955 LONG
EventLogFilter_AddRef(IN PEVENTLOGFILTER EventLogFilter
)
957 ASSERT(EventLogFilter
);
958 return InterlockedIncrement(&EventLogFilter
->ReferenceCount
);
961 LONG
EventLogFilter_Release(IN PEVENTLOGFILTER EventLogFilter
)
965 ASSERT(EventLogFilter
);
967 /* When the reference count reaches zero, delete the filter */
968 RefCount
= InterlockedDecrement(&EventLogFilter
->ReferenceCount
);
971 /* Remove the filter from the list */
972 /** RemoveEntryList(&EventLogFilter->ListEntry); **/
973 EventLogFilter_Free(EventLogFilter
);
986 c
= s
+ wcslen(s
) - 1;
987 while (c
>= s
&& iswspace(*c
))
994 GetEventMessageFileDLL(IN LPCWSTR lpLogName
,
995 IN LPCWSTR SourceName
,
996 IN LPCWSTR EntryName
,
997 OUT PWCHAR lpModuleName
) // TODO: Add IN DWORD BufLen
999 BOOL Success
= FALSE
;
1002 WCHAR szModuleName
[MAX_PATH
];
1003 WCHAR szKeyName
[MAX_PATH
];
1004 HKEY hLogKey
= NULL
;
1005 HKEY hSourceKey
= NULL
;
1007 StringCbCopyW(szKeyName
, sizeof(szKeyName
), EVENTLOG_BASE_KEY
);
1008 StringCbCatW(szKeyName
, sizeof(szKeyName
), lpLogName
);
1010 Result
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
,
1015 if (Result
!= ERROR_SUCCESS
)
1018 Result
= RegOpenKeyExW(hLogKey
,
1023 if (Result
!= ERROR_SUCCESS
)
1025 RegCloseKey(hLogKey
);
1029 dwSize
= sizeof(szModuleName
);
1030 Result
= RegQueryValueExW(hSourceKey
,
1034 (LPBYTE
)szModuleName
,
1036 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_EXPAND_SZ
&& Type
!= REG_SZ
))
1038 szModuleName
[0] = UNICODE_NULL
;
1042 /* NULL-terminate the string and expand it */
1043 szModuleName
[dwSize
/ sizeof(WCHAR
) - 1] = UNICODE_NULL
;
1044 ExpandEnvironmentStringsW(szModuleName
, lpModuleName
, ARRAYSIZE(szModuleName
));
1048 RegCloseKey(hSourceKey
);
1049 RegCloseKey(hLogKey
);
1055 GetEventCategory(IN LPCWSTR KeyName
,
1056 IN LPCWSTR SourceName
,
1057 IN PEVENTLOGRECORD pevlr
,
1058 OUT PWCHAR CategoryName
) // TODO: Add IN DWORD BufLen
1060 BOOL Success
= FALSE
;
1061 WCHAR szMessageDLL
[MAX_PATH
];
1062 LPWSTR lpMsgBuf
= NULL
;
1064 if (!GetEventMessageFileDLL(KeyName
, SourceName
, EVENT_CATEGORY_MESSAGE_FILE
, szMessageDLL
))
1067 /* Retrieve the message string without appending extra newlines */
1069 GetMessageStringFromDllList(szMessageDLL
,
1070 FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_HMODULE
|
1071 FORMAT_MESSAGE_IGNORE_INSERTS
| FORMAT_MESSAGE_MAX_WIDTH_MASK
,
1072 pevlr
->EventCategory
,
1073 EVENT_MESSAGE_FILE_BUFFER
,
1077 /* Trim the string */
1078 TrimNulls(lpMsgBuf
);
1080 /* Copy the category name */
1081 StringCchCopyW(CategoryName
, MAX_PATH
, lpMsgBuf
);
1083 /* Free the buffer allocated by FormatMessage */
1084 LocalFree(lpMsgBuf
);
1086 /* The ID was found and the message was formatted */
1093 if (pevlr
->EventCategory
!= 0)
1094 StringCchPrintfW(CategoryName
, MAX_PATH
, L
"(%lu)", pevlr
->EventCategory
);
1096 LoadStringW(hInst
, IDS_NONE
, CategoryName
, MAX_PATH
);
1103 BOOL
// NOTE: Used by evtdetctl.c
1104 GetEventMessage(IN LPCWSTR KeyName
,
1105 IN LPCWSTR SourceName
,
1106 IN PEVENTLOGRECORD pevlr
,
1107 OUT PWCHAR EventText
) // TODO: Add IN DWORD BufLen
1109 BOOL Success
= FALSE
;
1112 WCHAR SourceModuleName
[1024];
1113 WCHAR ParameterModuleName
[1024];
1114 BOOL IsParamModNameCached
= FALSE
;
1115 LPWSTR lpMsgBuf
= NULL
;
1116 LPWSTR szStringArray
, szMessage
;
1117 LPWSTR
*szArguments
;
1119 /* Get the event string array */
1120 szStringArray
= (LPWSTR
)((LPBYTE
)pevlr
+ pevlr
->StringOffset
);
1122 /* NOTE: GetEventMessageFileDLL can return a comma-separated list of DLLs */
1123 if (!GetEventMessageFileDLL(KeyName
, SourceName
, EVENT_MESSAGE_FILE
, SourceModuleName
))
1126 /* Allocate space for insertion strings */
1127 szArguments
= HeapAlloc(GetProcessHeap(), 0, pevlr
->NumStrings
* sizeof(LPVOID
));
1131 if (!IsParamModNameCached
)
1133 /* Now that the parameter file list is loaded, no need to reload it at the next run! */
1134 IsParamModNameCached
= GetEventMessageFileDLL(KeyName
, SourceName
, EVENT_PARAMETER_MESSAGE_FILE
, ParameterModuleName
);
1135 // FIXME: If the string loading failed the first time, no need to retry it just after???
1138 if (IsParamModNameCached
)
1140 /* Not yet support for reading messages from parameter message DLL */
1143 szMessage
= szStringArray
;
1146 * We do some hackish preformatting of the cached event strings...
1147 * That's because after we pass the string to FormatMessage
1148 * (via GetMessageStringFromDllList) with the FORMAT_MESSAGE_ARGUMENT_ARRAY
1149 * flag, instead of ignoring the insertion parameters and do the formatting
1150 * by ourselves. Therefore, the resulting string should have the parameter
1151 * string placeholders starting with a single '%' instead of a mix of one
1154 /* HACK part 1: Compute the full length of the string array */
1156 for (i
= 0; i
< pevlr
->NumStrings
; i
++)
1158 szMessage
+= wcslen(szMessage
) + 1;
1160 cch
= szMessage
- szStringArray
;
1162 /* HACK part 2: Now do the HACK proper! */
1163 szMessage
= szStringArray
;
1164 for (i
= 0; i
< pevlr
->NumStrings
; i
++)
1166 lpMsgBuf
= szMessage
;
1167 while ((lpMsgBuf
= wcsstr(lpMsgBuf
, L
"%%")))
1169 if (isdigit(lpMsgBuf
[2]))
1171 RtlMoveMemory(lpMsgBuf
, lpMsgBuf
+1, ((szStringArray
+ cch
) - lpMsgBuf
- 1) * sizeof(WCHAR
));
1175 szArguments
[i
] = szMessage
;
1176 szMessage
+= wcslen(szMessage
) + 1;
1179 /* Retrieve the message string without appending extra newlines */
1181 GetMessageStringFromDllList(SourceModuleName
,
1182 FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_HMODULE
|
1183 FORMAT_MESSAGE_ARGUMENT_ARRAY
| FORMAT_MESSAGE_MAX_WIDTH_MASK
,
1186 (va_list*)szArguments
);
1189 /* Trim the string */
1190 TrimNulls(lpMsgBuf
);
1193 Success
= (ApplyParameterStringsToMessage(ParameterModuleName
,
1196 &szMessage
) == ERROR_SUCCESS
);
1197 if (Success
&& szMessage
)
1199 /* Free the buffer allocated by FormatMessage */
1200 LocalFree(lpMsgBuf
);
1201 lpMsgBuf
= szMessage
;
1204 /* Copy the event text */
1205 StringCchCopyW(EventText
, EVENT_MESSAGE_EVENTTEXT_BUFFER
, lpMsgBuf
);
1207 /* Free the buffer allocated by FormatMessage */
1208 LocalFree(lpMsgBuf
);
1211 HeapFree(GetProcessHeap(), 0, szArguments
);
1216 /* Get a read-only pointer to the "event-not-found" string */
1217 lpMsgBuf
= HeapAlloc(GetProcessHeap(), 0, EVENT_MESSAGE_EVENTTEXT_BUFFER
* sizeof(WCHAR
));
1218 LoadStringW(hInst
, IDS_EVENTSTRINGIDNOTFOUND
, lpMsgBuf
, EVENT_MESSAGE_EVENTTEXT_BUFFER
);
1219 StringCchPrintfW(EventText
, EVENT_MESSAGE_EVENTTEXT_BUFFER
, lpMsgBuf
, (pevlr
->EventID
& 0xFFFF), SourceName
);
1221 /* Append the strings */
1222 szMessage
= szStringArray
;
1223 for (i
= 0; i
< pevlr
->NumStrings
; i
++)
1225 StringCchCatW(EventText
, EVENT_MESSAGE_EVENTTEXT_BUFFER
, szMessage
);
1226 StringCchCatW(EventText
, EVENT_MESSAGE_EVENTTEXT_BUFFER
, L
"\n");
1227 szMessage
+= wcslen(szMessage
) + 1;
1235 GetEventType(IN WORD dwEventType
,
1236 OUT PWCHAR eventTypeText
) // TODO: Add IN DWORD BufLen
1238 switch (dwEventType
)
1240 case EVENTLOG_ERROR_TYPE
:
1241 LoadStringW(hInst
, IDS_EVENTLOG_ERROR_TYPE
, eventTypeText
, MAX_LOADSTRING
);
1243 case EVENTLOG_WARNING_TYPE
:
1244 LoadStringW(hInst
, IDS_EVENTLOG_WARNING_TYPE
, eventTypeText
, MAX_LOADSTRING
);
1246 case EVENTLOG_INFORMATION_TYPE
:
1247 LoadStringW(hInst
, IDS_EVENTLOG_INFORMATION_TYPE
, eventTypeText
, MAX_LOADSTRING
);
1249 case EVENTLOG_SUCCESS
:
1250 LoadStringW(hInst
, IDS_EVENTLOG_SUCCESS
, eventTypeText
, MAX_LOADSTRING
);
1252 case EVENTLOG_AUDIT_SUCCESS
:
1253 LoadStringW(hInst
, IDS_EVENTLOG_AUDIT_SUCCESS
, eventTypeText
, MAX_LOADSTRING
);
1255 case EVENTLOG_AUDIT_FAILURE
:
1256 LoadStringW(hInst
, IDS_EVENTLOG_AUDIT_FAILURE
, eventTypeText
, MAX_LOADSTRING
);
1259 LoadStringW(hInst
, IDS_EVENTLOG_UNKNOWN_TYPE
, eventTypeText
, MAX_LOADSTRING
);
1265 GetEventUserName(IN PEVENTLOGRECORD pelr
,
1266 OUT PWCHAR pszUser
) // TODO: Add IN DWORD BufLen
1271 WCHAR szDomain
[1024];
1273 DWORD cchName
= ARRAYSIZE(szName
);
1274 DWORD cchDomain
= ARRAYSIZE(szDomain
);
1276 /* Point to the SID */
1277 pSid
= (PSID
)((LPBYTE
)pelr
+ pelr
->UserSidOffset
);
1280 if (pelr
->UserSidLength
> 0)
1283 * Try to retrieve the user account name and domain name corresponding
1284 * to the SID. If it cannot be retrieved, try to convert the SID to a
1285 * string-form. It should not be bigger than the user-provided buffer
1286 * 'pszUser', otherwise we return an error.
1288 if (LookupAccountSidW(NULL
, // FIXME: Use computer name? From the particular event?
1296 StringCchCopyW(pszUser
, MAX_PATH
, szName
);
1299 else if (ConvertSidToStringSidW(pSid
, &StringSid
))
1303 /* Copy the string only if the user-provided buffer is small enough */
1304 if (wcslen(StringSid
) + 1 <= MAX_PATH
) // + 1 for NULL-terminator
1306 StringCchCopyW(pszUser
, MAX_PATH
, StringSid
);
1311 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1315 /* Free the allocated buffer */
1316 LocalFree(StringSid
);
1326 static VOID
FreeRecords(VOID
)
1333 for (iIndex
= 0; iIndex
< g_TotalRecords
; iIndex
++)
1335 if (g_RecordPtrs
[iIndex
])
1336 HeapFree(GetProcessHeap(), 0, g_RecordPtrs
[iIndex
]);
1338 HeapFree(GetProcessHeap(), 0, g_RecordPtrs
);
1339 g_RecordPtrs
= NULL
;
1344 FilterByType(IN PEVENTLOGFILTER EventLogFilter
,
1345 IN PEVENTLOGRECORD pevlr
)
1347 if ((pevlr
->EventType
== EVENTLOG_SUCCESS
&& !EventLogFilter
->Information
) ||
1348 (pevlr
->EventType
== EVENTLOG_INFORMATION_TYPE
&& !EventLogFilter
->Information
) ||
1349 (pevlr
->EventType
== EVENTLOG_WARNING_TYPE
&& !EventLogFilter
->Warning
) ||
1350 (pevlr
->EventType
== EVENTLOG_ERROR_TYPE
&& !EventLogFilter
->Error
) ||
1351 (pevlr
->EventType
== EVENTLOG_AUDIT_SUCCESS
&& !EventLogFilter
->AuditSuccess
) ||
1352 (pevlr
->EventType
== EVENTLOG_AUDIT_FAILURE
&& !EventLogFilter
->AuditFailure
))
1360 FilterByString(IN PCWSTR FilterString
, // This is a multi-string
1365 /* The filter string is NULL so it does not filter anything */
1370 * If the filter string filters for an empty string AND the source string
1371 * is an empty string, we have a match (particular case of the last one).
1373 if (!*FilterString
&& !*String
)
1376 // if (*FilterString || *String)
1379 * If the filter string is empty BUT the source string is not empty,
1380 * OR vice-versa, we cannot have a match.
1382 if ( (!*FilterString
&& *String
) || (*FilterString
&& !*String
) )
1386 * If the filter string filters for at least a non-empty string,
1387 * browse it and search for a string that matches the source string.
1389 // else if (*FilterString && *String)
1391 pStr
= FilterString
;
1394 if (wcsicmp(pStr
, String
) == 0)
1396 /* We have a match, break the loop */
1400 pStr
+= (wcslen(pStr
) + 1);
1402 if (!*pStr
) // && *String
1404 /* We do not have a match */
1409 /* We have a match */
1414 * The events enumerator thread.
1417 EnumEventsThread(IN LPVOID lpParameter
)
1419 PEVENTLOGFILTER EventLogFilter
= (PEVENTLOGFILTER
)lpParameter
;
1420 LPWSTR lpMachineName
= NULL
; // EventLogFilter->ComputerName;
1425 PEVENTLOGRECORD pevlr
, pevlrTmp
= NULL
;
1426 DWORD dwRead
, dwNeeded
; // , dwThisRecord;
1427 DWORD dwTotalRecords
= 0, dwCurrentRecord
= 0;
1428 DWORD dwFlags
, dwMaxLength
;
1429 size_t cchRemaining
;
1430 LPWSTR lpszSourceName
;
1431 LPWSTR lpszComputerName
;
1432 BOOL bResult
= TRUE
; /* Read succeeded */
1434 UINT uStep
= 0, uStepAt
= 0, uPos
= 0;
1436 WCHAR szWindowTitle
[MAX_PATH
];
1437 WCHAR szStatusText
[MAX_PATH
];
1438 WCHAR szLocalDate
[MAX_PATH
];
1439 WCHAR szLocalTime
[MAX_PATH
];
1440 WCHAR szEventID
[MAX_PATH
];
1441 WCHAR szEventTypeText
[MAX_LOADSTRING
];
1442 WCHAR szCategoryID
[MAX_PATH
];
1443 WCHAR szUsername
[MAX_PATH
];
1444 WCHAR szCategory
[MAX_PATH
];
1445 PWCHAR lpTitleTemplateEnd
;
1448 LVITEMW lviEventItem
;
1450 /* Save the current event log filter globally */
1451 EventLogFilter_AddRef(EventLogFilter
);
1452 ActiveFilter
= EventLogFilter
;
1454 /* Disable list view redraw */
1455 SendMessageW(hwndListView
, WM_SETREDRAW
, FALSE
, 0);
1457 /* Clear the list view and free the cached records */
1458 ListView_DeleteAllItems(hwndListView
);
1461 SendMessageW(hwndListView
, LVM_PROGRESS
, 0, TRUE
);
1462 ProgressBar_SetRange(hwndStatusProgress
, 0);
1463 StatusBar_SetText(hwndStatus
, 0, NULL
);
1464 ShowWindow(hwndStatusProgress
, SW_SHOW
);
1466 /* Do a loop over the logs enumerated in the filter */
1467 // FIXME: For now we only support 1 event log per filter!
1469 // for (LogIndex = 0; LogIndex < EventLogFilter->NumOfEventLogs; ++LogIndex)
1472 EventLog
= EventLogFilter
->EventLogs
[LogIndex
];
1474 /* Open the event log */
1475 if (EventLog
->Permanent
)
1476 hEventLog
= OpenEventLogW(EventLog
->ComputerName
, EventLog
->LogName
);
1478 hEventLog
= OpenBackupEventLogW(EventLog
->ComputerName
, EventLog
->LogName
); // FileName
1480 if (hEventLog
== NULL
)
1482 ShowLastWin32Error();
1486 // GetOldestEventLogRecord(hEventLog, &dwThisRecord);
1488 /* Get the total number of event log records */
1489 GetNumberOfEventLogRecords(hEventLog
, &dwTotalRecords
);
1491 if (dwTotalRecords
> 0)
1493 EnableMenuItem(hMainMenu
, IDM_CLEAR_EVENTS
, MF_BYCOMMAND
| MF_ENABLED
);
1494 EnableMenuItem(hMainMenu
, IDM_SAVE_EVENTLOG
, MF_BYCOMMAND
| MF_ENABLED
);
1498 EnableMenuItem(hMainMenu
, IDM_CLEAR_EVENTS
, MF_BYCOMMAND
| MF_GRAYED
);
1499 EnableMenuItem(hMainMenu
, IDM_SAVE_EVENTLOG
, MF_BYCOMMAND
| MF_GRAYED
);
1502 /* Set up the event records cache */
1503 g_RecordPtrs
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, dwTotalRecords
* sizeof(*g_RecordPtrs
));
1506 // ShowLastWin32Error();
1509 g_TotalRecords
= dwTotalRecords
;
1511 if (WaitForSingleObject(hStopEnumEvent
, 0) == WAIT_OBJECT_0
)
1514 ProgressBar_SetRange(hwndStatusProgress
, MAKELPARAM(0, 100));
1515 uStepAt
= (dwTotalRecords
/ 100) + 1;
1517 dwFlags
= EVENTLOG_SEQUENTIAL_READ
|
1518 (NewestEventsFirst
? EVENTLOG_FORWARDS_READ
1519 : EVENTLOG_BACKWARDS_READ
);
1521 while (dwCurrentRecord
< dwTotalRecords
)
1524 // NOTE: We always allocate the minimum size for 1 record, and if
1525 // the ReadEventLog call fails (it always will anyway), we reallocate
1526 // the record pointer to be able to hold just 1 record, and then we
1527 // redo that for each event.
1528 // This is obviously not at all efficient (in terms of numbers of
1529 // ReadEventLog calls), since ReadEventLog can fill the buffer with
1530 // as many records they can fit completely in the buffer.
1533 pevlr
= HeapAlloc(GetProcessHeap(), 0, sizeof(*pevlr
));
1536 /* Cannot allocate, just skip the event */
1537 g_RecordPtrs
[dwCurrentRecord
] = NULL
;
1538 // --dwTotalRecords;
1541 g_RecordPtrs
[dwCurrentRecord
] = pevlr
;
1543 bResult
= ReadEventLogW(hEventLog
, // Event log handle
1544 dwFlags
, // Sequential read
1545 0, // Ignored for sequential read
1546 pevlr
, // Pointer to buffer
1547 sizeof(*pevlr
), // Size of buffer
1548 &dwRead
, // Number of bytes read
1549 &dwNeeded
); // Bytes in the next record
1550 if (!bResult
&& (GetLastError () == ERROR_INSUFFICIENT_BUFFER
))
1552 pevlrTmp
= HeapReAlloc(GetProcessHeap(), 0, pevlr
, dwNeeded
);
1555 /* Cannot reallocate, just skip the event */
1556 HeapFree(GetProcessHeap(), 0, pevlr
);
1557 g_RecordPtrs
[dwCurrentRecord
] = NULL
;
1558 // --dwTotalRecords;
1562 g_RecordPtrs
[dwCurrentRecord
] = pevlr
;
1564 ReadEventLogW(hEventLog
, // event log handle
1565 dwFlags
, // read flags
1566 0, // offset; default is 0
1567 pevlr
, // pointer to buffer
1568 dwNeeded
, // size of buffer
1569 &dwRead
, // number of bytes read
1570 &dwNeeded
); // bytes in next record
1575 // ProgressBar_StepIt(hwndStatusProgress);
1577 if(uStep
% uStepAt
== 0)
1580 ProgressBar_SetPos(hwndStatusProgress
, uPos
);
1583 if (WaitForSingleObject(hStopEnumEvent
, 0) == WAIT_OBJECT_0
)
1586 /* Filter by event type */
1587 if (!FilterByType(EventLogFilter
, pevlr
))
1590 /* Get the event source name and filter it */
1591 lpszSourceName
= (LPWSTR
)((LPBYTE
)pevlr
+ sizeof(EVENTLOGRECORD
));
1592 if (!FilterByString(EventLogFilter
->Sources
, lpszSourceName
))
1595 /* Get the computer name and filter it */
1596 lpszComputerName
= (LPWSTR
)((LPBYTE
)pevlr
+ sizeof(EVENTLOGRECORD
) + (wcslen(lpszSourceName
) + 1) * sizeof(WCHAR
));
1597 if (!FilterByString(EventLogFilter
->ComputerNames
, lpszComputerName
))
1600 /* Compute the event time */
1601 EventTimeToSystemTime(pevlr
->TimeWritten
, &time
);
1602 GetDateFormatW(LOCALE_USER_DEFAULT
, DATE_SHORTDATE
, &time
, NULL
, szLocalDate
, ARRAYSIZE(szLocalDate
));
1603 GetTimeFormatW(LOCALE_USER_DEFAULT
, 0, &time
, NULL
, szLocalTime
, ARRAYSIZE(szLocalTime
));
1605 LoadStringW(hInst
, IDS_NOT_AVAILABLE
, szUsername
, ARRAYSIZE(szUsername
));
1606 LoadStringW(hInst
, IDS_NONE
, szCategory
, ARRAYSIZE(szCategory
));
1608 /* Get the username that generated the event, and filter it */
1609 GetEventUserName(pevlr
, szUsername
);
1610 if (!FilterByString(EventLogFilter
->Users
, szUsername
))
1613 // TODO: Filter by event ID and category
1615 GetEventType(pevlr
->EventType
, szEventTypeText
);
1616 GetEventCategory(EventLog
->LogName
, lpszSourceName
, pevlr
, szCategory
);
1618 StringCbPrintfW(szEventID
, sizeof(szEventID
), L
"%u", (pevlr
->EventID
& 0xFFFF));
1619 StringCbPrintfW(szCategoryID
, sizeof(szCategoryID
), L
"%u", pevlr
->EventCategory
);
1621 lviEventItem
.mask
= LVIF_IMAGE
| LVIF_TEXT
| LVIF_PARAM
;
1622 lviEventItem
.iItem
= 0;
1623 lviEventItem
.iSubItem
= 0;
1624 lviEventItem
.lParam
= (LPARAM
)pevlr
;
1625 lviEventItem
.pszText
= szEventTypeText
;
1627 switch (pevlr
->EventType
)
1629 case EVENTLOG_SUCCESS
:
1630 case EVENTLOG_INFORMATION_TYPE
:
1631 lviEventItem
.iImage
= 0;
1634 case EVENTLOG_WARNING_TYPE
:
1635 lviEventItem
.iImage
= 1;
1638 case EVENTLOG_ERROR_TYPE
:
1639 lviEventItem
.iImage
= 2;
1642 case EVENTLOG_AUDIT_SUCCESS
:
1643 lviEventItem
.iImage
= 3;
1646 case EVENTLOG_AUDIT_FAILURE
:
1647 lviEventItem
.iImage
= 4;
1651 lviEventItem
.iItem
= ListView_InsertItem(hwndListView
, &lviEventItem
);
1653 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 1, szLocalDate
);
1654 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 2, szLocalTime
);
1655 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 3, lpszSourceName
);
1656 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 4, szCategory
);
1657 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 5, szEventID
);
1658 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 6, szUsername
);
1659 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 7, lpszComputerName
);
1662 dwRead
-= pevlr
->Length
;
1663 pevlr
= (PEVENTLOGRECORD
)((LPBYTE
) pevlr
+ pevlr
->Length
);
1671 /* Close the event log */
1672 CloseEventLog(hEventLog
);
1674 } // end-for (LogIndex)
1676 /* All events loaded */
1680 ShowWindow(hwndStatusProgress
, SW_HIDE
);
1681 SendMessageW(hwndListView
, LVM_PROGRESS
, 0, FALSE
);
1683 // FIXME: Use something else instead of EventLog->LogName !!
1686 * Use a different formatting, whether the event log filter holds
1687 * only one log, or many logs (the latter case is WIP TODO!)
1689 if (EventLogFilter
->NumOfEventLogs
<= 1)
1691 StringCchPrintfExW(szWindowTitle
,
1692 ARRAYSIZE(szWindowTitle
),
1693 &lpTitleTemplateEnd
,
1696 szTitleTemplate
, szTitle
, EventLog
->LogName
); /* i = number of characters written */
1697 dwMaxLength
= (DWORD
)cchRemaining
;
1699 GetComputerNameW(lpTitleTemplateEnd
, &dwMaxLength
);
1701 StringCchCopyW(lpTitleTemplateEnd
, dwMaxLength
, lpMachineName
);
1703 StringCbPrintfW(szStatusText
,
1704 sizeof(szStatusText
),
1705 szStatusBarTemplate
,
1708 ListView_GetItemCount(hwndListView
));
1712 // TODO: Use a different title & implement filtering for multi-log filters !!
1713 // (EventLogFilter->NumOfEventLogs > 1)
1714 MessageBoxW(hwndMainWindow
,
1715 L
"Many-logs filtering is not implemented yet!!",
1717 MB_OK
| MB_ICONINFORMATION
);
1720 /* Update the status bar */
1721 StatusBar_SetText(hwndStatus
, 0, szStatusText
);
1723 /* Set the window title */
1724 SetWindowTextW(hwndMainWindow
, szWindowTitle
);
1726 /* Resume list view redraw */
1727 SendMessageW(hwndListView
, WM_SETREDRAW
, TRUE
, 0);
1729 EventLogFilter_Release(EventLogFilter
);
1731 CloseHandle(hStopEnumEvent
);
1732 InterlockedExchangePointer((PVOID
*)&hStopEnumEvent
, NULL
);
1738 * The purpose of this thread is to serialize the creation of the events
1739 * enumeration thread, since the Event Log Viewer currently only supports
1740 * one view, one event list, one enumeration.
1743 StartStopEnumEventsThread(IN LPVOID lpParameter
)
1745 HANDLE WaitHandles
[2];
1748 WaitHandles
[0] = hStartStopEnumEvent
; // End-of-application event
1749 WaitHandles
[1] = hStartEnumEvent
; // Command event
1753 WaitResult
= WaitForMultipleObjects(ARRAYSIZE(WaitHandles
),
1759 case WAIT_OBJECT_0
+ 0:
1761 /* End-of-application event signaled, quit this thread */
1763 /* Stop the previous enumeration */
1764 if (hEnumEventsThread
)
1768 SetEvent(hStopEnumEvent
);
1769 WaitForSingleObject(hEnumEventsThread
, INFINITE
);
1770 // NOTE: The following is done by the enumeration thread just before terminating.
1771 // hStopEnumEvent = NULL;
1774 CloseHandle(hEnumEventsThread
);
1775 hEnumEventsThread
= NULL
;
1778 /* Clear the list view and free the cached records */
1779 ListView_DeleteAllItems(hwndListView
);
1782 /* Reset the active filter */
1783 ActiveFilter
= NULL
;
1788 case WAIT_OBJECT_0
+ 1:
1790 /* Restart a new enumeration if needed */
1791 PEVENTLOGFILTER EventLogFilter
;
1793 /* Stop the previous enumeration */
1794 if (hEnumEventsThread
)
1798 SetEvent(hStopEnumEvent
);
1799 WaitForSingleObject(hEnumEventsThread
, INFINITE
);
1800 // NOTE: The following is done by the enumeration thread just before terminating.
1801 // hStopEnumEvent = NULL;
1804 CloseHandle(hEnumEventsThread
);
1805 hEnumEventsThread
= NULL
;
1808 /* Clear the list view and free the cached records */
1809 ListView_DeleteAllItems(hwndListView
);
1812 /* Reset the active filter */
1813 ActiveFilter
= NULL
;
1815 EventLogFilter
= InterlockedExchangePointer((PVOID
*)&EnumFilter
, NULL
);
1816 if (!EventLogFilter
)
1819 // Manual-reset event
1820 hStopEnumEvent
= CreateEventW(NULL
, TRUE
, FALSE
, NULL
);
1821 if (!hStopEnumEvent
)
1824 hEnumEventsThread
= CreateThread(NULL
,
1827 (LPVOID
)EventLogFilter
,
1830 if (!hEnumEventsThread
)
1832 CloseHandle(hStopEnumEvent
);
1833 hStopEnumEvent
= NULL
;
1836 // CloseHandle(hEnumEventsThread);
1837 ResumeThread(hEnumEventsThread
);
1844 /* Unknown command, must never go there! */
1845 return GetLastError();
1854 EnumEvents(IN PEVENTLOGFILTER EventLogFilter
)
1856 /* Signal the enumerator thread we want to enumerate events */
1857 InterlockedExchangePointer((PVOID
*)&EnumFilter
, EventLogFilter
);
1858 SetEvent(hStartEnumEvent
);
1864 GetSelectedFilter(OUT HTREEITEM
* phti OPTIONAL
)
1872 /* Get index of selected item */
1873 hti
= TreeView_GetSelection(hwndTreeView
);
1875 return NULL
; // No filter
1877 tvItemEx
.mask
= TVIF_PARAM
;
1878 tvItemEx
.hItem
= hti
;
1880 TreeView_GetItem(hwndTreeView
, &tvItemEx
);
1883 *phti
= tvItemEx
.hItem
;
1885 return (PEVENTLOGFILTER
)tvItemEx
.lParam
;
1890 OpenUserEventLog(VOID
)
1893 PEVENTLOGFILTER EventLogFilter
;
1894 HTREEITEM hItem
= NULL
;
1895 WCHAR szFileName
[MAX_PATH
];
1897 ZeroMemory(szFileName
, sizeof(szFileName
));
1899 sfn
.lpstrFile
= szFileName
;
1900 sfn
.nMaxFile
= ARRAYSIZE(szFileName
);
1902 if (!GetOpenFileNameW(&sfn
))
1904 sfn
.lpstrFile
[sfn
.nMaxFile
-1] = UNICODE_NULL
;
1906 /* Allocate a new event log entry */
1907 EventLog
= AllocEventLog(NULL
, sfn
.lpstrFile
, FALSE
);
1908 if (EventLog
== NULL
)
1911 /* Allocate a new event log filter entry for this event log */
1912 EventLogFilter
= AllocEventLogFilter(// LogName,
1913 TRUE
, TRUE
, TRUE
, TRUE
, TRUE
,
1916 if (EventLogFilter
== NULL
)
1918 HeapFree(GetProcessHeap(), 0, EventLog
);
1922 /* Add the event log and the filter into their lists */
1923 InsertTailList(&EventLogList
, &EventLog
->ListEntry
);
1924 InsertTailList(&EventLogFilterList
, &EventLogFilter
->ListEntry
);
1926 /* Retrieve and cache the event log file */
1927 EventLog
->FileName
= HeapAlloc(GetProcessHeap(), 0, sfn
.nMaxFile
* sizeof(WCHAR
));
1928 if (EventLog
->FileName
)
1929 StringCchCopyW(EventLog
->FileName
, sfn
.nMaxFile
, sfn
.lpstrFile
);
1931 hItem
= TreeViewAddItem(hwndTreeView
, htiUserLogs
,
1933 2, 3, (LPARAM
)EventLogFilter
);
1935 /* Select the event log */
1938 // TreeView_Expand(hwndTreeView, htiUserLogs, TVE_EXPAND);
1939 TreeView_SelectItem(hwndTreeView
, hItem
);
1940 TreeView_EnsureVisible(hwndTreeView
, hItem
);
1942 SetFocus(hwndTreeView
);
1946 SaveEventLog(IN PEVENTLOGFILTER EventLogFilter
)
1950 WCHAR szFileName
[MAX_PATH
];
1952 /* Bail out if there is no available filter */
1953 if (!EventLogFilter
)
1956 ZeroMemory(szFileName
, sizeof(szFileName
));
1958 sfn
.lpstrFile
= szFileName
;
1959 sfn
.nMaxFile
= ARRAYSIZE(szFileName
);
1961 if (!GetSaveFileNameW(&sfn
))
1964 EventLogFilter_AddRef(EventLogFilter
);
1966 EventLog
= EventLogFilter
->EventLogs
[0];
1967 hEventLog
= OpenEventLogW(EventLog
->ComputerName
, EventLog
->LogName
);
1969 EventLogFilter_Release(EventLogFilter
);
1973 ShowLastWin32Error();
1977 if (!BackupEventLogW(hEventLog
, szFileName
))
1978 ShowLastWin32Error();
1980 CloseEventLog(hEventLog
);
1984 CloseUserEventLog(IN PEVENTLOGFILTER EventLogFilter
, IN HTREEITEM hti
)
1986 /* Bail out if there is no available filter */
1987 if (!EventLogFilter
)
1990 if (InterlockedCompareExchangePointer((PVOID
*)&ActiveFilter
, NULL
, NULL
) == EventLogFilter
)
1992 /* Signal the enumerator thread we want to stop enumerating events */
1993 // EnumEvents(NULL);
1994 InterlockedExchangePointer((PVOID
*)&EnumFilter
, NULL
);
1995 SetEvent(hStartEnumEvent
);
1999 * The deletion of the item automatically triggers a TVN_SELCHANGED
2000 * notification, that will reset the ActiveFilter (in case the item
2001 * selected is a filter). Otherwise we reset it there.
2003 TreeView_DeleteItem(hwndTreeView
, hti
);
2005 /* Remove the filter from the list */
2006 RemoveEntryList(&EventLogFilter
->ListEntry
);
2007 EventLogFilter_Release(EventLogFilter
);
2009 // /* Select the default event log */
2010 // // TreeView_Expand(hwndTreeView, htiUserLogs, TVE_EXPAND);
2011 // TreeView_SelectItem(hwndTreeView, hItem);
2012 // TreeView_EnsureVisible(hwndTreeView, hItem);
2013 SetFocus(hwndTreeView
);
2018 ClearEvents(IN PEVENTLOGFILTER EventLogFilter
)
2023 WCHAR szFileName
[MAX_PATH
];
2024 WCHAR szMessage
[MAX_LOADSTRING
];
2026 /* Bail out if there is no available filter */
2027 if (!EventLogFilter
)
2030 ZeroMemory(szFileName
, sizeof(szFileName
));
2031 ZeroMemory(szMessage
, sizeof(szMessage
));
2033 LoadStringW(hInst
, IDS_CLEAREVENTS_MSG
, szMessage
, ARRAYSIZE(szMessage
));
2035 sfn
.lpstrFile
= szFileName
;
2036 sfn
.nMaxFile
= ARRAYSIZE(szFileName
);
2038 switch (MessageBoxW(hwndMainWindow
, szMessage
, szTitle
, MB_YESNOCANCEL
| MB_ICONINFORMATION
))
2044 sfn
.lpstrFile
= NULL
;
2048 if (!GetSaveFileNameW(&sfn
))
2053 EventLogFilter_AddRef(EventLogFilter
);
2055 EventLog
= EventLogFilter
->EventLogs
[0];
2056 hEventLog
= OpenEventLogW(EventLog
->ComputerName
, EventLog
->LogName
);
2058 EventLogFilter_Release(EventLogFilter
);
2062 ShowLastWin32Error();
2066 Success
= ClearEventLogW(hEventLog
, sfn
.lpstrFile
);
2068 ShowLastWin32Error();
2070 CloseEventLog(hEventLog
);
2076 Refresh(IN PEVENTLOGFILTER EventLogFilter
)
2078 /* Bail out if there is no available filter */
2079 if (!EventLogFilter
)
2082 /* Reenumerate the events through the filter */
2083 EnumEvents(EventLogFilter
);
2088 MyRegisterClass(HINSTANCE hInstance
)
2092 wcex
.cbSize
= sizeof(wcex
);
2094 wcex
.lpfnWndProc
= WndProc
;
2095 wcex
.cbClsExtra
= 0;
2096 wcex
.cbWndExtra
= 0;
2097 wcex
.hInstance
= hInstance
;
2098 wcex
.hIcon
= LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_EVENTVWR
));
2099 wcex
.hCursor
= LoadCursorW(NULL
, MAKEINTRESOURCEW(IDC_ARROW
));
2100 wcex
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1); // COLOR_WINDOW + 1
2101 wcex
.lpszMenuName
= MAKEINTRESOURCEW(IDM_EVENTVWR
);
2102 wcex
.lpszClassName
= szWindowClass
;
2103 wcex
.hIconSm
= (HICON
)LoadImageW(hInstance
,
2104 MAKEINTRESOURCEW(IDI_EVENTVWR
),
2110 return RegisterClassExW(&wcex
);
2115 GetDisplayNameFileAndID(IN LPCWSTR lpLogName
,
2116 OUT PWCHAR lpModuleName
, // TODO: Add IN DWORD BufLen
2117 OUT PDWORD pdwMessageID
)
2119 BOOL Success
= FALSE
;
2125 DWORD dwMessageID
= 0;
2126 WCHAR szModuleName
[MAX_PATH
];
2128 /* Use a default value for the message ID */
2131 cbKeyPath
= (wcslen(EVENTLOG_BASE_KEY
) + wcslen(lpLogName
) + 1) * sizeof(WCHAR
);
2132 KeyPath
= HeapAlloc(GetProcessHeap(), 0, cbKeyPath
);
2136 StringCbCopyW(KeyPath
, cbKeyPath
, EVENTLOG_BASE_KEY
);
2137 StringCbCatW(KeyPath
, cbKeyPath
, lpLogName
);
2139 Result
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, KeyPath
, 0, KEY_QUERY_VALUE
, &hLogKey
);
2140 HeapFree(GetProcessHeap(), 0, KeyPath
);
2141 if (Result
!= ERROR_SUCCESS
)
2144 cbData
= sizeof(szModuleName
);
2145 Result
= RegQueryValueExW(hLogKey
,
2149 (LPBYTE
)szModuleName
,
2151 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_EXPAND_SZ
&& Type
!= REG_SZ
))
2153 szModuleName
[0] = UNICODE_NULL
;
2157 /* NULL-terminate the string and expand it */
2158 szModuleName
[cbData
/ sizeof(WCHAR
) - 1] = UNICODE_NULL
;
2159 ExpandEnvironmentStringsW(szModuleName
, lpModuleName
, ARRAYSIZE(szModuleName
));
2164 * If we have a 'DisplayNameFile', query for 'DisplayNameID';
2165 * otherwise it's not really useful. 'DisplayNameID' is optional.
2169 cbData
= sizeof(dwMessageID
);
2170 Result
= RegQueryValueExW(hLogKey
,
2174 (LPBYTE
)&dwMessageID
,
2176 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_DWORD
))
2179 *pdwMessageID
= dwMessageID
;
2182 RegCloseKey(hLogKey
);
2189 BuildLogListAndFilterList(IN LPCWSTR lpComputerName
)
2192 HKEY hEventLogKey
, hLogKey
;
2193 DWORD dwNumLogs
= 0;
2194 DWORD dwIndex
, dwMaxKeyLength
;
2197 PEVENTLOGFILTER EventLogFilter
;
2198 LPWSTR LogName
= NULL
;
2199 WCHAR szModuleName
[MAX_PATH
];
2202 LPWSTR lpDisplayName
;
2203 HTREEITEM hRootNode
= NULL
, hItem
= NULL
, hItemDefault
= NULL
;
2205 /* Open the EventLog key */
2206 // FIXME: Use local or remote computer
2207 Result
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, EVENTLOG_BASE_KEY
, 0, KEY_READ
, &hEventLogKey
);
2208 if (Result
!= ERROR_SUCCESS
)
2213 /* Retrieve the number of event logs enumerated as registry keys */
2214 Result
= RegQueryInfoKeyW(hEventLogKey
, NULL
, NULL
, NULL
, &dwNumLogs
, &dwMaxKeyLength
,
2215 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
2216 if (Result
!= ERROR_SUCCESS
)
2223 /* Take the NULL terminator into account */
2226 /* Allocate the temporary buffer */
2227 LogName
= HeapAlloc(GetProcessHeap(), 0, dwMaxKeyLength
* sizeof(WCHAR
));
2231 /* Enumerate and retrieve each event log name */
2232 for (dwIndex
= 0; dwIndex
< dwNumLogs
; dwIndex
++)
2234 lpcName
= dwMaxKeyLength
;
2235 Result
= RegEnumKeyExW(hEventLogKey
, dwIndex
, LogName
, &lpcName
, NULL
, NULL
, NULL
, NULL
);
2236 if (Result
!= ERROR_SUCCESS
)
2239 /* Take the NULL terminator into account */
2242 /* Allocate a new event log entry */
2243 EventLog
= AllocEventLog(lpComputerName
, LogName
, TRUE
);
2244 if (EventLog
== NULL
)
2247 /* Allocate a new event log filter entry for this event log */
2248 EventLogFilter
= AllocEventLogFilter(// LogName,
2249 TRUE
, TRUE
, TRUE
, TRUE
, TRUE
,
2252 if (EventLogFilter
== NULL
)
2254 HeapFree(GetProcessHeap(), 0, EventLog
);
2258 /* Add the event log and the filter into their lists */
2259 InsertTailList(&EventLogList
, &EventLog
->ListEntry
);
2260 InsertTailList(&EventLogFilterList
, &EventLogFilter
->ListEntry
);
2262 EventLog
->FileName
= NULL
;
2264 /* Retrieve and cache the event log file */
2265 Result
= RegOpenKeyExW(hEventLogKey
,
2270 if (Result
== ERROR_SUCCESS
)
2273 Result
= RegQueryValueExW(hLogKey
,
2279 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_EXPAND_SZ
&& Type
!= REG_SZ
))
2281 // Windows' EventLog uses some kind of default value, we do not.
2282 EventLog
->FileName
= NULL
;
2286 lpcName
= ROUND_DOWN(lpcName
, sizeof(WCHAR
));
2287 EventLog
->FileName
= HeapAlloc(GetProcessHeap(), 0, lpcName
);
2288 if (EventLog
->FileName
)
2290 Result
= RegQueryValueExW(hLogKey
,
2294 (LPBYTE
)EventLog
->FileName
,
2296 if (Result
!= ERROR_SUCCESS
)
2298 HeapFree(GetProcessHeap(), 0, EventLog
->FileName
);
2299 EventLog
->FileName
= NULL
;
2303 EventLog
->FileName
[lpcName
/ sizeof(WCHAR
) - 1] = UNICODE_NULL
;
2308 RegCloseKey(hLogKey
);
2311 /* Get the display name for the event log */
2312 lpDisplayName
= NULL
;
2314 ZeroMemory(szModuleName
, sizeof(szModuleName
));
2315 if (GetDisplayNameFileAndID(LogName
, szModuleName
, &dwMessageID
))
2317 /* Retrieve the message string without appending extra newlines */
2319 GetMessageStringFromDll(szModuleName
,
2320 FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_HMODULE
|
2321 FORMAT_MESSAGE_IGNORE_INSERTS
| FORMAT_MESSAGE_MAX_WIDTH_MASK
,
2328 * Select the correct tree root node, whether the log is a System
2329 * or an Application log. Default to Application log otherwise.
2331 hRootNode
= htiAppLogs
;
2332 for (lpcName
= 0; lpcName
< ARRAYSIZE(SystemLogs
); ++lpcName
)
2334 /* Check whether the log name is part of the system logs */
2335 if (wcsicmp(LogName
, SystemLogs
[lpcName
]) == 0)
2337 hRootNode
= htiSystemLogs
;
2342 hItem
= TreeViewAddItem(hwndTreeView
, hRootNode
,
2343 (lpDisplayName
? lpDisplayName
: LogName
),
2344 2, 3, (LPARAM
)EventLogFilter
);
2346 /* Try to get the default event log: "Application" */
2347 if ((hItemDefault
== NULL
) && (wcsicmp(LogName
, SystemLogs
[0]) == 0))
2349 hItemDefault
= hItem
;
2352 /* Free the buffer allocated by FormatMessage */
2354 LocalFree(lpDisplayName
);
2357 HeapFree(GetProcessHeap(), 0, LogName
);
2360 RegCloseKey(hEventLogKey
);
2362 /* Select the default event log */
2365 // TreeView_Expand(hwndTreeView, hRootNode, TVE_EXPAND);
2366 TreeView_SelectItem(hwndTreeView
, hItemDefault
);
2367 TreeView_EnsureVisible(hwndTreeView
, hItemDefault
);
2369 SetFocus(hwndTreeView
);
2380 while (!IsListEmpty(&EventLogList
))
2382 Entry
= RemoveHeadList(&EventLogList
);
2383 EventLog
= (PEVENTLOG
)CONTAINING_RECORD(Entry
, EVENTLOG
, ListEntry
);
2384 EventLog_Free(EventLog
);
2391 FreeLogFilterList(VOID
)
2394 PEVENTLOGFILTER EventLogFilter
;
2396 while (!IsListEmpty(&EventLogFilterList
))
2398 Entry
= RemoveHeadList(&EventLogFilterList
);
2399 EventLogFilter
= (PEVENTLOGFILTER
)CONTAINING_RECORD(Entry
, EVENTLOGFILTER
, ListEntry
);
2400 EventLogFilter_Free(EventLogFilter
);
2403 ActiveFilter
= NULL
;
2410 * ListView subclassing to handle WM_PAINT messages before and after they are
2411 * handled by the ListView window itself. We cannot use at this level the
2412 * custom-drawn notifications that are more suitable for drawing elements
2413 * inside the ListView.
2415 static WNDPROC orgListViewWndProc
= NULL
;
2416 static BOOL IsLoading
= FALSE
;
2419 ListViewWndProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
2425 /* TRUE: Create the dialog; FALSE: Destroy the dialog */
2426 IsLoading
= !!(BOOL
)lParam
;
2432 /* This code is adapted from: http://www.codeproject.com/Articles/216/Indicating-an-empty-ListView */
2440 COLORREF crTextOld
, crTextBkOld
;
2441 NONCLIENTMETRICSW ncm
;
2442 HFONT hFont
, hFontOld
;
2445 nItemCount
= ListView_GetItemCount(hWnd
);
2446 if (!IsLoading
&& nItemCount
> 0)
2451 * We could have used lpNMCustomDraw->nmcd.rc for the rectangle,
2452 * but this one actually holds the rectangle of the list view
2453 * that is being currently repainted, so that it can be smaller
2454 * than the list view proper. This is especially true when using
2455 * COMCTL32.DLL version <= 6.0 .
2458 GetClientRect(hWnd
, &rc
);
2459 hwndHeader
= ListView_GetHeader(hWnd
);
2462 /* Note that we could also use Header_GetItemRect() */
2463 GetClientRect(hwndHeader
, &rcH
);
2464 rc
.top
+= rcH
.bottom
;
2467 /* Add some space between the top of the list view and the text */
2470 BeginPaint(hWnd
, &ps
);
2472 * NOTE: Using a secondary hDC (and not the ps.hdc) gives the strange
2473 * property that the text is always recentered on the current view of
2474 * the window, instead of being scrolled together with the contents of
2481 * NOTE: We could have kept lpNMCustomDraw->clrText and
2482 * lpNMCustomDraw->clrTextBk, but they usually do not contain
2483 * the correct default colors for the items / default text.
2486 SetTextColor(hDC
, GetSysColor(COLOR_WINDOWTEXT
));
2488 SetBkColor(hDC
, GetSysColor(COLOR_WINDOW
));
2490 // FIXME: Cache the font?
2491 ncm
.cbSize
= sizeof(ncm
);
2493 if (SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
,
2494 sizeof(ncm
), &ncm
, 0))
2496 hFont
= CreateFontIndirectW(&ncm
.lfMessageFont
);
2499 hFont
= GetStockFont(DEFAULT_GUI_FONT
);
2501 hFontOld
= (HFONT
)SelectObject(hDC
, hFont
);
2503 FillRect(hDC
, &rc
, GetSysColorBrush(COLOR_WINDOW
));
2505 if (nItemCount
<= 0)
2506 lpszString
= szEmptyList
;
2507 else // if (IsLoading)
2508 lpszString
= szLoadingWait
;
2514 DT_CENTER
| DT_WORDBREAK
| DT_NOPREFIX
| DT_NOCLIP
);
2516 SelectObject(hDC
, hFontOld
);
2518 DeleteObject(hFont
);
2520 SetBkColor(hDC
, crTextBkOld
);
2521 SetTextColor(hDC
, crTextOld
);
2523 ReleaseDC(hWnd
, hDC
);
2524 EndPaint(hWnd
, &ps
);
2529 // case WM_ERASEBKGND:
2533 /* Continue with default message processing */
2534 return CallWindowProcW(orgListViewWndProc
, hWnd
, uMsg
, wParam
, lParam
);
2538 InitInstance(HINSTANCE hInstance
,
2544 LVCOLUMNW lvc
= {0};
2547 hInst
= hInstance
; // Store instance handle in our global variable
2549 /* Create the main window */
2550 hwndMainWindow
= CreateWindowW(szWindowClass
,
2552 WS_OVERLAPPEDWINDOW
| WS_CLIPCHILDREN
,
2553 CW_USEDEFAULT
, 0, CW_USEDEFAULT
, 0,
2558 if (!hwndMainWindow
)
2561 /* Create the status bar */
2562 hwndStatus
= CreateWindowExW(0, // no extended styles
2563 STATUSCLASSNAMEW
, // status bar
2565 WS_CHILD
| WS_VISIBLE
| CCS_BOTTOM
| SBARS_SIZEGRIP
, // styles
2566 0, 0, 0, 0, // x, y, cx, cy
2567 hwndMainWindow
, // parent window
2568 (HMENU
)100, // window ID
2569 hInstance
, // instance
2570 NULL
); // window data
2572 GetClientRect(hwndMainWindow
, &rcClient
);
2573 GetWindowRect(hwndStatus
, &rs
);
2574 StatusHeight
= rs
.bottom
- rs
.top
;
2576 /* Create a progress bar in the status bar (hidden by default) */
2577 StatusBar_GetItemRect(hwndStatus
, 0, &rs
);
2578 hwndStatusProgress
= CreateWindowExW(0, // no extended styles
2579 PROGRESS_CLASSW
, // status bar
2581 WS_CHILD
| PBS_SMOOTH
, // styles
2582 rs
.left
, rs
.top
, // x, y
2583 rs
.right
-rs
.left
, rs
.bottom
-rs
.top
, // cx, cy
2584 hwndStatus
, // parent window
2586 hInstance
, // instance
2587 NULL
); // window data
2588 ProgressBar_SetStep(hwndStatusProgress
, 1);
2590 /* Initialize the splitter default positions */
2594 /* Create the TreeView */
2595 hwndTreeView
= CreateWindowExW(WS_EX_CLIENTEDGE
,
2598 // WS_CHILD | WS_VISIBLE | TVS_HASLINES | TVS_SHOWSELALWAYS,
2599 WS_CHILD
| WS_VISIBLE
| /* WS_TABSTOP | */ TVS_HASLINES
| TVS_HASBUTTONS
| TVS_LINESATROOT
| TVS_EDITLABELS
| TVS_SHOWSELALWAYS
,
2601 nVSplitPos
- SPLIT_WIDTH
/2,
2602 (rcClient
.bottom
- rcClient
.top
) - StatusHeight
,
2608 /* Create the ImageList */
2609 hSmall
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
),
2610 GetSystemMetrics(SM_CYSMICON
),
2611 ILC_COLOR32
| ILC_MASK
, // ILC_COLOR24
2614 /* Add event type icons to the ImageList: closed/opened folder, event log (normal/viewed) */
2615 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_CLOSED_CATEGORY
)));
2616 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_OPENED_CATEGORY
)));
2617 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_EVENTLOG
)));
2618 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_EVENTVWR
)));
2620 /* Assign the ImageList to the Tree View */
2621 TreeView_SetImageList(hwndTreeView
, hSmall
, TVSIL_NORMAL
);
2623 /* Add the event logs nodes */
2625 LoadStringW(hInstance
, IDS_EVENTLOG_SYSTEM
, szTemp
, ARRAYSIZE(szTemp
));
2626 htiSystemLogs
= TreeViewAddItem(hwndTreeView
, NULL
, szTemp
, 0, 1, (LPARAM
)NULL
);
2627 // "Application Logs"
2628 LoadStringW(hInstance
, IDS_EVENTLOG_APP
, szTemp
, ARRAYSIZE(szTemp
));
2629 htiAppLogs
= TreeViewAddItem(hwndTreeView
, NULL
, szTemp
, 0, 1, (LPARAM
)NULL
);
2631 LoadStringW(hInstance
, IDS_EVENTLOG_USER
, szTemp
, ARRAYSIZE(szTemp
));
2632 htiUserLogs
= TreeViewAddItem(hwndTreeView
, NULL
, szTemp
, 0, 1, (LPARAM
)NULL
);
2634 /* Create the Event details pane (optional) */
2635 hwndEventDetails
= CreateEventDetailsCtrl(hInst
, hwndMainWindow
, (LPARAM
)NULL
);
2636 // hwndEventDetails = NULL;
2637 if (hwndEventDetails
)
2639 // SetWindowLongPtrW(hwndEventDetails, GWL_STYLE,
2640 // GetWindowLongPtrW(hwndEventDetails, GWL_STYLE) | WS_BORDER);
2641 SetWindowLongPtrW(hwndEventDetails
, GWL_EXSTYLE
,
2642 GetWindowLongPtrW(hwndEventDetails
, GWL_EXSTYLE
) | WS_EX_CLIENTEDGE
);
2643 SetWindowPos(hwndEventDetails
, NULL
,
2644 nVSplitPos
+ SPLIT_WIDTH
/2,
2645 nHSplitPos
+ SPLIT_WIDTH
/2,
2646 (rcClient
.right
- rcClient
.left
) - nVSplitPos
- SPLIT_WIDTH
/2,
2647 (rcClient
.bottom
- rcClient
.top
) - nHSplitPos
- SPLIT_WIDTH
/2 - StatusHeight
,
2648 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_SHOWWINDOW
);
2651 /* Create the ListView */
2652 hwndListView
= CreateWindowExW(WS_EX_CLIENTEDGE
,
2655 WS_CHILD
| WS_VISIBLE
| LVS_SHOWSELALWAYS
| LVS_REPORT
,
2656 nVSplitPos
+ SPLIT_WIDTH
/2,
2658 (rcClient
.right
- rcClient
.left
) - nVSplitPos
- SPLIT_WIDTH
/2,
2659 hwndEventDetails
? nHSplitPos
- SPLIT_WIDTH
/2
2660 : (rcClient
.bottom
- rcClient
.top
) - StatusHeight
,
2666 /* Add the extended ListView styles */
2667 ListView_SetExtendedListViewStyle(hwndListView
, LVS_EX_HEADERDRAGDROP
| LVS_EX_FULLROWSELECT
|LVS_EX_LABELTIP
);
2669 /* Create the ImageList */
2670 hSmall
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
),
2671 GetSystemMetrics(SM_CYSMICON
),
2672 ILC_COLOR32
| ILC_MASK
, // ILC_COLOR24
2675 /* Add event type icons to the ImageList */
2676 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_INFORMATIONICON
)));
2677 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_WARNINGICON
)));
2678 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_ERRORICON
)));
2679 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_AUDITSUCCESSICON
)));
2680 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_AUDITFAILUREICON
)));
2682 /* Assign the ImageList to the List View */
2683 ListView_SetImageList(hwndListView
, hSmall
, LVSIL_SMALL
);
2685 /* Now set up the listview with its columns */
2686 lvc
.mask
= LVCF_TEXT
| LVCF_WIDTH
;
2688 LoadStringW(hInstance
,
2692 lvc
.pszText
= szTemp
;
2693 ListView_InsertColumn(hwndListView
, 0, &lvc
);
2696 LoadStringW(hInstance
,
2700 lvc
.pszText
= szTemp
;
2701 ListView_InsertColumn(hwndListView
, 1, &lvc
);
2704 LoadStringW(hInstance
,
2708 lvc
.pszText
= szTemp
;
2709 ListView_InsertColumn(hwndListView
, 2, &lvc
);
2712 LoadStringW(hInstance
,
2716 lvc
.pszText
= szTemp
;
2717 ListView_InsertColumn(hwndListView
, 3, &lvc
);
2720 LoadStringW(hInstance
,
2724 lvc
.pszText
= szTemp
;
2725 ListView_InsertColumn(hwndListView
, 4, &lvc
);
2728 LoadStringW(hInstance
,
2732 lvc
.pszText
= szTemp
;
2733 ListView_InsertColumn(hwndListView
, 5, &lvc
);
2736 LoadStringW(hInstance
,
2740 lvc
.pszText
= szTemp
;
2741 ListView_InsertColumn(hwndListView
, 6, &lvc
);
2744 LoadStringW(hInstance
,
2748 lvc
.pszText
= szTemp
;
2749 ListView_InsertColumn(hwndListView
, 7, &lvc
);
2751 /* Subclass the ListView */
2752 // orgListViewWndProc = SubclassWindow(hwndListView, ListViewWndProc);
2753 orgListViewWndProc
= (WNDPROC
)(LONG_PTR
)GetWindowLongPtrW(hwndListView
, GWLP_WNDPROC
);
2754 SetWindowLongPtrW(hwndListView
, GWLP_WNDPROC
, (LONG_PTR
)ListViewWndProc
);
2756 /* Initialize the save Dialog */
2757 ZeroMemory(&sfn
, sizeof(sfn
));
2758 ZeroMemory(szSaveFilter
, sizeof(szSaveFilter
));
2760 LoadStringW(hInst
, IDS_SAVE_FILTER
, szSaveFilter
, ARRAYSIZE(szSaveFilter
));
2762 sfn
.lStructSize
= sizeof(sfn
);
2763 sfn
.hwndOwner
= hwndMainWindow
;
2764 sfn
.hInstance
= hInstance
;
2765 sfn
.lpstrFilter
= szSaveFilter
;
2766 sfn
.lpstrInitialDir
= NULL
;
2767 sfn
.Flags
= OFN_HIDEREADONLY
| OFN_SHAREAWARE
;
2768 sfn
.lpstrDefExt
= NULL
;
2770 ShowWindow(hwndMainWindow
, nCmdShow
);
2771 UpdateWindow(hwndMainWindow
);
2777 ExitInstance(HINSTANCE hInstance
)
2779 /* Restore the original ListView WndProc */
2780 // SubclassWindow(hwndListView, orgListViewWndProc);
2781 SetWindowLongPtrW(hwndListView
, GWLP_WNDPROC
, (LONG_PTR
)orgListViewWndProc
);
2782 orgListViewWndProc
= NULL
;
2785 VOID
ResizeWnd(INT cx
, INT cy
)
2791 /* Resize the status bar -- now done in WM_SIZE */
2792 // SendMessageW(hwndStatus, WM_SIZE, 0, 0);
2793 GetWindowRect(hwndStatus
, &rs
);
2794 StatusHeight
= rs
.bottom
- rs
.top
;
2796 /* Move the progress bar */
2797 StatusBar_GetItemRect(hwndStatus
, 0, &rs
);
2798 MoveWindow(hwndStatusProgress
,
2799 rs
.left
, rs
.top
, rs
.right
-rs
.left
, rs
.bottom
-rs
.top
,
2800 IsWindowVisible(hwndStatusProgress
) ? TRUE
: FALSE
);
2803 * TODO: Adjust the splitter positions:
2804 * - Vertical splitter (1) : fixed position from the left window side.
2805 * - Horizontal splitter (2): fixed position from the bottom window side.
2807 nVSplitPos
= min(max(nVSplitPos
, SPLIT_WIDTH
/2), cx
- SPLIT_WIDTH
/2); // OK
2808 nHSplitPos
= min(max(nHSplitPos
, SPLIT_WIDTH
/2), cy
- SPLIT_WIDTH
/2 - StatusHeight
); // FIXME!
2810 hdwp
= BeginDeferWindowPos(3);
2813 hdwp
= DeferWindowPos(hdwp
,
2817 nVSplitPos
- SPLIT_WIDTH
/2,
2819 SWP_NOZORDER
| SWP_NOACTIVATE
);
2822 hdwp
= DeferWindowPos(hdwp
,
2825 nVSplitPos
+ SPLIT_WIDTH
/2, 0,
2826 cx
- nVSplitPos
- SPLIT_WIDTH
/2,
2827 hwndEventDetails
? nHSplitPos
- SPLIT_WIDTH
/2
2828 : cy
- StatusHeight
,
2829 SWP_NOZORDER
| SWP_NOACTIVATE
);
2831 if (hwndEventDetails
&& hdwp
)
2832 hdwp
= DeferWindowPos(hdwp
,
2835 nVSplitPos
+ SPLIT_WIDTH
/2,
2836 nHSplitPos
+ SPLIT_WIDTH
/2,
2837 cx
- nVSplitPos
- SPLIT_WIDTH
/2,
2838 cy
- nHSplitPos
- SPLIT_WIDTH
/2 - StatusHeight
,
2839 SWP_NOZORDER
| SWP_NOACTIVATE
);
2842 EndDeferWindowPos(hdwp
);
2847 WndProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
2854 hMainMenu
= GetMenu(hWnd
);
2863 LPNMHDR hdr
= (LPNMHDR
)lParam
;
2865 if (hdr
->hwndFrom
== hwndListView
)
2869 case LVN_ITEMCHANGED
:
2871 LPNMLISTVIEW pnmv
= (LPNMLISTVIEW
)lParam
;
2873 if ( (pnmv
->uChanged
& LVIF_STATE
) && /* The state has changed */
2874 (pnmv
->uNewState
& LVIS_SELECTED
) /* The item has been (de)selected */ )
2876 if (hwndEventDetails
)
2877 SendMessageW(hwndEventDetails
, EVT_DISPLAY
, 0, 0);
2884 SendMessageW(hWnd
, WM_COMMAND
, IDM_EVENT_DETAILS
, 0);
2888 else if (hdr
->hwndFrom
== hwndTreeView
)
2892 case TVN_BEGINLABELEDIT
:
2894 HTREEITEM hItem
= ((LPNMTVDISPINFO
)lParam
)->item
.hItem
;
2896 /* Disable label editing for root nodes */
2897 return ((hItem
== htiSystemLogs
) ||
2898 (hItem
== htiAppLogs
) ||
2899 (hItem
== htiUserLogs
));
2902 case TVN_ENDLABELEDIT
:
2904 TVITEMW item
= ((LPNMTVDISPINFO
)lParam
)->item
;
2905 HTREEITEM hItem
= item
.hItem
;
2907 /* Disable label editing for root nodes */
2908 if ((hItem
== htiSystemLogs
) ||
2909 (hItem
== htiAppLogs
) ||
2910 (hItem
== htiUserLogs
))
2917 LPWSTR pszText
= item
.pszText
;
2919 /* Trim all whitespace */
2920 while (*pszText
&& iswspace(*pszText
))
2934 case TVN_SELCHANGED
:
2936 PEVENTLOGFILTER EventLogFilter
=
2937 (PEVENTLOGFILTER
)((LPNMTREEVIEW
)lParam
)->itemNew
.lParam
;
2939 // FIXME: It might be nice to reference here the filter,
2940 // so that we don't have to reference/dereference it many times
2941 // in the other functions???
2943 // FIXME: This is a hack!!
2944 if (hwndEventDetails
&& EventLogFilter
)
2946 SendMessageW(hwndEventDetails
, EVT_SETFILTER
, 0, (LPARAM
)EventLogFilter
);
2952 * If we have selected a filter, enable the menu commands;
2953 * they will possibly be updated after events enumeration.
2955 EnableMenuItem(hMainMenu
, IDM_SAVE_EVENTLOG
, MF_BYCOMMAND
| MF_ENABLED
);
2956 EnableMenuItem(hMainMenu
, IDM_CLOSE_EVENTLOG
, MF_BYCOMMAND
| MF_ENABLED
);
2957 EnableMenuItem(hMainMenu
, IDM_CLEAR_EVENTS
, MF_BYCOMMAND
| MF_ENABLED
);
2958 EnableMenuItem(hMainMenu
, IDM_RENAME_EVENTLOG
, MF_BYCOMMAND
| MF_ENABLED
);
2959 EnableMenuItem(hMainMenu
, IDM_EVENTLOG_SETTINGS
, MF_BYCOMMAND
| MF_ENABLED
);
2963 EnableMenuItem(hMainMenu
, IDM_SAVE_EVENTLOG
, MF_BYCOMMAND
| MF_GRAYED
);
2964 EnableMenuItem(hMainMenu
, IDM_CLOSE_EVENTLOG
, MF_BYCOMMAND
| MF_GRAYED
);
2965 EnableMenuItem(hMainMenu
, IDM_CLEAR_EVENTS
, MF_BYCOMMAND
| MF_GRAYED
);
2966 EnableMenuItem(hMainMenu
, IDM_RENAME_EVENTLOG
, MF_BYCOMMAND
| MF_GRAYED
);
2967 EnableMenuItem(hMainMenu
, IDM_EVENTLOG_SETTINGS
, MF_BYCOMMAND
| MF_GRAYED
);
2971 * The enumeration thread that is triggered by EnumEvents
2972 * will set a new value for the 'ActiveFilter'.
2975 EnumEvents(EventLogFilter
);
2986 /* Parse the menu selections */
2987 switch (LOWORD(wParam
))
2989 case IDM_OPEN_EVENTLOG
:
2993 case IDM_SAVE_EVENTLOG
:
2994 SaveEventLog(GetSelectedFilter(NULL
));
2997 case IDM_CLOSE_EVENTLOG
:
3000 PEVENTLOGFILTER EventLogFilter
= GetSelectedFilter(&hti
);
3001 CloseUserEventLog(EventLogFilter
, hti
);
3005 case IDM_CLEAR_EVENTS
:
3007 PEVENTLOGFILTER EventLogFilter
= GetSelectedFilter(NULL
);
3008 if (EventLogFilter
&& ClearEvents(EventLogFilter
))
3009 Refresh(EventLogFilter
);
3013 case IDM_RENAME_EVENTLOG
:
3014 if (GetFocus() == hwndTreeView
)
3015 TreeView_EditLabel(hwndTreeView
, TreeView_GetSelection(hwndTreeView
));
3018 case IDM_EVENTLOG_SETTINGS
:
3020 PEVENTLOGFILTER EventLogFilter
= GetSelectedFilter(NULL
);
3021 // TODO: Check the returned value?
3023 EventLogProperties(hInst
, hWnd
, EventLogFilter
);
3027 case IDM_LIST_NEWEST
:
3029 CheckMenuRadioItem(hMainMenu
, IDM_LIST_NEWEST
, IDM_LIST_OLDEST
, IDM_LIST_NEWEST
, MF_BYCOMMAND
);
3030 if (!NewestEventsFirst
)
3032 NewestEventsFirst
= TRUE
;
3033 Refresh(GetSelectedFilter(NULL
));
3038 case IDM_LIST_OLDEST
:
3040 CheckMenuRadioItem(hMainMenu
, IDM_LIST_NEWEST
, IDM_LIST_OLDEST
, IDM_LIST_OLDEST
, MF_BYCOMMAND
);
3041 if (NewestEventsFirst
)
3043 NewestEventsFirst
= FALSE
;
3044 Refresh(GetSelectedFilter(NULL
));
3049 case IDM_EVENT_DETAILS
:
3051 // LPNMITEMACTIVATE lpnmitem = (LPNMITEMACTIVATE)lParam;
3052 PEVENTLOGFILTER EventLogFilter
= GetSelectedFilter(NULL
);
3053 if (/*lpnmitem->iItem != -1 &&*/ EventLogFilter
)
3055 EventLogFilter_AddRef(EventLogFilter
);
3056 DialogBoxParamW(hInst
,
3057 MAKEINTRESOURCEW(IDD_EVENTDETAILS_DLG
),
3060 (LPARAM
)EventLogFilter
);
3061 EventLogFilter_Release(EventLogFilter
);
3067 Refresh(GetSelectedFilter(NULL
));
3073 WCHAR szCopyright
[MAX_LOADSTRING
];
3075 hIcon
= LoadIconW(hInst
, MAKEINTRESOURCEW(IDI_EVENTVWR
));
3076 LoadStringW(hInst
, IDS_COPYRIGHT
, szCopyright
, ARRAYSIZE(szCopyright
));
3077 ShellAboutW(hWnd
, szTitle
, szCopyright
, hIcon
);
3078 DeleteObject(hIcon
);
3083 MessageBoxW(hwndMainWindow
,
3084 L
"Help not implemented yet!",
3086 MB_OK
| MB_ICONINFORMATION
);
3090 DestroyWindow(hWnd
);
3094 return DefWindowProcW(hWnd
, uMsg
, wParam
, lParam
);
3100 if (LOWORD(lParam
) == HTCLIENT
)
3104 ScreenToClient(hWnd
, &pt
);
3106 /* Set the cursor for the vertical splitter */
3107 if (pt
.x
>= nVSplitPos
- SPLIT_WIDTH
/2 && pt
.x
< nVSplitPos
+ SPLIT_WIDTH
/2 + 1)
3110 GetClientRect(hWnd
, &rect
);
3111 GetWindowRect(hwndStatus
, &rs
);
3112 if (pt
.y
>= rect
.top
&& pt
.y
< rect
.bottom
- (rs
.bottom
- rs
.top
))
3114 SetCursor(LoadCursorW(NULL
, IDC_SIZEWE
));
3119 /* Set the cursor for the horizontal splitter, if the Event details pane is displayed */
3120 if (hwndEventDetails
&&
3121 (pt
.y
>= nHSplitPos
- SPLIT_WIDTH
/2 && pt
.y
< nHSplitPos
+ SPLIT_WIDTH
/2 + 1))
3124 GetClientRect(hWnd
, &rect
);
3125 // GetWindowRect(hwndStatus, &rs);
3126 if (pt
.x
>= nVSplitPos
+ SPLIT_WIDTH
/2 + 1 /* rect.left + (rs.bottom - rs.top) */ &&
3129 SetCursor(LoadCursorW(NULL
, IDC_SIZENS
));
3136 case WM_LBUTTONDOWN
:
3138 INT x
= GET_X_LPARAM(lParam
);
3139 INT y
= GET_Y_LPARAM(lParam
);
3141 /* Reset the splitter state */
3144 /* Capture the cursor for the vertical splitter */
3145 if (x
>= nVSplitPos
- SPLIT_WIDTH
/2 && x
< nVSplitPos
+ SPLIT_WIDTH
/2 + 1)
3151 /* Capture the cursor for the horizontal splitter, if the Event details pane is displayed */
3152 if (hwndEventDetails
&&
3153 (y
>= nHSplitPos
- SPLIT_WIDTH
/2 && y
< nHSplitPos
+ SPLIT_WIDTH
/2 + 1))
3162 case WM_RBUTTONDOWN
:
3163 if (GetCapture() == hWnd
)
3165 /* Adjust the correct splitter position */
3167 nVSplitPos
= GET_X_LPARAM(lParam
);
3168 else if (bSplit
== 2)
3169 nHSplitPos
= GET_Y_LPARAM(lParam
);
3171 /* If we are splitting, resize the windows */
3174 GetClientRect(hWnd
, &rect
);
3175 ResizeWnd(rect
.right
- rect
.left
, rect
.bottom
- rect
.top
);
3178 /* Reset the splitter state */
3186 if (GetCapture() == hWnd
)
3188 /* Move the correct splitter */
3191 INT x
= GET_X_LPARAM(lParam
);
3193 GetClientRect(hWnd
, &rect
);
3195 x
= min(max(x
, SPLIT_WIDTH
/2), rect
.right
- rect
.left
- SPLIT_WIDTH
/2);
3196 if (nVSplitPos
!= x
)
3199 ResizeWnd(rect
.right
- rect
.left
, rect
.bottom
- rect
.top
);
3202 else if (bSplit
== 2)
3205 INT y
= GET_Y_LPARAM(lParam
);
3207 GetClientRect(hWnd
, &rect
);
3208 GetWindowRect(hwndStatus
, &rs
);
3210 y
= min(max(y
, SPLIT_WIDTH
/2), rect
.bottom
- rect
.top
- SPLIT_WIDTH
/2 - (rs
.bottom
- rs
.top
));
3211 if (nHSplitPos
!= y
)
3214 ResizeWnd(rect
.right
- rect
.left
, rect
.bottom
- rect
.top
);
3222 if (wParam
!= SIZE_MINIMIZED
)
3224 SendMessageW(hwndStatus
, WM_SIZE
, 0, 0);
3225 ResizeWnd(LOWORD(lParam
), HIWORD(lParam
));
3228 /* Fall through the default case */
3232 return DefWindowProcW(hWnd
, uMsg
, wParam
, lParam
);
3241 InitPropertiesDlg(HWND hDlg
, PEVENTLOG EventLog
)
3243 LPWSTR lpLogName
= EventLog
->LogName
;
3246 DWORD dwMaxSize
= 0, dwRetention
= 0;
3248 WIN32_FIND_DATAW FileInfo
; // WIN32_FILE_ATTRIBUTE_DATA
3249 ULARGE_INTEGER FileSize
;
3250 WCHAR wszBuf
[MAX_PATH
];
3251 WCHAR szTemp
[MAX_LOADSTRING
];
3259 if (EventLog
->Permanent
)
3262 cbKeyPath
= (wcslen(EVENTLOG_BASE_KEY
) + wcslen(lpLogName
) + 1) * sizeof(WCHAR
);
3263 KeyPath
= HeapAlloc(GetProcessHeap(), 0, cbKeyPath
);
3269 StringCbCopyW(KeyPath
, cbKeyPath
, EVENTLOG_BASE_KEY
);
3270 StringCbCatW(KeyPath
, cbKeyPath
, lpLogName
);
3272 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE
, KeyPath
, 0, KEY_QUERY_VALUE
, &hLogKey
) != ERROR_SUCCESS
)
3274 HeapFree(GetProcessHeap(), 0, KeyPath
);
3277 HeapFree(GetProcessHeap(), 0, KeyPath
);
3280 cbData
= sizeof(dwMaxSize
);
3281 Result
= RegQueryValueExW(hLogKey
,
3287 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_DWORD
))
3289 // dwMaxSize = 512 * 1024; /* 512 kBytes */
3295 cbData
= sizeof(dwRetention
);
3296 Result
= RegQueryValueExW(hLogKey
,
3300 (LPBYTE
)&dwRetention
,
3302 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_DWORD
))
3304 /* On Windows 2003 it is 604800 (secs) == 7 days */
3307 /* Convert in days, rounded up */ // ROUND_UP
3308 // dwRetention = ROUND_UP(dwRetention, 24*3600) / (24*3600);
3309 dwRetention
= (dwRetention
+ 24*3600 - 1) / (24*3600);
3312 RegCloseKey(hLogKey
);
3319 SetDlgItemTextW(hDlg
, IDC_DISPLAYNAME
, lpLogName
); // FIXME!
3320 SetDlgItemTextW(hDlg
, IDC_LOGNAME
, lpLogName
);
3322 FileName
= EventLog
->FileName
;
3323 if (FileName
&& *FileName
)
3325 ExpandEnvironmentStringsW(FileName
, wszBuf
, MAX_PATH
);
3328 SetDlgItemTextW(hDlg
, IDC_LOGFILE
, FileName
);
3331 * The general problem here (and in the shell as well) is that
3332 * GetFileAttributesEx fails for files that are opened without
3333 * shared access. To retrieve file information for those we need
3334 * to use something else: FindFirstFile, on the full file name.
3337 Success
= GetFileAttributesExW(FileName
,
3338 GetFileExInfoStandard
,
3339 (LPWIN32_FILE_ATTRIBUTE_DATA
)&FileInfo
);
3342 HANDLE hFind
= FindFirstFileW(FileName
, &FileInfo
);
3343 Success
= (hFind
!= INVALID_HANDLE_VALUE
);
3348 // Starting there, FileName is invalid (because it uses wszBuf)
3352 FileSize
.u
.LowPart
= FileInfo
.nFileSizeLow
;
3353 FileSize
.u
.HighPart
= FileInfo
.nFileSizeHigh
;
3354 if (FormatFileSizeWithBytes(&FileSize
, wszBuf
, ARRAYSIZE(wszBuf
)))
3355 SetDlgItemTextW(hDlg
, IDC_SIZE_LABEL
, wszBuf
);
3357 LoadStringW(hInst
, IDS_NOT_AVAILABLE
, szTemp
, ARRAYSIZE(szTemp
));
3359 if (GetFileTimeString(&FileInfo
.ftCreationTime
, wszBuf
, ARRAYSIZE(wszBuf
)))
3360 SetDlgItemTextW(hDlg
, IDC_CREATED_LABEL
, wszBuf
);
3362 SetDlgItemTextW(hDlg
, IDC_CREATED_LABEL
, szTemp
);
3364 if (GetFileTimeString(&FileInfo
.ftLastWriteTime
, wszBuf
, ARRAYSIZE(wszBuf
)))
3365 SetDlgItemTextW(hDlg
, IDC_MODIFIED_LABEL
, wszBuf
);
3367 SetDlgItemTextW(hDlg
, IDC_MODIFIED_LABEL
, szTemp
);
3369 if (GetFileTimeString(&FileInfo
.ftLastAccessTime
, wszBuf
, ARRAYSIZE(wszBuf
)))
3370 SetDlgItemTextW(hDlg
, IDC_ACCESSED_LABEL
, wszBuf
);
3372 SetDlgItemTextW(hDlg
, IDC_MODIFIED_LABEL
, szTemp
);
3376 LoadStringW(hInst
, IDS_NOT_AVAILABLE
, szTemp
, ARRAYSIZE(szTemp
));
3378 SetDlgItemTextW(hDlg
, IDC_SIZE_LABEL
, szTemp
);
3379 SetDlgItemTextW(hDlg
, IDC_CREATED_LABEL
, szTemp
);
3380 SetDlgItemTextW(hDlg
, IDC_MODIFIED_LABEL
, szTemp
);
3381 SetDlgItemTextW(hDlg
, IDC_ACCESSED_LABEL
, szTemp
);
3384 if (EventLog
->Permanent
)
3386 SendDlgItemMessageW(hDlg
, IDC_UPDOWN_MAXLOGSIZE
, UDM_SETRANGE32
, (WPARAM
)1, (LPARAM
)0x3FFFC0);
3387 SendDlgItemMessageW(hDlg
, IDC_UPDOWN_EVENTS_AGE
, UDM_SETRANGE
, 0, (LPARAM
)MAKELONG(365, 1));
3389 SetDlgItemInt(hDlg
, IDC_EDIT_MAXLOGSIZE
, dwMaxSize
, FALSE
);
3390 SetDlgItemInt(hDlg
, IDC_EDIT_EVENTS_AGE
, dwRetention
, FALSE
);
3392 if (dwRetention
== 0)
3394 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_OVERWRITE_AS_NEEDED
);
3395 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, FALSE
);
3396 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, FALSE
);
3398 else if (dwRetention
== INFINITE
)
3400 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_NO_OVERWRITE
);
3401 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, FALSE
);
3402 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, FALSE
);
3406 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_OVERWRITE_OLDER_THAN
);
3407 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, TRUE
);
3408 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, TRUE
);
3413 // TODO: Hide the unused controls! Or, just use another type of property sheet!
3417 /* Message handler for EventLog Properties dialog */
3419 EventLogPropProc(HWND hDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
3423 EventLog
= (PEVENTLOG
)GetWindowLongPtrW(hDlg
, DWLP_USER
);
3429 EventLog
= (PEVENTLOG
)((LPPROPSHEETPAGE
)lParam
)->lParam
;
3430 SetWindowLongPtrW(hDlg
, DWLP_USER
, (LONG_PTR
)EventLog
);
3432 InitPropertiesDlg(hDlg
, EventLog
);
3434 PropSheet_UnChanged(GetParent(hDlg
), hDlg
);
3435 return (INT_PTR
)TRUE
;
3439 return (INT_PTR
)TRUE
;
3442 switch (LOWORD(wParam
))
3446 EndDialog(hDlg
, LOWORD(wParam
));
3447 return (INT_PTR
)TRUE
;
3449 case IDC_OVERWRITE_AS_NEEDED
:
3451 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_OVERWRITE_AS_NEEDED
);
3452 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, FALSE
);
3453 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, FALSE
);
3457 case IDC_OVERWRITE_OLDER_THAN
:
3459 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_OVERWRITE_OLDER_THAN
);
3460 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, TRUE
);
3461 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, TRUE
);
3465 case IDC_NO_OVERWRITE
:
3467 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_NO_OVERWRITE
);
3468 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, FALSE
);
3469 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, FALSE
);
3475 L
"Help not implemented yet!",
3477 MB_OK
| MB_ICONINFORMATION
);
3478 return (INT_PTR
)TRUE
;
3486 return (INT_PTR
)FALSE
;
3490 EventLogProperties(HINSTANCE hInstance
, HWND hWndParent
, PEVENTLOGFILTER EventLogFilter
)
3493 PROPSHEETHEADERW psh
;
3494 PROPSHEETPAGEW psp
[1]; // 2
3497 * Bail out if there is no available filter, or if the filter
3498 * contains more than one log.
3500 if (!EventLogFilter
)
3503 EventLogFilter_AddRef(EventLogFilter
);
3505 if (EventLogFilter
->NumOfEventLogs
> 1 ||
3506 EventLogFilter
->EventLogs
[0] == NULL
)
3512 psh
.dwSize
= sizeof(psh
);
3513 psh
.dwFlags
= PSH_PROPSHEETPAGE
/*| PSH_USEICONID */ | PSH_PROPTITLE
| PSH_HASHELP
/*| PSH_NOCONTEXTHELP */ /*| PSH_USECALLBACK */;
3514 psh
.hInstance
= hInstance
;
3515 psh
.hwndParent
= hWndParent
;
3516 // psh.pszIcon = MAKEINTRESOURCEW(IDI_APPICON); // Disabled because it only sets the small icon; the big icon is a stretched version of the small one.
3517 psh
.pszCaption
= EventLogFilter
->EventLogs
[0]->LogName
;
3520 psh
.nPages
= ARRAYSIZE(psp
);
3521 // psh.pfnCallback = PropSheetCallback;
3523 /* Log properties page */
3524 psp
[0].dwSize
= sizeof(psp
[0]);
3525 psp
[0].dwFlags
= PSP_HASHELP
;
3526 psp
[0].hInstance
= hInstance
;
3527 psp
[0].pszTemplate
= MAKEINTRESOURCEW(IDD_LOGPROPERTIES_GENERAL
);
3528 psp
[0].pfnDlgProc
= EventLogPropProc
;
3529 psp
[0].lParam
= (LPARAM
)EventLogFilter
->EventLogs
[0];
3532 /* TODO: Log sources page */
3533 psp
[1].dwSize
= sizeof(psp
[1]);
3534 psp
[1].dwFlags
= PSP_HASHELP
;
3535 psp
[1].hInstance
= hInstance
;
3536 psp
[1].pszTemplate
= MAKEINTRESOURCEW(IDD_GENERAL_PAGE
);
3537 psp
[1].pfnDlgProc
= GeneralPageWndProc
;
3538 psp
[0].lParam
= (LPARAM
)EventLogFilter
->EventLogs
[0];
3541 /* Create the property sheet */
3542 ret
= PropertySheetW(&psh
);
3545 EventLogFilter_Release(EventLogFilter
);
3549 /* Message handler for Event Details dialog */
3550 static HWND hWndDetailsCtrl
= NULL
; // May go into the DWLP_USER
3551 static HWND hWndGrip
= NULL
;
3552 static INT cxMin
, cyMin
; // In window coordinates
3553 static INT cxOld
, cyOld
; // In client coordinates
3556 EventDetails(HWND hDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
3563 INT sbVXSize
, sbHYSize
;
3566 hWndDetailsCtrl
= CreateEventDetailsCtrl(hInst
, hDlg
, lParam
);
3567 if (!hWndDetailsCtrl
)
3570 return (INT_PTR
)TRUE
;
3573 /* Create a size grip if the dialog has a sizing border */
3574 GetClientRect(hDlg
, &rcWnd
);
3575 dwStyle
= GetWindowLongPtrW(hDlg
, GWL_STYLE
);
3576 sbVXSize
= GetSystemMetrics(SM_CXVSCROLL
);
3577 sbHYSize
= GetSystemMetrics(SM_CYHSCROLL
);
3578 if (dwStyle
& WS_THICKFRAME
/* == WS_SIZEBOX */)
3580 hWndGrip
= CreateWindowW(WC_SCROLLBARW
,
3582 WS_CHILD
| WS_VISIBLE
| /**/ WS_CLIPSIBLINGS
| /**/ SBS_SIZEGRIP
| SBS_SIZEBOXBOTTOMRIGHTALIGN
,
3583 rcWnd
.right
- sbVXSize
,
3584 rcWnd
.bottom
- sbHYSize
,
3592 // SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)hWndDetailsCtrl);
3595 * Compute the minimum window size (in window coordinates) by
3596 * adding the widths/heights of the "Help" and "Close" buttons,
3597 * together with the margins, and add some minimal spacing
3598 * between the buttons.
3600 GetWindowRect(hDlg
, &rcWnd
);
3603 GetWindowRect(GetDlgItem(hDlg
, IDHELP
), &rect
);
3604 cxMin
+= (rect
.right
- rect
.left
) + (rect
.left
- rcWnd
.left
); // == (rect.right - rcWnd.left);
3605 cyMin
+= (rect
.bottom
- rect
.top
) + (rcWnd
.bottom
- rect
.bottom
); // == (rcWnd.bottom - rect.top);
3607 GetWindowRect(GetDlgItem(hDlg
, IDOK
), &rect
);
3608 cxMin
+= (rect
.right
- rect
.left
) + (rcWnd
.right
- rect
.right
); // == (rcWnd.right - rect.left);
3609 cyMin
+= (rect
.bottom
- rect
.top
) + (rcWnd
.bottom
- rect
.bottom
); // == (rcWnd.bottom - rect.top);
3612 * Convert the window rect from window to client coordinates
3613 * in order to retrieve the sizes of the left and top margins,
3614 * and add some extra space.
3616 MapWindowPoints(HWND_DESKTOP
/*NULL*/, hDlg
, (LPPOINT
)&rcWnd
, sizeof(RECT
)/sizeof(POINT
));
3618 cxMin
+= -2*rcWnd
.left
; // Minimal spacing between the buttons == 2 * left margin
3619 cyMin
+= -rcWnd
.top
+ 12; // Add some space on top
3621 GetClientRect(hDlg
, &rcWnd
);
3622 cxOld
= rcWnd
.right
- rcWnd
.left
;
3623 cyOld
= rcWnd
.bottom
- rcWnd
.top
;
3625 /* Show event info on dialog control */
3626 SendMessageW(hWndDetailsCtrl
, EVT_DISPLAY
, 0, 0);
3628 // SetWindowPos(hWndDetailsCtrl, NULL,
3630 // (rcWnd.right - rcWnd.left),
3631 // (rcWnd.bottom - rcWnd.top),
3632 // SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
3635 * Hide the placeholder static control and show the event details
3636 * control instead. Note that the placeholder is here so far just
3637 * to get the dimensions right in the dialog resource editor.
3638 * I plan to remove it and use a custom control with a suitable
3639 * window class for it, that would create the event details control
3642 ShowWindow(GetDlgItem(hDlg
, IDC_STATIC
), SW_HIDE
);
3643 ShowWindow(hWndDetailsCtrl
, SW_SHOW
);
3644 return (INT_PTR
)TRUE
;
3648 if (IsWindow(hWndDetailsCtrl
))
3649 DestroyWindow(hWndDetailsCtrl
);
3650 hWndDetailsCtrl
= NULL
;
3651 return (INT_PTR
)TRUE
;
3654 switch (LOWORD(wParam
))
3658 EndDialog(hDlg
, LOWORD(wParam
));
3659 return (INT_PTR
)TRUE
;
3663 L
"Help not implemented yet!",
3665 MB_OK
| MB_ICONINFORMATION
);
3666 return (INT_PTR
)TRUE
;
3674 if (((HWND
)wParam
== hWndGrip
) && (LOWORD(lParam
) == HTCLIENT
))
3676 SetCursor(LoadCursorW(NULL
, IDC_SIZENWSE
));
3677 SetWindowLongPtrW(hDlg
, DWLP_MSGRESULT
, TRUE
);
3678 return (INT_PTR
)TRUE
;
3684 /* Forbid resizing the dialog smaller than its minimal size */
3685 PRECT dragRect
= (PRECT
)lParam
;
3687 if ((wParam
== WMSZ_LEFT
) || (wParam
== WMSZ_TOPLEFT
) || (wParam
== WMSZ_BOTTOMLEFT
))
3689 if (dragRect
->right
- dragRect
->left
< cxMin
)
3690 dragRect
->left
= dragRect
->right
- cxMin
;
3693 if ((wParam
== WMSZ_RIGHT
) || (wParam
== WMSZ_TOPRIGHT
) || (wParam
== WMSZ_BOTTOMRIGHT
))
3695 if (dragRect
->right
- dragRect
->left
< cxMin
)
3696 dragRect
->right
= dragRect
->left
+ cxMin
;
3699 if ((wParam
== WMSZ_TOP
) || (wParam
== WMSZ_TOPLEFT
) || (wParam
== WMSZ_TOPRIGHT
))
3701 if (dragRect
->bottom
- dragRect
->top
< cyMin
)
3702 dragRect
->top
= dragRect
->bottom
- cyMin
;
3705 if ((wParam
== WMSZ_BOTTOM
) || (wParam
== WMSZ_BOTTOMLEFT
) || (wParam
== WMSZ_BOTTOMRIGHT
))
3707 if (dragRect
->bottom
- dragRect
->top
< cyMin
)
3708 dragRect
->bottom
= dragRect
->top
+ cyMin
;
3711 SetWindowLongPtrW(hDlg
, DWLP_MSGRESULT
, TRUE
);
3712 return (INT_PTR
)TRUE
;
3717 INT cx
= LOWORD(lParam
);
3718 INT cy
= HIWORD(lParam
);
3724 hdwp
= BeginDeferWindowPos(4);
3726 /* Resize the event details control window */
3728 hItemWnd
= hWndDetailsCtrl
;
3729 GetWindowRect(hItemWnd
, &rect
);
3730 MapWindowPoints(HWND_DESKTOP
/*NULL*/, hDlg
, (LPPOINT
)&rect
, sizeof(RECT
)/sizeof(POINT
));
3733 hdwp
= DeferWindowPos(hdwp
,
3737 (rect
.right
- rect
.left
) + (cx
- cxOld
),
3738 (rect
.bottom
- rect
.top
) + (cy
- cyOld
),
3739 SWP_NOMOVE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
3741 /* Move the buttons */
3743 hItemWnd
= GetDlgItem(hDlg
, IDHELP
);
3744 GetWindowRect(hItemWnd
, &rect
);
3745 MapWindowPoints(HWND_DESKTOP
/*NULL*/, hDlg
, (LPPOINT
)&rect
, sizeof(RECT
)/sizeof(POINT
));
3748 hdwp
= DeferWindowPos(hdwp
,
3752 rect
.top
+ (cy
- cyOld
),
3754 SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
3756 hItemWnd
= GetDlgItem(hDlg
, IDOK
);
3757 GetWindowRect(hItemWnd
, &rect
);
3758 MapWindowPoints(HWND_DESKTOP
/*NULL*/, hDlg
, (LPPOINT
)&rect
, sizeof(RECT
)/sizeof(POINT
));
3761 hdwp
= DeferWindowPos(hdwp
,
3764 rect
.left
+ (cx
- cxOld
),
3765 rect
.top
+ (cy
- cyOld
),
3767 SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
3769 /* Move the size grip */
3770 if (hWndGrip
&& hdwp
)
3772 GetWindowRect(hWndGrip
, &rect
);
3773 MapWindowPoints(HWND_DESKTOP
/*NULL*/, hDlg
, (LPPOINT
)&rect
, sizeof(RECT
)/sizeof(POINT
));
3775 hdwp
= DeferWindowPos(hdwp
,
3778 rect
.left
+ (cx
- cxOld
),
3779 rect
.top
+ (cy
- cyOld
),
3781 SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
3785 EndDeferWindowPos(hdwp
);
3787 /* Hide the size grip if we are in maximized mode */
3789 ShowWindow(hWndGrip
, (wParam
== SIZE_MAXIMIZED
) ? SW_HIDE
: SW_SHOW
);
3794 SetWindowLongPtrW(hDlg
, DWLP_MSGRESULT
, 0);
3795 return (INT_PTR
)TRUE
;
3799 return (INT_PTR
)FALSE
;