2 * ReactOS Win32 Applications
3 * Copyright (C) 2007 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 * PROJECT: ReactOS Event Log Viewer
21 * LICENSE: GPL - See COPYING in the top level directory
22 * FILE: base/applications/mscutils/eventvwr/eventvwr.c
23 * PURPOSE: Event Log Viewer main file
24 * PROGRAMMERS: Marc Piulachs (marc.piulachs at codexchange [dot] net)
26 * Hermes Belusca-Maito
30 #include "evtdetctl.h"
32 #include <sddl.h> // For ConvertSidToStringSidW
36 // #include "resource.h"
38 #define LVM_PROGRESS (WM_APP + 1) // Used by the subclassed ListView
40 static const LPCWSTR szWindowClass
= L
"EVENTVWR"; /* The main window class name */
41 static const WCHAR EVENTLOG_BASE_KEY
[] = L
"SYSTEM\\CurrentControlSet\\Services\\EventLog\\";
43 /* The 3 system logs that should always exist in the user's system */
44 static const LPCWSTR SystemLogs
[] =
51 /* MessageFile message buffer size */
52 #define EVENT_MESSAGE_EVENTTEXT_BUFFER 1024*10 // NOTE: Used by evtdetctl.c
53 #define EVENT_MESSAGE_FILE_BUFFER 1024*10
54 #define EVENT_DLL_SEPARATOR L";"
55 #define EVENT_CATEGORY_MESSAGE_FILE L"CategoryMessageFile"
56 #define EVENT_MESSAGE_FILE L"EventMessageFile"
57 #define EVENT_PARAMETER_MESSAGE_FILE L"ParameterMessageFile"
59 #define MAX_LOADSTRING 255
64 HINSTANCE hInst
; /* Current instance */
65 WCHAR szTitle
[MAX_LOADSTRING
]; /* The title bar text */
66 WCHAR szTitleTemplate
[MAX_LOADSTRING
]; /* The logged-on title bar text */
67 WCHAR szStatusBarTemplate
[MAX_LOADSTRING
]; /* The status bar text */
68 WCHAR szLoadingWait
[MAX_LOADSTRING
]; /* The "Loading, please wait..." text */
69 WCHAR szEmptyList
[MAX_LOADSTRING
]; /* The "There are no items to show in this view" text */
70 WCHAR szSaveFilter
[MAX_LOADSTRING
]; /* Filter Mask for the save Dialog */
72 INT nVSplitPos
; /* Vertical splitter (1) position */
73 INT nHSplitPos
; /* Horizontal splitter (2) position */
74 BYTE bSplit
= 0; /* Splitter state:
76 * 1: Vertical splitting;
77 * 2: Horizontal splitting.
80 HWND hwndMainWindow
= NULL
; /* Main window */
81 HWND hwndTreeView
; /* TreeView control */
82 HWND hwndListView
; /* ListView control */ // NOTE: Used by evtdetctl.c
83 HWND hwndEventDetails
; /* Event details pane */
84 HWND hwndStatus
; /* Status bar */
85 HWND hwndStatusProgress
; /* Progress bar in the status bar */
86 HMENU hMainMenu
; /* The application's main menu */
88 HTREEITEM htiSystemLogs
= NULL
, htiAppLogs
= NULL
, htiUserLogs
= NULL
;
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 LRESULT CALLBACK
WndProc(HWND
, UINT
, WPARAM
, LPARAM
);
128 INT_PTR
EventLogProperties(HINSTANCE
, HWND
, PEVENTLOGFILTER
);
129 INT_PTR CALLBACK
EventDetails(HWND
, UINT
, WPARAM
, LPARAM
);
132 /* MAIN FUNCTIONS *************************************************************/
135 ShowWin32Error(IN DWORD dwError
)
137 LPWSTR lpMessageBuffer
;
139 if (dwError
== ERROR_SUCCESS
)
142 if (!FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER
|
143 FORMAT_MESSAGE_FROM_SYSTEM
|
144 FORMAT_MESSAGE_IGNORE_INSERTS
,
148 (LPWSTR
)&lpMessageBuffer
,
154 MessageBoxW(hwndMainWindow
, lpMessageBuffer
, szTitle
, MB_OK
| MB_ICONERROR
);
155 LocalFree(lpMessageBuffer
);
159 wWinMain(HINSTANCE hInstance
,
160 HINSTANCE hPrevInstance
,
165 INITCOMMONCONTROLSEX iccx
;
170 UNREFERENCED_PARAMETER(hPrevInstance
);
171 UNREFERENCED_PARAMETER(lpCmdLine
);
173 /* Whenever any of the common controls are used in your app,
174 * you must call InitCommonControlsEx() to register the classes
175 * for those controls. */
176 iccx
.dwSize
= sizeof(iccx
);
177 iccx
.dwICC
= ICC_LISTVIEW_CLASSES
;
178 InitCommonControlsEx(&iccx
);
180 /* Load the RichEdit DLL to add support for RichEdit controls */
181 hRichEdit
= LoadLibraryW(L
"riched20.dll");
185 msg
.wParam
= (WPARAM
)-1;
187 /* Store the instance handle in the global variable */
190 /* Initialize global strings */
191 LoadStringW(hInstance
, IDS_APP_TITLE
, szTitle
, ARRAYSIZE(szTitle
));
192 LoadStringW(hInstance
, IDS_APP_TITLE_EX
, szTitleTemplate
, ARRAYSIZE(szTitleTemplate
));
193 LoadStringW(hInstance
, IDS_STATUS_MSG
, szStatusBarTemplate
, ARRAYSIZE(szStatusBarTemplate
));
194 LoadStringW(hInstance
, IDS_LOADING_WAIT
, szLoadingWait
, ARRAYSIZE(szLoadingWait
));
195 LoadStringW(hInstance
, IDS_NO_ITEMS
, szEmptyList
, ARRAYSIZE(szEmptyList
));
197 if (!MyRegisterClass(hInstance
))
200 /* Perform application initialization */
201 if (!InitInstance(hInstance
, nCmdShow
))
204 hAccelTable
= LoadAcceleratorsW(hInstance
, MAKEINTRESOURCEW(IDA_EVENTVWR
));
206 /* Create the Start/Stop enumerator thread */
207 // Manual-reset event
208 hStartStopEnumEvent
= CreateEventW(NULL
, TRUE
, FALSE
, NULL
);
209 if (!hStartStopEnumEvent
)
213 hStartEnumEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
214 if (!hStartEnumEvent
)
217 hThread
= CreateThread(NULL
, 0,
218 StartStopEnumEventsThread
,
223 /* Retrieve the available event logs on this computer and create filters for them */
224 InitializeListHead(&EventLogList
);
225 InitializeListHead(&EventLogFilterList
);
226 // TODO: Implement connection to remote computer...
227 // At the moment we only support the user local computer.
228 BuildLogListAndFilterList(NULL
);
230 // TODO: If the user wants to open an external event log with the Event Log Viewer
231 // (via the command line), it's here that the log should be opened.
233 /* Main message loop */
234 while (GetMessageW(&msg
, NULL
, 0, 0))
236 if (!TranslateAcceleratorW(hwndMainWindow
, hAccelTable
, &msg
))
238 TranslateMessage(&msg
);
239 DispatchMessageW(&msg
);
243 SetEvent(hStartStopEnumEvent
);
244 WaitForSingleObject(hThread
, INFINITE
);
245 CloseHandle(hThread
);
247 /* Free the filters list and the event logs list */
254 CloseHandle(hStartEnumEvent
);
255 if (hStartStopEnumEvent
)
256 CloseHandle(hStartStopEnumEvent
);
259 FreeLibrary(hRichEdit
);
261 return (int)msg
.wParam
;
265 /* GENERIC HELPER FUNCTIONS ***************************************************/
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 * explicitly 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 (iswdigit(*pTempMessage
))
484 while (iswdigit(*++pTempMessage
)) ;
490 while ((pTempMessage
= wcsstr(pTempMessage
, L
"%%")))
493 if (iswdigit(*pTempMessage
))
496 while (iswdigit(*++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 (iswdigit(*pTempMessage
))
528 pParamData
[i
].pStartingAddress
= pTempMessage
-1;
529 pParamData
[i
].pParameterID
= (DWORD
)_wtol(pTempMessage
);
531 while (iswdigit(*++pTempMessage
)) ;
533 pParamData
[i
].pEndingAddress
= pTempMessage
;
540 while ((pTempMessage
= wcsstr(pTempMessage
, L
"%%")) && (i
< dwParamCount
))
543 if (iswdigit(*pTempMessage
))
545 pParamData
[i
].pStartingAddress
= pTempMessage
-2;
546 pParamData
[i
].pParameterID
= (DWORD
)_wtol(pTempMessage
);
548 while (iswdigit(*++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 if (EventLog
->ComputerName
)
854 HeapFree(GetProcessHeap(), 0, EventLog
->ComputerName
);
855 HeapFree(GetProcessHeap(), 0, EventLog
);
858 StringCchCopyW(EventLog
->LogName
, cchName
, LogName
);
860 EventLog
->Permanent
= Permanent
;
866 EventLog_Free(IN PEVENTLOG EventLog
)
868 if (EventLog
->LogName
)
869 HeapFree(GetProcessHeap(), 0, EventLog
->LogName
);
871 if (EventLog
->ComputerName
)
872 HeapFree(GetProcessHeap(), 0, EventLog
->ComputerName
);
874 if (EventLog
->FileName
)
875 HeapFree(GetProcessHeap(), 0, EventLog
->FileName
);
877 HeapFree(GetProcessHeap(), 0, EventLog
);
882 AllocAndCopyMultiStr(IN PCWSTR MultiStr OPTIONAL
)
890 pStr
= (PWSTR
)MultiStr
;
891 while (*pStr
) pStr
+= (wcslen(pStr
) + 1);
892 Length
= MultiStr
- pStr
+ 2;
894 pStr
= HeapAlloc(GetProcessHeap(), 0, Length
* sizeof(WCHAR
));
895 // NOTE: If we failed allocating the string, then fall back into no filter!
897 RtlCopyMemory(pStr
, MultiStr
, Length
* sizeof(WCHAR
));
903 AllocEventLogFilter(// IN PCWSTR FilterName,
907 IN BOOL AuditSuccess
,
908 IN BOOL AuditFailure
,
909 IN PCWSTR Sources OPTIONAL
,
910 IN PCWSTR Users OPTIONAL
,
911 IN PCWSTR ComputerNames OPTIONAL
,
912 IN ULONG NumOfEventLogs
,
913 IN PEVENTLOG
* EventLogs
)
915 PEVENTLOGFILTER EventLogFilter
;
917 /* Allocate a new event log filter entry, big enough to accommodate the list of logs */
918 EventLogFilter
= HeapAlloc(GetProcessHeap(),
920 FIELD_OFFSET(EVENTLOGFILTER
, EventLogs
[NumOfEventLogs
]));
924 EventLogFilter
->Information
= Information
;
925 EventLogFilter
->Warning
= Warning
;
926 EventLogFilter
->Error
= Error
;
927 EventLogFilter
->AuditSuccess
= AuditSuccess
;
928 EventLogFilter
->AuditFailure
= AuditFailure
;
930 /* Allocate and copy the sources, users, and computers multi-strings */
931 EventLogFilter
->Sources
= AllocAndCopyMultiStr(Sources
);
932 EventLogFilter
->Users
= AllocAndCopyMultiStr(Users
);
933 EventLogFilter
->ComputerNames
= AllocAndCopyMultiStr(ComputerNames
);
935 /* Copy the list of event logs */
936 EventLogFilter
->NumOfEventLogs
= NumOfEventLogs
;
937 RtlCopyMemory(EventLogFilter
->EventLogs
, EventLogs
, NumOfEventLogs
* sizeof(PEVENTLOG
));
939 /* Initialize the filter reference count */
940 EventLogFilter
->ReferenceCount
= 1;
942 return EventLogFilter
;
946 EventLogFilter_Free(IN PEVENTLOGFILTER EventLogFilter
)
948 if (EventLogFilter
->Sources
)
949 HeapFree(GetProcessHeap(), 0, EventLogFilter
->Sources
);
951 if (EventLogFilter
->Users
)
952 HeapFree(GetProcessHeap(), 0, EventLogFilter
->Users
);
954 if (EventLogFilter
->ComputerNames
)
955 HeapFree(GetProcessHeap(), 0, EventLogFilter
->ComputerNames
);
957 HeapFree(GetProcessHeap(), 0, EventLogFilter
);
960 LONG
EventLogFilter_AddRef(IN PEVENTLOGFILTER EventLogFilter
)
962 ASSERT(EventLogFilter
);
963 return InterlockedIncrement(&EventLogFilter
->ReferenceCount
);
966 LONG
EventLogFilter_Release(IN PEVENTLOGFILTER EventLogFilter
)
970 ASSERT(EventLogFilter
);
972 /* When the reference count reaches zero, delete the filter */
973 RefCount
= InterlockedDecrement(&EventLogFilter
->ReferenceCount
);
976 /* Remove the filter from the list */
977 /** RemoveEntryList(&EventLogFilter->ListEntry); **/
978 EventLogFilter_Free(EventLogFilter
);
991 c
= s
+ wcslen(s
) - 1;
992 while (c
>= s
&& iswspace(*c
))
999 GetEventMessageFileDLL(IN LPCWSTR lpLogName
,
1000 IN LPCWSTR SourceName
,
1001 IN LPCWSTR EntryName
,
1002 OUT PWCHAR lpModuleName
) // TODO: Add IN DWORD BufLen
1004 BOOL Success
= FALSE
;
1007 WCHAR szModuleName
[MAX_PATH
];
1008 WCHAR szKeyName
[MAX_PATH
];
1009 HKEY hLogKey
= NULL
;
1010 HKEY hSourceKey
= NULL
;
1012 StringCbCopyW(szKeyName
, sizeof(szKeyName
), EVENTLOG_BASE_KEY
);
1013 StringCbCatW(szKeyName
, sizeof(szKeyName
), lpLogName
);
1015 Result
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
,
1020 if (Result
!= ERROR_SUCCESS
)
1023 Result
= RegOpenKeyExW(hLogKey
,
1028 if (Result
!= ERROR_SUCCESS
)
1030 RegCloseKey(hLogKey
);
1034 dwSize
= sizeof(szModuleName
);
1035 Result
= RegQueryValueExW(hSourceKey
,
1039 (LPBYTE
)szModuleName
,
1041 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_EXPAND_SZ
&& Type
!= REG_SZ
))
1043 szModuleName
[0] = UNICODE_NULL
;
1047 /* NULL-terminate the string and expand it */
1048 szModuleName
[dwSize
/ sizeof(WCHAR
) - 1] = UNICODE_NULL
;
1049 ExpandEnvironmentStringsW(szModuleName
, lpModuleName
, ARRAYSIZE(szModuleName
));
1053 RegCloseKey(hSourceKey
);
1054 RegCloseKey(hLogKey
);
1060 GetEventCategory(IN LPCWSTR KeyName
,
1061 IN LPCWSTR SourceName
,
1062 IN PEVENTLOGRECORD pevlr
,
1063 OUT PWCHAR CategoryName
) // TODO: Add IN DWORD BufLen
1065 BOOL Success
= FALSE
;
1066 WCHAR szMessageDLL
[MAX_PATH
];
1067 LPWSTR lpMsgBuf
= NULL
;
1069 if (!GetEventMessageFileDLL(KeyName
, SourceName
, EVENT_CATEGORY_MESSAGE_FILE
, szMessageDLL
))
1072 /* Retrieve the message string without appending extra newlines */
1074 GetMessageStringFromDllList(szMessageDLL
,
1075 FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_HMODULE
|
1076 FORMAT_MESSAGE_IGNORE_INSERTS
| FORMAT_MESSAGE_MAX_WIDTH_MASK
,
1077 pevlr
->EventCategory
,
1078 EVENT_MESSAGE_FILE_BUFFER
,
1082 /* Trim the string */
1083 TrimNulls(lpMsgBuf
);
1085 /* Copy the category name */
1086 StringCchCopyW(CategoryName
, MAX_PATH
, lpMsgBuf
);
1088 /* Free the buffer allocated by FormatMessage */
1089 LocalFree(lpMsgBuf
);
1091 /* The ID was found and the message was formatted */
1098 if (pevlr
->EventCategory
!= 0)
1100 StringCchPrintfW(CategoryName
, MAX_PATH
, L
"(%lu)", pevlr
->EventCategory
);
1109 BOOL
// NOTE: Used by evtdetctl.c
1110 GetEventMessage(IN LPCWSTR KeyName
,
1111 IN LPCWSTR SourceName
,
1112 IN PEVENTLOGRECORD pevlr
,
1113 OUT PWCHAR EventText
) // TODO: Add IN DWORD BufLen
1115 BOOL Success
= FALSE
;
1118 WCHAR SourceModuleName
[1024];
1119 WCHAR ParameterModuleName
[1024];
1120 BOOL IsParamModNameCached
= FALSE
;
1121 LPWSTR lpMsgBuf
= NULL
;
1122 LPWSTR szStringArray
, szMessage
;
1123 LPWSTR
*szArguments
;
1125 /* Get the event string array */
1126 szStringArray
= (LPWSTR
)((LPBYTE
)pevlr
+ pevlr
->StringOffset
);
1128 /* NOTE: GetEventMessageFileDLL can return a comma-separated list of DLLs */
1129 if (!GetEventMessageFileDLL(KeyName
, SourceName
, EVENT_MESSAGE_FILE
, SourceModuleName
))
1132 /* Allocate space for insertion strings */
1133 szArguments
= HeapAlloc(GetProcessHeap(), 0, pevlr
->NumStrings
* sizeof(LPVOID
));
1137 if (!IsParamModNameCached
)
1139 /* Now that the parameter file list is loaded, no need to reload it at the next run! */
1140 IsParamModNameCached
= GetEventMessageFileDLL(KeyName
, SourceName
, EVENT_PARAMETER_MESSAGE_FILE
, ParameterModuleName
);
1141 // FIXME: If the string loading failed the first time, no need to retry it just after???
1144 if (IsParamModNameCached
)
1146 /* Not yet support for reading messages from parameter message DLL */
1149 szMessage
= szStringArray
;
1152 * We do some hackish preformatting of the cached event strings...
1153 * That's because after we pass the string to FormatMessage
1154 * (via GetMessageStringFromDllList) with the FORMAT_MESSAGE_ARGUMENT_ARRAY
1155 * flag, instead of ignoring the insertion parameters and do the formatting
1156 * by ourselves. Therefore, the resulting string should have the parameter
1157 * string placeholders starting with a single '%' instead of a mix of one
1160 /* HACK part 1: Compute the full length of the string array */
1162 for (i
= 0; i
< pevlr
->NumStrings
; i
++)
1164 szMessage
+= wcslen(szMessage
) + 1;
1166 cch
= szMessage
- szStringArray
;
1168 /* HACK part 2: Now do the HACK proper! */
1169 szMessage
= szStringArray
;
1170 for (i
= 0; i
< pevlr
->NumStrings
; i
++)
1172 lpMsgBuf
= szMessage
;
1173 while ((lpMsgBuf
= wcsstr(lpMsgBuf
, L
"%%")))
1175 if (iswdigit(lpMsgBuf
[2]))
1177 RtlMoveMemory(lpMsgBuf
, lpMsgBuf
+1, ((szStringArray
+ cch
) - lpMsgBuf
- 1) * sizeof(WCHAR
));
1181 szArguments
[i
] = szMessage
;
1182 szMessage
+= wcslen(szMessage
) + 1;
1185 /* Retrieve the message string without appending extra newlines */
1187 GetMessageStringFromDllList(SourceModuleName
,
1188 FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_HMODULE
|
1189 FORMAT_MESSAGE_ARGUMENT_ARRAY
| FORMAT_MESSAGE_MAX_WIDTH_MASK
,
1192 (va_list*)szArguments
);
1195 /* Trim the string */
1196 TrimNulls(lpMsgBuf
);
1199 Success
= (ApplyParameterStringsToMessage(ParameterModuleName
,
1202 &szMessage
) == ERROR_SUCCESS
);
1203 if (Success
&& szMessage
)
1205 /* Free the buffer allocated by FormatMessage */
1206 LocalFree(lpMsgBuf
);
1207 lpMsgBuf
= szMessage
;
1210 /* Copy the event text */
1211 StringCchCopyW(EventText
, EVENT_MESSAGE_EVENTTEXT_BUFFER
, lpMsgBuf
);
1213 /* Free the buffer allocated by FormatMessage */
1214 LocalFree(lpMsgBuf
);
1217 HeapFree(GetProcessHeap(), 0, szArguments
);
1222 /* Get a read-only pointer to the "event-not-found" string */
1223 lpMsgBuf
= HeapAlloc(GetProcessHeap(), 0, EVENT_MESSAGE_EVENTTEXT_BUFFER
* sizeof(WCHAR
));
1224 LoadStringW(hInst
, IDS_EVENTSTRINGIDNOTFOUND
, lpMsgBuf
, EVENT_MESSAGE_EVENTTEXT_BUFFER
);
1225 StringCchPrintfW(EventText
, EVENT_MESSAGE_EVENTTEXT_BUFFER
, lpMsgBuf
, (pevlr
->EventID
& 0xFFFF), SourceName
);
1227 /* Append the strings */
1228 szMessage
= szStringArray
;
1229 for (i
= 0; i
< pevlr
->NumStrings
; i
++)
1231 StringCchCatW(EventText
, EVENT_MESSAGE_EVENTTEXT_BUFFER
, szMessage
);
1232 StringCchCatW(EventText
, EVENT_MESSAGE_EVENTTEXT_BUFFER
, L
"\n");
1233 szMessage
+= wcslen(szMessage
) + 1;
1241 GetEventType(IN WORD dwEventType
,
1242 OUT PWCHAR eventTypeText
) // TODO: Add IN DWORD BufLen
1244 switch (dwEventType
)
1246 case EVENTLOG_ERROR_TYPE
:
1247 LoadStringW(hInst
, IDS_EVENTLOG_ERROR_TYPE
, eventTypeText
, MAX_LOADSTRING
);
1249 case EVENTLOG_WARNING_TYPE
:
1250 LoadStringW(hInst
, IDS_EVENTLOG_WARNING_TYPE
, eventTypeText
, MAX_LOADSTRING
);
1252 case EVENTLOG_INFORMATION_TYPE
:
1253 LoadStringW(hInst
, IDS_EVENTLOG_INFORMATION_TYPE
, eventTypeText
, MAX_LOADSTRING
);
1255 case EVENTLOG_SUCCESS
:
1256 LoadStringW(hInst
, IDS_EVENTLOG_SUCCESS
, eventTypeText
, MAX_LOADSTRING
);
1258 case EVENTLOG_AUDIT_SUCCESS
:
1259 LoadStringW(hInst
, IDS_EVENTLOG_AUDIT_SUCCESS
, eventTypeText
, MAX_LOADSTRING
);
1261 case EVENTLOG_AUDIT_FAILURE
:
1262 LoadStringW(hInst
, IDS_EVENTLOG_AUDIT_FAILURE
, eventTypeText
, MAX_LOADSTRING
);
1265 LoadStringW(hInst
, IDS_EVENTLOG_UNKNOWN_TYPE
, eventTypeText
, MAX_LOADSTRING
);
1271 GetEventUserName(IN PEVENTLOGRECORD pelr
,
1272 IN OUT PSID
*pLastSid
,
1273 OUT PWCHAR pszUser
) // TODO: Add IN DWORD BufLen
1278 WCHAR szDomain
[1024];
1280 DWORD cchName
= ARRAYSIZE(szName
);
1281 DWORD cchDomain
= ARRAYSIZE(szDomain
);
1282 BOOL Success
= FALSE
;
1284 /* Point to the SID */
1285 pCurrentSid
= (PSID
)((LPBYTE
)pelr
+ pelr
->UserSidOffset
);
1287 if (!IsValidSid(pCurrentSid
))
1292 else if (*pLastSid
&& EqualSid(*pLastSid
, pCurrentSid
))
1298 if (pelr
->UserSidLength
> 0)
1301 * Try to retrieve the user account name and domain name corresponding
1302 * to the SID. If it cannot be retrieved, try to convert the SID to a
1303 * string-form. It should not be bigger than the user-provided buffer
1304 * 'pszUser', otherwise we return an error.
1306 if (LookupAccountSidW(NULL
, // FIXME: Use computer name? From the particular event?
1314 StringCchCopyW(pszUser
, MAX_PATH
, szName
);
1317 else if (ConvertSidToStringSidW(pCurrentSid
, &StringSid
))
1319 /* Copy the string only if the user-provided buffer is big enough */
1320 if (wcslen(StringSid
) + 1 <= MAX_PATH
) // + 1 for NULL-terminator
1322 StringCchCopyW(pszUser
, MAX_PATH
, StringSid
);
1327 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1331 /* Free the allocated buffer */
1332 LocalFree(StringSid
);
1336 *pLastSid
= Success
? pCurrentSid
: NULL
;
1342 static VOID
FreeRecords(VOID
)
1349 for (iIndex
= 0; iIndex
< g_TotalRecords
; iIndex
++)
1351 if (g_RecordPtrs
[iIndex
])
1352 HeapFree(GetProcessHeap(), 0, g_RecordPtrs
[iIndex
]);
1354 HeapFree(GetProcessHeap(), 0, g_RecordPtrs
);
1355 g_RecordPtrs
= NULL
;
1360 FilterByType(IN PEVENTLOGFILTER EventLogFilter
,
1361 IN PEVENTLOGRECORD pevlr
)
1363 if ((pevlr
->EventType
== EVENTLOG_SUCCESS
&& !EventLogFilter
->Information
) ||
1364 (pevlr
->EventType
== EVENTLOG_INFORMATION_TYPE
&& !EventLogFilter
->Information
) ||
1365 (pevlr
->EventType
== EVENTLOG_WARNING_TYPE
&& !EventLogFilter
->Warning
) ||
1366 (pevlr
->EventType
== EVENTLOG_ERROR_TYPE
&& !EventLogFilter
->Error
) ||
1367 (pevlr
->EventType
== EVENTLOG_AUDIT_SUCCESS
&& !EventLogFilter
->AuditSuccess
) ||
1368 (pevlr
->EventType
== EVENTLOG_AUDIT_FAILURE
&& !EventLogFilter
->AuditFailure
))
1376 FilterByString(IN PCWSTR FilterString
, // This is a multi-string
1381 /* The filter string is NULL so it does not filter anything */
1386 * If the filter string filters for an empty string AND the source string
1387 * is an empty string, we have a match (particular case of the last one).
1389 if (!*FilterString
&& !*String
)
1392 // if (*FilterString || *String)
1395 * If the filter string is empty BUT the source string is not empty,
1396 * OR vice-versa, we cannot have a match.
1398 if ( (!*FilterString
&& *String
) || (*FilterString
&& !*String
) )
1402 * If the filter string filters for at least a non-empty string,
1403 * browse it and search for a string that matches the source string.
1405 // else if (*FilterString && *String)
1407 pStr
= FilterString
;
1410 if (wcsicmp(pStr
, String
) == 0)
1412 /* We have a match, break the loop */
1416 pStr
+= (wcslen(pStr
) + 1);
1418 if (!*pStr
) // && *String
1420 /* We do not have a match */
1425 /* We have a match */
1430 * The events enumerator thread.
1433 EnumEventsThread(IN LPVOID lpParameter
)
1435 PEVENTLOGFILTER EventLogFilter
= (PEVENTLOGFILTER
)lpParameter
;
1436 LPWSTR lpMachineName
= NULL
; // EventLogFilter->ComputerName;
1441 PEVENTLOGRECORD pEvlr
= NULL
;
1444 DWORD dwWanted
, dwRead
, dwNeeded
, dwStatus
= ERROR_SUCCESS
;
1445 DWORD dwTotalRecords
= 0, dwCurrentRecord
= 0;
1446 DWORD dwFlags
, dwMaxLength
;
1447 size_t cchRemaining
;
1448 LPWSTR lpszSourceName
;
1449 LPWSTR lpszComputerName
;
1450 BOOL bResult
= TRUE
; /* Read succeeded */
1451 HANDLE hProcessHeap
= GetProcessHeap();
1452 PSID pLastSid
= NULL
;
1454 UINT uStep
= 0, uStepAt
= 0, uPos
= 0;
1456 WCHAR szWindowTitle
[MAX_PATH
];
1457 WCHAR szStatusText
[MAX_PATH
];
1458 WCHAR szLocalDate
[MAX_PATH
];
1459 WCHAR szLocalTime
[MAX_PATH
];
1460 WCHAR szEventID
[MAX_PATH
];
1461 WCHAR szEventTypeText
[MAX_LOADSTRING
];
1462 WCHAR szCategoryID
[MAX_PATH
];
1463 WCHAR szUsername
[MAX_PATH
];
1464 WCHAR szNoUsername
[MAX_PATH
];
1465 WCHAR szCategory
[MAX_PATH
];
1466 WCHAR szNoCategory
[MAX_PATH
];
1467 PWCHAR lpTitleTemplateEnd
;
1470 LVITEMW lviEventItem
;
1472 /* Save the current event log filter globally */
1473 EventLogFilter_AddRef(EventLogFilter
);
1474 ActiveFilter
= EventLogFilter
;
1476 /* Disable list view redraw */
1477 SendMessageW(hwndListView
, WM_SETREDRAW
, FALSE
, 0);
1479 /* Clear the list view and free the cached records */
1480 ListView_DeleteAllItems(hwndListView
);
1483 SendMessageW(hwndListView
, LVM_PROGRESS
, 0, TRUE
);
1484 ProgressBar_SetRange(hwndStatusProgress
, 0);
1485 StatusBar_SetText(hwndStatus
, 0, NULL
);
1486 ShowWindow(hwndStatusProgress
, SW_SHOW
);
1488 /* Do a loop over the logs enumerated in the filter */
1489 // FIXME: For now we only support 1 event log per filter!
1491 // for (LogIndex = 0; LogIndex < EventLogFilter->NumOfEventLogs; ++LogIndex)
1494 EventLog
= EventLogFilter
->EventLogs
[LogIndex
];
1496 /* Open the event log */
1497 if (EventLog
->Permanent
)
1498 hEventLog
= OpenEventLogW(EventLog
->ComputerName
, EventLog
->LogName
);
1500 hEventLog
= OpenBackupEventLogW(EventLog
->ComputerName
, EventLog
->LogName
); // FileName
1502 if (hEventLog
== NULL
)
1504 ShowWin32Error(GetLastError());
1508 // GetOldestEventLogRecord(hEventLog, &dwThisRecord);
1510 /* Get the total number of event log records */
1511 GetNumberOfEventLogRecords(hEventLog
, &dwTotalRecords
);
1513 if (dwTotalRecords
> 0)
1515 EnableMenuItem(hMainMenu
, IDM_CLEAR_EVENTS
, MF_BYCOMMAND
| MF_ENABLED
);
1516 EnableMenuItem(hMainMenu
, IDM_SAVE_EVENTLOG
, MF_BYCOMMAND
| MF_ENABLED
);
1520 EnableMenuItem(hMainMenu
, IDM_CLEAR_EVENTS
, MF_BYCOMMAND
| MF_GRAYED
);
1521 EnableMenuItem(hMainMenu
, IDM_SAVE_EVENTLOG
, MF_BYCOMMAND
| MF_GRAYED
);
1524 /* Set up the event records cache */
1525 g_RecordPtrs
= HeapAlloc(hProcessHeap
, HEAP_ZERO_MEMORY
, dwTotalRecords
* sizeof(*g_RecordPtrs
));
1528 // ShowWin32Error(GetLastError());
1531 g_TotalRecords
= dwTotalRecords
;
1533 if (WaitForSingleObject(hStopEnumEvent
, 0) == WAIT_OBJECT_0
)
1536 LoadStringW(hInst
, IDS_NOT_AVAILABLE
, szNoUsername
, ARRAYSIZE(szNoUsername
));
1537 LoadStringW(hInst
, IDS_NONE
, szNoCategory
, ARRAYSIZE(szNoCategory
));
1539 ProgressBar_SetRange(hwndStatusProgress
, MAKELPARAM(0, 100));
1540 uStepAt
= (dwTotalRecords
/ 100) + 1;
1542 dwFlags
= EVENTLOG_SEQUENTIAL_READ
| (NewestEventsFirst
? EVENTLOG_FORWARDS_READ
: EVENTLOG_BACKWARDS_READ
);
1544 /* 0x7ffff is the maximum buffer size ReadEventLog will accept */
1546 pEvlr
= HeapAlloc(hProcessHeap
, 0, dwWanted
);
1551 while (dwStatus
== ERROR_SUCCESS
)
1553 bResult
= ReadEventLogW(hEventLog
, dwFlags
, 0, pEvlr
, dwWanted
, &dwRead
, &dwNeeded
);
1554 dwStatus
= GetLastError();
1556 if (!bResult
&& dwStatus
== ERROR_INSUFFICIENT_BUFFER
)
1558 pEvlr
= HeapReAlloc(hProcessHeap
, 0, pEvlr
, dwNeeded
);
1559 dwWanted
= dwNeeded
;
1564 bResult
= ReadEventLogW(hEventLog
, dwFlags
, 0, pEvlr
, dwNeeded
, &dwRead
, &dwNeeded
);
1571 /* exit on other errors (ERROR_HANDLE_EOF) */
1575 pEvlrBuffer
= (LPBYTE
)pEvlr
;
1576 pEvlrEnd
= pEvlrBuffer
+ dwRead
;
1578 while (pEvlrBuffer
< pEvlrEnd
)
1580 PEVENTLOGRECORD pEvlrTmp
= (PEVENTLOGRECORD
)pEvlrBuffer
;
1581 PWSTR lpszUsername
, lpszCategoryName
;
1582 g_RecordPtrs
[dwCurrentRecord
] = NULL
;
1584 // ProgressBar_StepIt(hwndStatusProgress);
1586 if (uStep
% uStepAt
== 0)
1589 ProgressBar_SetPos(hwndStatusProgress
, uPos
);
1592 if (WaitForSingleObject(hStopEnumEvent
, 0) == WAIT_OBJECT_0
)
1595 /* Filter by event type */
1596 if (!FilterByType(EventLogFilter
, pEvlrTmp
))
1599 /* Get the event source name and filter it */
1600 lpszSourceName
= (LPWSTR
)(pEvlrBuffer
+ sizeof(EVENTLOGRECORD
));
1601 if (!FilterByString(EventLogFilter
->Sources
, lpszSourceName
))
1604 /* Get the computer name and filter it */
1605 lpszComputerName
= (LPWSTR
)(pEvlrBuffer
+ sizeof(EVENTLOGRECORD
) + (wcslen(lpszSourceName
) + 1) * sizeof(WCHAR
));
1606 if (!FilterByString(EventLogFilter
->ComputerNames
, lpszComputerName
))
1609 /* Compute the event time */
1610 EventTimeToSystemTime(pEvlrTmp
->TimeWritten
, &time
);
1611 GetDateFormatW(LOCALE_USER_DEFAULT
, DATE_SHORTDATE
, &time
, NULL
, szLocalDate
, ARRAYSIZE(szLocalDate
));
1612 GetTimeFormatW(LOCALE_USER_DEFAULT
, 0, &time
, NULL
, szLocalTime
, ARRAYSIZE(szLocalTime
));
1614 /* Get the username that generated the event, and filter it */
1615 lpszUsername
= GetEventUserName(pEvlrTmp
, &pLastSid
, szUsername
) ? szUsername
: szNoUsername
;
1617 if (!FilterByString(EventLogFilter
->Users
, lpszUsername
))
1620 // TODO: Filter by event ID and category
1621 GetEventType(pEvlrTmp
->EventType
, szEventTypeText
);
1623 lpszCategoryName
= GetEventCategory(EventLog
->LogName
, lpszSourceName
, pEvlrTmp
, szCategory
) ? szCategory
: szNoCategory
;
1625 StringCbPrintfW(szEventID
, sizeof(szEventID
), L
"%u", (pEvlrTmp
->EventID
& 0xFFFF));
1626 StringCbPrintfW(szCategoryID
, sizeof(szCategoryID
), L
"%u", pEvlrTmp
->EventCategory
);
1628 g_RecordPtrs
[dwCurrentRecord
] = HeapAlloc(hProcessHeap
, 0, pEvlrTmp
->Length
);
1629 RtlCopyMemory(g_RecordPtrs
[dwCurrentRecord
], pEvlrTmp
, pEvlrTmp
->Length
);
1631 lviEventItem
.mask
= LVIF_IMAGE
| LVIF_TEXT
| LVIF_PARAM
;
1632 lviEventItem
.iItem
= 0;
1633 lviEventItem
.iSubItem
= 0;
1634 lviEventItem
.lParam
= (LPARAM
)g_RecordPtrs
[dwCurrentRecord
];
1635 lviEventItem
.pszText
= szEventTypeText
;
1637 switch (pEvlrTmp
->EventType
)
1639 case EVENTLOG_SUCCESS
:
1640 case EVENTLOG_INFORMATION_TYPE
:
1641 lviEventItem
.iImage
= 0;
1644 case EVENTLOG_WARNING_TYPE
:
1645 lviEventItem
.iImage
= 1;
1648 case EVENTLOG_ERROR_TYPE
:
1649 lviEventItem
.iImage
= 2;
1652 case EVENTLOG_AUDIT_SUCCESS
:
1653 lviEventItem
.iImage
= 3;
1656 case EVENTLOG_AUDIT_FAILURE
:
1657 lviEventItem
.iImage
= 4;
1661 lviEventItem
.iItem
= ListView_InsertItem(hwndListView
, &lviEventItem
);
1663 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 1, szLocalDate
);
1664 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 2, szLocalTime
);
1665 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 3, lpszSourceName
);
1666 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 4, lpszCategoryName
);
1667 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 5, szEventID
);
1668 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 6, lpszUsername
);
1669 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 7, lpszComputerName
);
1672 pEvlrBuffer
+= pEvlrTmp
->Length
;
1680 HeapFree(hProcessHeap
, 0, pEvlr
);
1682 /* Close the event log */
1683 CloseEventLog(hEventLog
);
1685 } // end-for (LogIndex)
1687 /* All events loaded */
1691 ShowWindow(hwndStatusProgress
, SW_HIDE
);
1692 SendMessageW(hwndListView
, LVM_PROGRESS
, 0, FALSE
);
1694 // FIXME: Use something else instead of EventLog->LogName !!
1697 * Use a different formatting, whether the event log filter holds
1698 * only one log, or many logs (the latter case is WIP TODO!)
1700 if (EventLogFilter
->NumOfEventLogs
<= 1)
1702 StringCchPrintfExW(szWindowTitle
,
1703 ARRAYSIZE(szWindowTitle
),
1704 &lpTitleTemplateEnd
,
1707 szTitleTemplate
, szTitle
, EventLog
->LogName
); /* i = number of characters written */
1708 dwMaxLength
= (DWORD
)cchRemaining
;
1710 GetComputerNameW(lpTitleTemplateEnd
, &dwMaxLength
);
1712 StringCchCopyW(lpTitleTemplateEnd
, dwMaxLength
, lpMachineName
);
1714 StringCbPrintfW(szStatusText
,
1715 sizeof(szStatusText
),
1716 szStatusBarTemplate
,
1719 ListView_GetItemCount(hwndListView
));
1723 // TODO: Use a different title & implement filtering for multi-log filters !!
1724 // (EventLogFilter->NumOfEventLogs > 1)
1725 MessageBoxW(hwndMainWindow
,
1726 L
"Many-logs filtering is not implemented yet!!",
1728 MB_OK
| MB_ICONINFORMATION
);
1731 /* Update the status bar */
1732 StatusBar_SetText(hwndStatus
, 0, szStatusText
);
1734 /* Set the window title */
1735 SetWindowTextW(hwndMainWindow
, szWindowTitle
);
1737 /* Resume list view redraw */
1738 SendMessageW(hwndListView
, WM_SETREDRAW
, TRUE
, 0);
1740 EventLogFilter_Release(EventLogFilter
);
1742 CloseHandle(hStopEnumEvent
);
1743 InterlockedExchangePointer((PVOID
*)&hStopEnumEvent
, NULL
);
1749 * The purpose of this thread is to serialize the creation of the events
1750 * enumeration thread, since the Event Log Viewer currently only supports
1751 * one view, one event list, one enumeration.
1754 StartStopEnumEventsThread(IN LPVOID lpParameter
)
1756 HANDLE WaitHandles
[2];
1759 WaitHandles
[0] = hStartStopEnumEvent
; // End-of-application event
1760 WaitHandles
[1] = hStartEnumEvent
; // Command event
1764 WaitResult
= WaitForMultipleObjects(ARRAYSIZE(WaitHandles
),
1770 case WAIT_OBJECT_0
+ 0:
1772 /* End-of-application event signaled, quit this thread */
1774 /* Stop the previous enumeration */
1775 if (hEnumEventsThread
)
1779 SetEvent(hStopEnumEvent
);
1780 WaitForSingleObject(hEnumEventsThread
, INFINITE
);
1781 // NOTE: The following is done by the enumeration thread just before terminating.
1782 // hStopEnumEvent = NULL;
1785 CloseHandle(hEnumEventsThread
);
1786 hEnumEventsThread
= NULL
;
1789 /* Clear the list view and free the cached records */
1790 ListView_DeleteAllItems(hwndListView
);
1793 /* Reset the active filter */
1794 ActiveFilter
= NULL
;
1799 case WAIT_OBJECT_0
+ 1:
1801 /* Restart a new enumeration if needed */
1802 PEVENTLOGFILTER EventLogFilter
;
1804 /* Stop the previous enumeration */
1805 if (hEnumEventsThread
)
1809 SetEvent(hStopEnumEvent
);
1810 WaitForSingleObject(hEnumEventsThread
, INFINITE
);
1811 // NOTE: The following is done by the enumeration thread just before terminating.
1812 // hStopEnumEvent = NULL;
1815 CloseHandle(hEnumEventsThread
);
1816 hEnumEventsThread
= NULL
;
1819 /* Clear the list view and free the cached records */
1820 ListView_DeleteAllItems(hwndListView
);
1823 /* Reset the active filter */
1824 ActiveFilter
= NULL
;
1826 EventLogFilter
= InterlockedExchangePointer((PVOID
*)&EnumFilter
, NULL
);
1827 if (!EventLogFilter
)
1830 // Manual-reset event
1831 hStopEnumEvent
= CreateEventW(NULL
, TRUE
, FALSE
, NULL
);
1832 if (!hStopEnumEvent
)
1835 hEnumEventsThread
= CreateThread(NULL
,
1838 (LPVOID
)EventLogFilter
,
1841 if (!hEnumEventsThread
)
1843 CloseHandle(hStopEnumEvent
);
1844 hStopEnumEvent
= NULL
;
1847 // CloseHandle(hEnumEventsThread);
1848 ResumeThread(hEnumEventsThread
);
1855 /* Unknown command, must never go there! */
1856 return GetLastError();
1865 EnumEvents(IN PEVENTLOGFILTER EventLogFilter
)
1867 /* Signal the enumerator thread we want to enumerate events */
1868 InterlockedExchangePointer((PVOID
*)&EnumFilter
, EventLogFilter
);
1869 SetEvent(hStartEnumEvent
);
1875 GetSelectedFilter(OUT HTREEITEM
* phti OPTIONAL
)
1883 /* Get index of selected item */
1884 hti
= TreeView_GetSelection(hwndTreeView
);
1886 return NULL
; // No filter
1888 tvItemEx
.mask
= TVIF_PARAM
;
1889 tvItemEx
.hItem
= hti
;
1891 TreeView_GetItem(hwndTreeView
, &tvItemEx
);
1894 *phti
= tvItemEx
.hItem
;
1896 return (PEVENTLOGFILTER
)tvItemEx
.lParam
;
1901 OpenUserEventLogFile(IN LPCWSTR lpszFileName
)
1903 WIN32_FIND_DATAW FindData
;
1906 PEVENTLOGFILTER EventLogFilter
;
1908 HTREEITEM hItem
= NULL
;
1910 /* Check whether the file actually exists */
1911 hFind
= FindFirstFileW(lpszFileName
, &FindData
);
1912 if (hFind
== INVALID_HANDLE_VALUE
)
1914 ShowWin32Error(GetLastError());
1919 /* Allocate a new event log entry */
1920 EventLog
= AllocEventLog(NULL
, lpszFileName
, FALSE
);
1921 if (EventLog
== NULL
)
1923 ShowWin32Error(ERROR_NOT_ENOUGH_MEMORY
);
1927 /* Allocate a new event log filter entry for this event log */
1928 EventLogFilter
= AllocEventLogFilter(// LogName,
1929 TRUE
, TRUE
, TRUE
, TRUE
, TRUE
,
1932 if (EventLogFilter
== NULL
)
1934 ShowWin32Error(ERROR_NOT_ENOUGH_MEMORY
);
1935 EventLog_Free(EventLog
);
1939 /* Add the event log and the filter into their lists */
1940 InsertTailList(&EventLogList
, &EventLog
->ListEntry
);
1941 InsertTailList(&EventLogFilterList
, &EventLogFilter
->ListEntry
);
1943 /* Retrieve and cache the event log file */
1944 cchFileName
= wcslen(lpszFileName
) + 1;
1945 EventLog
->FileName
= HeapAlloc(GetProcessHeap(), 0, cchFileName
* sizeof(WCHAR
));
1946 if (EventLog
->FileName
)
1947 StringCchCopyW(EventLog
->FileName
, cchFileName
, lpszFileName
);
1949 hItem
= TreeViewAddItem(hwndTreeView
, htiUserLogs
,
1950 (LPWSTR
)lpszFileName
,
1951 2, 3, (LPARAM
)EventLogFilter
);
1953 /* Select the event log */
1956 // TreeView_Expand(hwndTreeView, htiUserLogs, TVE_EXPAND);
1957 TreeView_SelectItem(hwndTreeView
, hItem
);
1958 TreeView_EnsureVisible(hwndTreeView
, hItem
);
1960 InvalidateRect(hwndTreeView
, NULL
, FALSE
);
1961 SetFocus(hwndTreeView
);
1965 OpenUserEventLog(VOID
)
1967 WCHAR szFileName
[MAX_PATH
];
1969 ZeroMemory(szFileName
, sizeof(szFileName
));
1971 sfn
.lpstrFile
= szFileName
;
1972 sfn
.nMaxFile
= ARRAYSIZE(szFileName
);
1974 if (!GetOpenFileNameW(&sfn
))
1976 sfn
.lpstrFile
[sfn
.nMaxFile
-1] = UNICODE_NULL
;
1978 OpenUserEventLogFile(sfn
.lpstrFile
);
1982 SaveEventLog(IN PEVENTLOGFILTER EventLogFilter
)
1986 WCHAR szFileName
[MAX_PATH
];
1988 /* Bail out if there is no available filter */
1989 if (!EventLogFilter
)
1992 ZeroMemory(szFileName
, sizeof(szFileName
));
1994 sfn
.lpstrFile
= szFileName
;
1995 sfn
.nMaxFile
= ARRAYSIZE(szFileName
);
1997 if (!GetSaveFileNameW(&sfn
))
2000 EventLogFilter_AddRef(EventLogFilter
);
2002 EventLog
= EventLogFilter
->EventLogs
[0];
2003 hEventLog
= OpenEventLogW(EventLog
->ComputerName
, EventLog
->LogName
);
2005 EventLogFilter_Release(EventLogFilter
);
2009 ShowWin32Error(GetLastError());
2013 if (!BackupEventLogW(hEventLog
, szFileName
))
2014 ShowWin32Error(GetLastError());
2016 CloseEventLog(hEventLog
);
2020 CloseUserEventLog(IN PEVENTLOGFILTER EventLogFilter
, IN HTREEITEM hti
)
2022 /* Bail out if there is no available filter */
2023 if (!EventLogFilter
)
2026 if (InterlockedCompareExchangePointer((PVOID
*)&ActiveFilter
, NULL
, NULL
) == EventLogFilter
)
2028 /* Signal the enumerator thread we want to stop enumerating events */
2029 // EnumEvents(NULL);
2030 InterlockedExchangePointer((PVOID
*)&EnumFilter
, NULL
);
2031 SetEvent(hStartEnumEvent
);
2035 * The deletion of the item automatically triggers a TVN_SELCHANGED
2036 * notification, that will reset the ActiveFilter (in case the item
2037 * selected is a filter). Otherwise we reset it there.
2039 TreeView_DeleteItem(hwndTreeView
, hti
);
2041 /* Remove the filter from the list */
2042 RemoveEntryList(&EventLogFilter
->ListEntry
);
2043 EventLogFilter_Release(EventLogFilter
);
2045 // /* Select the default event log */
2046 // // TreeView_Expand(hwndTreeView, htiUserLogs, TVE_EXPAND);
2047 // TreeView_SelectItem(hwndTreeView, hItem);
2048 // TreeView_EnsureVisible(hwndTreeView, hItem);
2049 InvalidateRect(hwndTreeView
, NULL
, FALSE
);
2050 SetFocus(hwndTreeView
);
2055 ClearEvents(IN PEVENTLOGFILTER EventLogFilter
)
2060 WCHAR szFileName
[MAX_PATH
];
2061 WCHAR szMessage
[MAX_LOADSTRING
];
2063 /* Bail out if there is no available filter */
2064 if (!EventLogFilter
)
2067 ZeroMemory(szFileName
, sizeof(szFileName
));
2068 ZeroMemory(szMessage
, sizeof(szMessage
));
2070 LoadStringW(hInst
, IDS_CLEAREVENTS_MSG
, szMessage
, ARRAYSIZE(szMessage
));
2072 sfn
.lpstrFile
= szFileName
;
2073 sfn
.nMaxFile
= ARRAYSIZE(szFileName
);
2075 switch (MessageBoxW(hwndMainWindow
, szMessage
, szTitle
, MB_YESNOCANCEL
| MB_ICONINFORMATION
))
2081 sfn
.lpstrFile
= NULL
;
2085 if (!GetSaveFileNameW(&sfn
))
2090 EventLogFilter_AddRef(EventLogFilter
);
2092 EventLog
= EventLogFilter
->EventLogs
[0];
2093 hEventLog
= OpenEventLogW(EventLog
->ComputerName
, EventLog
->LogName
);
2095 EventLogFilter_Release(EventLogFilter
);
2099 ShowWin32Error(GetLastError());
2103 Success
= ClearEventLogW(hEventLog
, sfn
.lpstrFile
);
2105 ShowWin32Error(GetLastError());
2107 CloseEventLog(hEventLog
);
2113 Refresh(IN PEVENTLOGFILTER EventLogFilter
)
2115 /* Bail out if there is no available filter */
2116 if (!EventLogFilter
)
2119 /* Reenumerate the events through the filter */
2120 EnumEvents(EventLogFilter
);
2125 MyRegisterClass(HINSTANCE hInstance
)
2129 wcex
.cbSize
= sizeof(wcex
);
2131 wcex
.lpfnWndProc
= WndProc
;
2132 wcex
.cbClsExtra
= 0;
2133 wcex
.cbWndExtra
= 0;
2134 wcex
.hInstance
= hInstance
;
2135 wcex
.hIcon
= LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_EVENTVWR
));
2136 wcex
.hCursor
= LoadCursorW(NULL
, MAKEINTRESOURCEW(IDC_ARROW
));
2137 wcex
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1); // COLOR_WINDOW + 1
2138 wcex
.lpszMenuName
= MAKEINTRESOURCEW(IDM_EVENTVWR
);
2139 wcex
.lpszClassName
= szWindowClass
;
2140 wcex
.hIconSm
= (HICON
)LoadImageW(hInstance
,
2141 MAKEINTRESOURCEW(IDI_EVENTVWR
),
2147 return RegisterClassExW(&wcex
);
2152 GetDisplayNameFileAndID(IN LPCWSTR lpLogName
,
2153 OUT PWCHAR lpModuleName
, // TODO: Add IN DWORD BufLen
2154 OUT PDWORD pdwMessageID
)
2156 BOOL Success
= FALSE
;
2162 DWORD dwMessageID
= 0;
2163 WCHAR szModuleName
[MAX_PATH
];
2165 /* Use a default value for the message ID */
2168 cbKeyPath
= (wcslen(EVENTLOG_BASE_KEY
) + wcslen(lpLogName
) + 1) * sizeof(WCHAR
);
2169 KeyPath
= HeapAlloc(GetProcessHeap(), 0, cbKeyPath
);
2173 StringCbCopyW(KeyPath
, cbKeyPath
, EVENTLOG_BASE_KEY
);
2174 StringCbCatW(KeyPath
, cbKeyPath
, lpLogName
);
2176 Result
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, KeyPath
, 0, KEY_QUERY_VALUE
, &hLogKey
);
2177 HeapFree(GetProcessHeap(), 0, KeyPath
);
2178 if (Result
!= ERROR_SUCCESS
)
2181 cbData
= sizeof(szModuleName
);
2182 Result
= RegQueryValueExW(hLogKey
,
2186 (LPBYTE
)szModuleName
,
2188 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_EXPAND_SZ
&& Type
!= REG_SZ
))
2190 szModuleName
[0] = UNICODE_NULL
;
2194 /* NULL-terminate the string and expand it */
2195 szModuleName
[cbData
/ sizeof(WCHAR
) - 1] = UNICODE_NULL
;
2196 ExpandEnvironmentStringsW(szModuleName
, lpModuleName
, ARRAYSIZE(szModuleName
));
2201 * If we have a 'DisplayNameFile', query for 'DisplayNameID';
2202 * otherwise it's not really useful. 'DisplayNameID' is optional.
2206 cbData
= sizeof(dwMessageID
);
2207 Result
= RegQueryValueExW(hLogKey
,
2211 (LPBYTE
)&dwMessageID
,
2213 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_DWORD
))
2216 *pdwMessageID
= dwMessageID
;
2219 RegCloseKey(hLogKey
);
2226 BuildLogListAndFilterList(IN LPCWSTR lpComputerName
)
2229 HKEY hEventLogKey
, hLogKey
;
2230 DWORD dwNumLogs
= 0;
2231 DWORD dwIndex
, dwMaxKeyLength
;
2234 PEVENTLOGFILTER EventLogFilter
;
2235 LPWSTR LogName
= NULL
;
2236 WCHAR szModuleName
[MAX_PATH
];
2239 LPWSTR lpDisplayName
;
2240 HTREEITEM hRootNode
= NULL
, hItem
= NULL
, hItemDefault
= NULL
;
2242 /* Open the EventLog key */
2243 // FIXME: Use local or remote computer
2244 Result
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, EVENTLOG_BASE_KEY
, 0, KEY_READ
, &hEventLogKey
);
2245 if (Result
!= ERROR_SUCCESS
)
2250 /* Retrieve the number of event logs enumerated as registry keys */
2251 Result
= RegQueryInfoKeyW(hEventLogKey
, NULL
, NULL
, NULL
, &dwNumLogs
, &dwMaxKeyLength
,
2252 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
2253 if (Result
!= ERROR_SUCCESS
)
2260 /* Take the NULL terminator into account */
2263 /* Allocate the temporary buffer */
2264 LogName
= HeapAlloc(GetProcessHeap(), 0, dwMaxKeyLength
* sizeof(WCHAR
));
2268 /* Enumerate and retrieve each event log name */
2269 for (dwIndex
= 0; dwIndex
< dwNumLogs
; dwIndex
++)
2271 lpcName
= dwMaxKeyLength
;
2272 Result
= RegEnumKeyExW(hEventLogKey
, dwIndex
, LogName
, &lpcName
, NULL
, NULL
, NULL
, NULL
);
2273 if (Result
!= ERROR_SUCCESS
)
2276 /* Take the NULL terminator into account */
2279 /* Allocate a new event log entry */
2280 EventLog
= AllocEventLog(lpComputerName
, LogName
, TRUE
);
2281 if (EventLog
== NULL
)
2284 /* Allocate a new event log filter entry for this event log */
2285 EventLogFilter
= AllocEventLogFilter(// LogName,
2286 TRUE
, TRUE
, TRUE
, TRUE
, TRUE
,
2289 if (EventLogFilter
== NULL
)
2291 EventLog_Free(EventLog
);
2295 /* Add the event log and the filter into their lists */
2296 InsertTailList(&EventLogList
, &EventLog
->ListEntry
);
2297 InsertTailList(&EventLogFilterList
, &EventLogFilter
->ListEntry
);
2299 EventLog
->FileName
= NULL
;
2301 /* Retrieve and cache the event log file */
2302 Result
= RegOpenKeyExW(hEventLogKey
,
2307 if (Result
== ERROR_SUCCESS
)
2310 Result
= RegQueryValueExW(hLogKey
,
2316 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_EXPAND_SZ
&& Type
!= REG_SZ
))
2318 // Windows' EventLog uses some kind of default value, we do not.
2319 EventLog
->FileName
= NULL
;
2323 lpcName
= ROUND_DOWN(lpcName
, sizeof(WCHAR
));
2324 EventLog
->FileName
= HeapAlloc(GetProcessHeap(), 0, lpcName
);
2325 if (EventLog
->FileName
)
2327 Result
= RegQueryValueExW(hLogKey
,
2331 (LPBYTE
)EventLog
->FileName
,
2333 if (Result
!= ERROR_SUCCESS
)
2335 HeapFree(GetProcessHeap(), 0, EventLog
->FileName
);
2336 EventLog
->FileName
= NULL
;
2340 EventLog
->FileName
[lpcName
/ sizeof(WCHAR
) - 1] = UNICODE_NULL
;
2345 RegCloseKey(hLogKey
);
2348 /* Get the display name for the event log */
2349 lpDisplayName
= NULL
;
2351 ZeroMemory(szModuleName
, sizeof(szModuleName
));
2352 if (GetDisplayNameFileAndID(LogName
, szModuleName
, &dwMessageID
))
2354 /* Retrieve the message string without appending extra newlines */
2356 GetMessageStringFromDll(szModuleName
,
2357 FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_HMODULE
|
2358 FORMAT_MESSAGE_IGNORE_INSERTS
| FORMAT_MESSAGE_MAX_WIDTH_MASK
,
2365 * Select the correct tree root node, whether the log is a System
2366 * or an Application log. Default to Application log otherwise.
2368 hRootNode
= htiAppLogs
;
2369 for (lpcName
= 0; lpcName
< ARRAYSIZE(SystemLogs
); ++lpcName
)
2371 /* Check whether the log name is part of the system logs */
2372 if (wcsicmp(LogName
, SystemLogs
[lpcName
]) == 0)
2374 hRootNode
= htiSystemLogs
;
2379 hItem
= TreeViewAddItem(hwndTreeView
, hRootNode
,
2380 (lpDisplayName
? lpDisplayName
: LogName
),
2381 2, 3, (LPARAM
)EventLogFilter
);
2383 /* Try to get the default event log: "Application" */
2384 if ((hItemDefault
== NULL
) && (wcsicmp(LogName
, SystemLogs
[0]) == 0))
2386 hItemDefault
= hItem
;
2389 /* Free the buffer allocated by FormatMessage */
2391 LocalFree(lpDisplayName
);
2394 HeapFree(GetProcessHeap(), 0, LogName
);
2397 RegCloseKey(hEventLogKey
);
2399 /* Select the default event log */
2402 // TreeView_Expand(hwndTreeView, hRootNode, TVE_EXPAND);
2403 TreeView_SelectItem(hwndTreeView
, hItemDefault
);
2404 TreeView_EnsureVisible(hwndTreeView
, hItemDefault
);
2406 InvalidateRect(hwndTreeView
, NULL
, FALSE
);
2407 SetFocus(hwndTreeView
);
2418 while (!IsListEmpty(&EventLogList
))
2420 Entry
= RemoveHeadList(&EventLogList
);
2421 EventLog
= (PEVENTLOG
)CONTAINING_RECORD(Entry
, EVENTLOG
, ListEntry
);
2422 EventLog_Free(EventLog
);
2429 FreeLogFilterList(VOID
)
2432 PEVENTLOGFILTER EventLogFilter
;
2434 while (!IsListEmpty(&EventLogFilterList
))
2436 Entry
= RemoveHeadList(&EventLogFilterList
);
2437 EventLogFilter
= (PEVENTLOGFILTER
)CONTAINING_RECORD(Entry
, EVENTLOGFILTER
, ListEntry
);
2438 EventLogFilter_Free(EventLogFilter
);
2441 ActiveFilter
= NULL
;
2447 InitInstance(HINSTANCE hInstance
,
2453 LVCOLUMNW lvc
= {0};
2456 /* Create the main window */
2457 hwndMainWindow
= CreateWindowW(szWindowClass
,
2459 WS_OVERLAPPEDWINDOW
| WS_CLIPCHILDREN
,
2460 CW_USEDEFAULT
, 0, CW_USEDEFAULT
, 0,
2465 if (!hwndMainWindow
)
2468 /* Create the status bar */
2469 hwndStatus
= CreateWindowExW(0, // no extended styles
2470 STATUSCLASSNAMEW
, // status bar
2472 WS_CHILD
| WS_VISIBLE
| CCS_BOTTOM
| SBARS_SIZEGRIP
, // styles
2473 0, 0, 0, 0, // x, y, cx, cy
2474 hwndMainWindow
, // parent window
2475 (HMENU
)100, // window ID
2476 hInstance
, // instance
2477 NULL
); // window data
2479 GetClientRect(hwndMainWindow
, &rcClient
);
2480 GetWindowRect(hwndStatus
, &rs
);
2481 StatusHeight
= rs
.bottom
- rs
.top
;
2483 /* Create a progress bar in the status bar (hidden by default) */
2484 StatusBar_GetItemRect(hwndStatus
, 0, &rs
);
2485 hwndStatusProgress
= CreateWindowExW(0, // no extended styles
2486 PROGRESS_CLASSW
, // status bar
2488 WS_CHILD
| PBS_SMOOTH
, // styles
2489 rs
.left
, rs
.top
, // x, y
2490 rs
.right
-rs
.left
, rs
.bottom
-rs
.top
, // cx, cy
2491 hwndStatus
, // parent window
2493 hInstance
, // instance
2494 NULL
); // window data
2495 ProgressBar_SetStep(hwndStatusProgress
, 1);
2497 /* Initialize the splitter default positions */
2501 /* Create the TreeView */
2502 hwndTreeView
= CreateWindowExW(WS_EX_CLIENTEDGE
,
2505 // WS_CHILD | WS_VISIBLE | TVS_HASLINES | TVS_SHOWSELALWAYS,
2506 WS_CHILD
| WS_VISIBLE
| /* WS_TABSTOP | */ TVS_HASLINES
| TVS_HASBUTTONS
| TVS_LINESATROOT
| TVS_EDITLABELS
| TVS_SHOWSELALWAYS
,
2508 nVSplitPos
- SPLIT_WIDTH
/2,
2509 (rcClient
.bottom
- rcClient
.top
) - StatusHeight
,
2515 /* Create the ImageList */
2516 hSmall
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
),
2517 GetSystemMetrics(SM_CYSMICON
),
2518 ILC_COLOR32
| ILC_MASK
, // ILC_COLOR24
2521 /* Add event type icons to the ImageList: closed/opened folder, event log (normal/viewed) */
2522 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_CLOSED_CATEGORY
)));
2523 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_OPENED_CATEGORY
)));
2524 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_EVENTLOG
)));
2525 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_EVENTVWR
)));
2527 /* Assign the ImageList to the Tree View */
2528 TreeView_SetImageList(hwndTreeView
, hSmall
, TVSIL_NORMAL
);
2530 /* Add the event logs nodes */
2532 LoadStringW(hInstance
, IDS_EVENTLOG_SYSTEM
, szTemp
, ARRAYSIZE(szTemp
));
2533 htiSystemLogs
= TreeViewAddItem(hwndTreeView
, NULL
, szTemp
, 0, 1, (LPARAM
)NULL
);
2534 // "Application Logs"
2535 LoadStringW(hInstance
, IDS_EVENTLOG_APP
, szTemp
, ARRAYSIZE(szTemp
));
2536 htiAppLogs
= TreeViewAddItem(hwndTreeView
, NULL
, szTemp
, 0, 1, (LPARAM
)NULL
);
2538 LoadStringW(hInstance
, IDS_EVENTLOG_USER
, szTemp
, ARRAYSIZE(szTemp
));
2539 htiUserLogs
= TreeViewAddItem(hwndTreeView
, NULL
, szTemp
, 0, 1, (LPARAM
)NULL
);
2541 /* Create the Event details pane (optional) */
2542 hwndEventDetails
= CreateEventDetailsCtrl(hInst
, hwndMainWindow
, (LPARAM
)NULL
);
2543 // hwndEventDetails = NULL;
2544 if (hwndEventDetails
)
2546 // SetWindowLongPtrW(hwndEventDetails, GWL_STYLE,
2547 // GetWindowLongPtrW(hwndEventDetails, GWL_STYLE) | WS_BORDER);
2548 SetWindowLongPtrW(hwndEventDetails
, GWL_EXSTYLE
,
2549 GetWindowLongPtrW(hwndEventDetails
, GWL_EXSTYLE
) | WS_EX_CLIENTEDGE
);
2550 SetWindowPos(hwndEventDetails
, NULL
,
2551 nVSplitPos
+ SPLIT_WIDTH
/2,
2552 nHSplitPos
+ SPLIT_WIDTH
/2,
2553 (rcClient
.right
- rcClient
.left
) - nVSplitPos
- SPLIT_WIDTH
/2,
2554 (rcClient
.bottom
- rcClient
.top
) - nHSplitPos
- SPLIT_WIDTH
/2 - StatusHeight
,
2555 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_SHOWWINDOW
);
2558 /* Create the ListView */
2559 hwndListView
= CreateWindowExW(WS_EX_CLIENTEDGE
,
2562 WS_CHILD
| WS_VISIBLE
| LVS_SHOWSELALWAYS
| LVS_REPORT
,
2563 nVSplitPos
+ SPLIT_WIDTH
/2,
2565 (rcClient
.right
- rcClient
.left
) - nVSplitPos
- SPLIT_WIDTH
/2,
2566 hwndEventDetails
? nHSplitPos
- SPLIT_WIDTH
/2
2567 : (rcClient
.bottom
- rcClient
.top
) - StatusHeight
,
2573 /* Add the extended ListView styles */
2574 ListView_SetExtendedListViewStyle(hwndListView
, LVS_EX_HEADERDRAGDROP
| LVS_EX_FULLROWSELECT
|LVS_EX_LABELTIP
);
2576 /* Create the ImageList */
2577 hSmall
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
),
2578 GetSystemMetrics(SM_CYSMICON
),
2579 ILC_COLOR32
| ILC_MASK
, // ILC_COLOR24
2582 /* Add event type icons to the ImageList */
2583 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_INFORMATIONICON
)));
2584 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_WARNINGICON
)));
2585 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_ERRORICON
)));
2586 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_AUDITSUCCESSICON
)));
2587 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_AUDITFAILUREICON
)));
2589 /* Assign the ImageList to the List View */
2590 ListView_SetImageList(hwndListView
, hSmall
, LVSIL_SMALL
);
2592 /* Now set up the listview with its columns */
2593 lvc
.mask
= LVCF_TEXT
| LVCF_WIDTH
;
2595 LoadStringW(hInstance
,
2599 lvc
.pszText
= szTemp
;
2600 ListView_InsertColumn(hwndListView
, 0, &lvc
);
2603 LoadStringW(hInstance
,
2607 lvc
.pszText
= szTemp
;
2608 ListView_InsertColumn(hwndListView
, 1, &lvc
);
2611 LoadStringW(hInstance
,
2615 lvc
.pszText
= szTemp
;
2616 ListView_InsertColumn(hwndListView
, 2, &lvc
);
2619 LoadStringW(hInstance
,
2623 lvc
.pszText
= szTemp
;
2624 ListView_InsertColumn(hwndListView
, 3, &lvc
);
2627 LoadStringW(hInstance
,
2631 lvc
.pszText
= szTemp
;
2632 ListView_InsertColumn(hwndListView
, 4, &lvc
);
2635 LoadStringW(hInstance
,
2639 lvc
.pszText
= szTemp
;
2640 ListView_InsertColumn(hwndListView
, 5, &lvc
);
2643 LoadStringW(hInstance
,
2647 lvc
.pszText
= szTemp
;
2648 ListView_InsertColumn(hwndListView
, 6, &lvc
);
2651 LoadStringW(hInstance
,
2655 lvc
.pszText
= szTemp
;
2656 ListView_InsertColumn(hwndListView
, 7, &lvc
);
2658 /* Initialize the save Dialog */
2659 ZeroMemory(&sfn
, sizeof(sfn
));
2660 ZeroMemory(szSaveFilter
, sizeof(szSaveFilter
));
2662 LoadStringW(hInst
, IDS_SAVE_FILTER
, szSaveFilter
, ARRAYSIZE(szSaveFilter
));
2664 sfn
.lStructSize
= sizeof(sfn
);
2665 sfn
.hwndOwner
= hwndMainWindow
;
2666 sfn
.hInstance
= hInstance
;
2667 sfn
.lpstrFilter
= szSaveFilter
;
2668 sfn
.lpstrInitialDir
= NULL
;
2669 sfn
.Flags
= OFN_HIDEREADONLY
| OFN_SHAREAWARE
;
2670 sfn
.lpstrDefExt
= NULL
;
2672 ShowWindow(hwndMainWindow
, nCmdShow
);
2673 UpdateWindow(hwndMainWindow
);
2678 VOID
ResizeWnd(INT cx
, INT cy
)
2684 /* Resize the status bar -- now done in WM_SIZE */
2685 // SendMessageW(hwndStatus, WM_SIZE, 0, 0);
2686 GetWindowRect(hwndStatus
, &rs
);
2687 StatusHeight
= rs
.bottom
- rs
.top
;
2689 /* Move the progress bar */
2690 StatusBar_GetItemRect(hwndStatus
, 0, &rs
);
2691 MoveWindow(hwndStatusProgress
,
2692 rs
.left
, rs
.top
, rs
.right
-rs
.left
, rs
.bottom
-rs
.top
,
2693 IsWindowVisible(hwndStatusProgress
) ? TRUE
: FALSE
);
2696 * TODO: Adjust the splitter positions:
2697 * - Vertical splitter (1) : fixed position from the left window side.
2698 * - Horizontal splitter (2): fixed position from the bottom window side.
2700 nVSplitPos
= min(max(nVSplitPos
, SPLIT_WIDTH
/2), cx
- SPLIT_WIDTH
/2); // OK
2701 nHSplitPos
= min(max(nHSplitPos
, SPLIT_WIDTH
/2), cy
- SPLIT_WIDTH
/2 - StatusHeight
); // FIXME!
2703 hdwp
= BeginDeferWindowPos(3);
2706 hdwp
= DeferWindowPos(hdwp
,
2710 nVSplitPos
- SPLIT_WIDTH
/2,
2712 SWP_NOZORDER
| SWP_NOACTIVATE
);
2715 hdwp
= DeferWindowPos(hdwp
,
2718 nVSplitPos
+ SPLIT_WIDTH
/2, 0,
2719 cx
- nVSplitPos
- SPLIT_WIDTH
/2,
2720 hwndEventDetails
? nHSplitPos
- SPLIT_WIDTH
/2
2721 : cy
- StatusHeight
,
2722 SWP_NOZORDER
| SWP_NOACTIVATE
);
2724 if (hwndEventDetails
&& hdwp
)
2725 hdwp
= DeferWindowPos(hdwp
,
2728 nVSplitPos
+ SPLIT_WIDTH
/2,
2729 nHSplitPos
+ SPLIT_WIDTH
/2,
2730 cx
- nVSplitPos
- SPLIT_WIDTH
/2,
2731 cy
- nHSplitPos
- SPLIT_WIDTH
/2 - StatusHeight
,
2732 SWP_NOZORDER
| SWP_NOACTIVATE
);
2735 EndDeferWindowPos(hdwp
);
2740 WndProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
2747 hMainMenu
= GetMenu(hWnd
);
2756 LPNMHDR hdr
= (LPNMHDR
)lParam
;
2758 if (hdr
->hwndFrom
== hwndListView
)
2762 case LVN_ITEMCHANGED
:
2764 LPNMLISTVIEW pnmv
= (LPNMLISTVIEW
)lParam
;
2766 if ( (pnmv
->uChanged
& LVIF_STATE
) && /* The state has changed */
2767 (pnmv
->uNewState
& LVIS_SELECTED
) /* The item has been (de)selected */ )
2769 if (hwndEventDetails
)
2770 SendMessageW(hwndEventDetails
, EVT_DISPLAY
, 0, 0);
2777 SendMessageW(hWnd
, WM_COMMAND
, IDM_EVENT_DETAILS
, 0);
2781 else if (hdr
->hwndFrom
== hwndTreeView
)
2785 case TVN_BEGINLABELEDIT
:
2787 HTREEITEM hItem
= ((LPNMTVDISPINFO
)lParam
)->item
.hItem
;
2789 /* Disable label editing for root nodes */
2790 return ((hItem
== htiSystemLogs
) ||
2791 (hItem
== htiAppLogs
) ||
2792 (hItem
== htiUserLogs
));
2795 case TVN_ENDLABELEDIT
:
2797 TVITEMW item
= ((LPNMTVDISPINFO
)lParam
)->item
;
2798 HTREEITEM hItem
= item
.hItem
;
2800 /* Disable label editing for root nodes */
2801 if ((hItem
== htiSystemLogs
) ||
2802 (hItem
== htiAppLogs
) ||
2803 (hItem
== htiUserLogs
))
2810 LPWSTR pszText
= item
.pszText
;
2812 /* Trim all whitespace */
2813 while (*pszText
&& iswspace(*pszText
))
2827 case TVN_SELCHANGED
:
2829 PEVENTLOGFILTER EventLogFilter
=
2830 (PEVENTLOGFILTER
)((LPNMTREEVIEW
)lParam
)->itemNew
.lParam
;
2832 // FIXME: It might be nice to reference here the filter,
2833 // so that we don't have to reference/dereference it many times
2834 // in the other functions???
2836 // FIXME: This is a hack!!
2837 if (hwndEventDetails
&& EventLogFilter
)
2839 SendMessageW(hwndEventDetails
, EVT_SETFILTER
, 0, (LPARAM
)EventLogFilter
);
2845 * If we have selected a filter, enable the menu commands;
2846 * they will possibly be updated after events enumeration.
2848 EnableMenuItem(hMainMenu
, IDM_SAVE_EVENTLOG
, MF_BYCOMMAND
| MF_ENABLED
);
2849 EnableMenuItem(hMainMenu
, IDM_CLOSE_EVENTLOG
, MF_BYCOMMAND
| MF_ENABLED
);
2850 EnableMenuItem(hMainMenu
, IDM_CLEAR_EVENTS
, MF_BYCOMMAND
| MF_ENABLED
);
2851 EnableMenuItem(hMainMenu
, IDM_RENAME_EVENTLOG
, MF_BYCOMMAND
| MF_ENABLED
);
2852 EnableMenuItem(hMainMenu
, IDM_EVENTLOG_SETTINGS
, MF_BYCOMMAND
| MF_ENABLED
);
2856 EnableMenuItem(hMainMenu
, IDM_SAVE_EVENTLOG
, MF_BYCOMMAND
| MF_GRAYED
);
2857 EnableMenuItem(hMainMenu
, IDM_CLOSE_EVENTLOG
, MF_BYCOMMAND
| MF_GRAYED
);
2858 EnableMenuItem(hMainMenu
, IDM_CLEAR_EVENTS
, MF_BYCOMMAND
| MF_GRAYED
);
2859 EnableMenuItem(hMainMenu
, IDM_RENAME_EVENTLOG
, MF_BYCOMMAND
| MF_GRAYED
);
2860 EnableMenuItem(hMainMenu
, IDM_EVENTLOG_SETTINGS
, MF_BYCOMMAND
| MF_GRAYED
);
2864 * The enumeration thread that is triggered by EnumEvents
2865 * will set a new value for the 'ActiveFilter'.
2868 EnumEvents(EventLogFilter
);
2879 /* Parse the menu selections */
2880 switch (LOWORD(wParam
))
2882 case IDM_OPEN_EVENTLOG
:
2886 case IDM_SAVE_EVENTLOG
:
2887 SaveEventLog(GetSelectedFilter(NULL
));
2890 case IDM_CLOSE_EVENTLOG
:
2893 PEVENTLOGFILTER EventLogFilter
= GetSelectedFilter(&hti
);
2894 CloseUserEventLog(EventLogFilter
, hti
);
2898 case IDM_CLEAR_EVENTS
:
2900 PEVENTLOGFILTER EventLogFilter
= GetSelectedFilter(NULL
);
2901 if (EventLogFilter
&& ClearEvents(EventLogFilter
))
2902 Refresh(EventLogFilter
);
2906 case IDM_RENAME_EVENTLOG
:
2907 if (GetFocus() == hwndTreeView
)
2908 TreeView_EditLabel(hwndTreeView
, TreeView_GetSelection(hwndTreeView
));
2911 case IDM_EVENTLOG_SETTINGS
:
2913 PEVENTLOGFILTER EventLogFilter
= GetSelectedFilter(NULL
);
2914 // TODO: Check the returned value?
2916 EventLogProperties(hInst
, hWnd
, EventLogFilter
);
2920 case IDM_LIST_NEWEST
:
2922 CheckMenuRadioItem(hMainMenu
, IDM_LIST_NEWEST
, IDM_LIST_OLDEST
, IDM_LIST_NEWEST
, MF_BYCOMMAND
);
2923 if (!NewestEventsFirst
)
2925 NewestEventsFirst
= TRUE
;
2926 Refresh(GetSelectedFilter(NULL
));
2931 case IDM_LIST_OLDEST
:
2933 CheckMenuRadioItem(hMainMenu
, IDM_LIST_NEWEST
, IDM_LIST_OLDEST
, IDM_LIST_OLDEST
, MF_BYCOMMAND
);
2934 if (NewestEventsFirst
)
2936 NewestEventsFirst
= FALSE
;
2937 Refresh(GetSelectedFilter(NULL
));
2942 case IDM_EVENT_DETAILS
:
2944 // LPNMITEMACTIVATE lpnmitem = (LPNMITEMACTIVATE)lParam;
2945 PEVENTLOGFILTER EventLogFilter
= GetSelectedFilter(NULL
);
2946 if (/*lpnmitem->iItem != -1 &&*/ EventLogFilter
)
2948 EventLogFilter_AddRef(EventLogFilter
);
2949 DialogBoxParamW(hInst
,
2950 MAKEINTRESOURCEW(IDD_EVENTDETAILS_DLG
),
2953 (LPARAM
)EventLogFilter
);
2954 EventLogFilter_Release(EventLogFilter
);
2960 Refresh(GetSelectedFilter(NULL
));
2966 WCHAR szCopyright
[MAX_LOADSTRING
];
2968 hIcon
= LoadIconW(hInst
, MAKEINTRESOURCEW(IDI_EVENTVWR
));
2969 LoadStringW(hInst
, IDS_COPYRIGHT
, szCopyright
, ARRAYSIZE(szCopyright
));
2970 ShellAboutW(hWnd
, szTitle
, szCopyright
, hIcon
);
2971 DeleteObject(hIcon
);
2976 MessageBoxW(hwndMainWindow
,
2977 L
"Help not implemented yet!",
2979 MB_OK
| MB_ICONINFORMATION
);
2983 DestroyWindow(hWnd
);
2987 return DefWindowProcW(hWnd
, uMsg
, wParam
, lParam
);
2993 if (LOWORD(lParam
) == HTCLIENT
)
2997 ScreenToClient(hWnd
, &pt
);
2999 /* Set the cursor for the vertical splitter */
3000 if (pt
.x
>= nVSplitPos
- SPLIT_WIDTH
/2 && pt
.x
< nVSplitPos
+ SPLIT_WIDTH
/2 + 1)
3003 GetClientRect(hWnd
, &rect
);
3004 GetWindowRect(hwndStatus
, &rs
);
3005 if (pt
.y
>= rect
.top
&& pt
.y
< rect
.bottom
- (rs
.bottom
- rs
.top
))
3007 SetCursor(LoadCursorW(NULL
, IDC_SIZEWE
));
3012 /* Set the cursor for the horizontal splitter, if the Event details pane is displayed */
3013 if (hwndEventDetails
&&
3014 (pt
.y
>= nHSplitPos
- SPLIT_WIDTH
/2 && pt
.y
< nHSplitPos
+ SPLIT_WIDTH
/2 + 1))
3017 GetClientRect(hWnd
, &rect
);
3018 // GetWindowRect(hwndStatus, &rs);
3019 if (pt
.x
>= nVSplitPos
+ SPLIT_WIDTH
/2 + 1 /* rect.left + (rs.bottom - rs.top) */ &&
3022 SetCursor(LoadCursorW(NULL
, IDC_SIZENS
));
3029 case WM_LBUTTONDOWN
:
3031 INT x
= GET_X_LPARAM(lParam
);
3032 INT y
= GET_Y_LPARAM(lParam
);
3034 /* Reset the splitter state */
3037 /* Capture the cursor for the vertical splitter */
3038 if (x
>= nVSplitPos
- SPLIT_WIDTH
/2 && x
< nVSplitPos
+ SPLIT_WIDTH
/2 + 1)
3044 /* Capture the cursor for the horizontal splitter, if the Event details pane is displayed */
3045 if (hwndEventDetails
&&
3046 (y
>= nHSplitPos
- SPLIT_WIDTH
/2 && y
< nHSplitPos
+ SPLIT_WIDTH
/2 + 1))
3055 case WM_RBUTTONDOWN
:
3056 if (GetCapture() == hWnd
)
3058 /* Adjust the correct splitter position */
3060 nVSplitPos
= GET_X_LPARAM(lParam
);
3061 else if (bSplit
== 2)
3062 nHSplitPos
= GET_Y_LPARAM(lParam
);
3064 /* If we are splitting, resize the windows */
3067 GetClientRect(hWnd
, &rect
);
3068 ResizeWnd(rect
.right
- rect
.left
, rect
.bottom
- rect
.top
);
3071 /* Reset the splitter state */
3079 if (GetCapture() == hWnd
)
3081 /* Move the correct splitter */
3084 INT x
= GET_X_LPARAM(lParam
);
3086 GetClientRect(hWnd
, &rect
);
3088 x
= min(max(x
, SPLIT_WIDTH
/2), rect
.right
- rect
.left
- SPLIT_WIDTH
/2);
3089 if (nVSplitPos
!= x
)
3092 ResizeWnd(rect
.right
- rect
.left
, rect
.bottom
- rect
.top
);
3095 else if (bSplit
== 2)
3098 INT y
= GET_Y_LPARAM(lParam
);
3100 GetClientRect(hWnd
, &rect
);
3101 GetWindowRect(hwndStatus
, &rs
);
3103 y
= min(max(y
, SPLIT_WIDTH
/2), rect
.bottom
- rect
.top
- SPLIT_WIDTH
/2 - (rs
.bottom
- rs
.top
));
3104 if (nHSplitPos
!= y
)
3107 ResizeWnd(rect
.right
- rect
.left
, rect
.bottom
- rect
.top
);
3115 if (wParam
!= SIZE_MINIMIZED
)
3117 SendMessageW(hwndStatus
, WM_SIZE
, 0, 0);
3118 ResizeWnd(LOWORD(lParam
), HIWORD(lParam
));
3121 /* Fall through the default case */
3125 return DefWindowProcW(hWnd
, uMsg
, wParam
, lParam
);
3134 InitPropertiesDlg(HWND hDlg
, PEVENTLOG EventLog
)
3136 LPWSTR lpLogName
= EventLog
->LogName
;
3139 DWORD dwMaxSize
= 0, dwRetention
= 0;
3141 WIN32_FIND_DATAW FileInfo
; // WIN32_FILE_ATTRIBUTE_DATA
3142 ULARGE_INTEGER FileSize
;
3143 WCHAR wszBuf
[MAX_PATH
];
3144 WCHAR szTemp
[MAX_LOADSTRING
];
3152 if (EventLog
->Permanent
)
3155 cbKeyPath
= (wcslen(EVENTLOG_BASE_KEY
) + wcslen(lpLogName
) + 1) * sizeof(WCHAR
);
3156 KeyPath
= HeapAlloc(GetProcessHeap(), 0, cbKeyPath
);
3162 StringCbCopyW(KeyPath
, cbKeyPath
, EVENTLOG_BASE_KEY
);
3163 StringCbCatW(KeyPath
, cbKeyPath
, lpLogName
);
3165 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE
, KeyPath
, 0, KEY_QUERY_VALUE
, &hLogKey
) != ERROR_SUCCESS
)
3167 HeapFree(GetProcessHeap(), 0, KeyPath
);
3170 HeapFree(GetProcessHeap(), 0, KeyPath
);
3173 cbData
= sizeof(dwMaxSize
);
3174 Result
= RegQueryValueExW(hLogKey
,
3180 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_DWORD
))
3182 // dwMaxSize = 512 * 1024; /* 512 kBytes */
3188 cbData
= sizeof(dwRetention
);
3189 Result
= RegQueryValueExW(hLogKey
,
3193 (LPBYTE
)&dwRetention
,
3195 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_DWORD
))
3197 /* On Windows 2003 it is 604800 (secs) == 7 days */
3200 /* Convert in days, rounded up */ // ROUND_UP
3201 // dwRetention = ROUND_UP(dwRetention, 24*3600) / (24*3600);
3202 dwRetention
= (dwRetention
+ 24*3600 - 1) / (24*3600);
3205 RegCloseKey(hLogKey
);
3212 SetDlgItemTextW(hDlg
, IDC_DISPLAYNAME
, lpLogName
); // FIXME!
3213 SetDlgItemTextW(hDlg
, IDC_LOGNAME
, lpLogName
);
3215 FileName
= EventLog
->FileName
;
3216 if (FileName
&& *FileName
)
3218 ExpandEnvironmentStringsW(FileName
, wszBuf
, MAX_PATH
);
3221 SetDlgItemTextW(hDlg
, IDC_LOGFILE
, FileName
);
3224 * The general problem here (and in the shell as well) is that
3225 * GetFileAttributesEx fails for files that are opened without
3226 * shared access. To retrieve file information for those we need
3227 * to use something else: FindFirstFile, on the full file name.
3230 Success
= GetFileAttributesExW(FileName
,
3231 GetFileExInfoStandard
,
3232 (LPWIN32_FILE_ATTRIBUTE_DATA
)&FileInfo
);
3235 HANDLE hFind
= FindFirstFileW(FileName
, &FileInfo
);
3236 Success
= (hFind
!= INVALID_HANDLE_VALUE
);
3241 // Starting there, FileName is invalid (because it uses wszBuf)
3245 FileSize
.u
.LowPart
= FileInfo
.nFileSizeLow
;
3246 FileSize
.u
.HighPart
= FileInfo
.nFileSizeHigh
;
3247 if (FormatFileSizeWithBytes(&FileSize
, wszBuf
, ARRAYSIZE(wszBuf
)))
3248 SetDlgItemTextW(hDlg
, IDC_SIZE_LABEL
, wszBuf
);
3250 LoadStringW(hInst
, IDS_NOT_AVAILABLE
, szTemp
, ARRAYSIZE(szTemp
));
3252 if (GetFileTimeString(&FileInfo
.ftCreationTime
, wszBuf
, ARRAYSIZE(wszBuf
)))
3253 SetDlgItemTextW(hDlg
, IDC_CREATED_LABEL
, wszBuf
);
3255 SetDlgItemTextW(hDlg
, IDC_CREATED_LABEL
, szTemp
);
3257 if (GetFileTimeString(&FileInfo
.ftLastWriteTime
, wszBuf
, ARRAYSIZE(wszBuf
)))
3258 SetDlgItemTextW(hDlg
, IDC_MODIFIED_LABEL
, wszBuf
);
3260 SetDlgItemTextW(hDlg
, IDC_MODIFIED_LABEL
, szTemp
);
3262 if (GetFileTimeString(&FileInfo
.ftLastAccessTime
, wszBuf
, ARRAYSIZE(wszBuf
)))
3263 SetDlgItemTextW(hDlg
, IDC_ACCESSED_LABEL
, wszBuf
);
3265 SetDlgItemTextW(hDlg
, IDC_MODIFIED_LABEL
, szTemp
);
3269 LoadStringW(hInst
, IDS_NOT_AVAILABLE
, szTemp
, ARRAYSIZE(szTemp
));
3271 SetDlgItemTextW(hDlg
, IDC_SIZE_LABEL
, szTemp
);
3272 SetDlgItemTextW(hDlg
, IDC_CREATED_LABEL
, szTemp
);
3273 SetDlgItemTextW(hDlg
, IDC_MODIFIED_LABEL
, szTemp
);
3274 SetDlgItemTextW(hDlg
, IDC_ACCESSED_LABEL
, szTemp
);
3277 if (EventLog
->Permanent
)
3279 SendDlgItemMessageW(hDlg
, IDC_UPDOWN_MAXLOGSIZE
, UDM_SETRANGE32
, (WPARAM
)1, (LPARAM
)0x3FFFC0);
3280 SendDlgItemMessageW(hDlg
, IDC_UPDOWN_EVENTS_AGE
, UDM_SETRANGE
, 0, (LPARAM
)MAKELONG(365, 1));
3282 SetDlgItemInt(hDlg
, IDC_EDIT_MAXLOGSIZE
, dwMaxSize
, FALSE
);
3283 SetDlgItemInt(hDlg
, IDC_EDIT_EVENTS_AGE
, dwRetention
, FALSE
);
3285 if (dwRetention
== 0)
3287 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_OVERWRITE_AS_NEEDED
);
3288 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, FALSE
);
3289 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, FALSE
);
3291 else if (dwRetention
== INFINITE
)
3293 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_NO_OVERWRITE
);
3294 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, FALSE
);
3295 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, FALSE
);
3299 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_OVERWRITE_OLDER_THAN
);
3300 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, TRUE
);
3301 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, TRUE
);
3306 // TODO: Hide the unused controls! Or, just use another type of property sheet!
3310 /* Message handler for EventLog Properties dialog */
3312 EventLogPropProc(HWND hDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
3316 EventLog
= (PEVENTLOG
)GetWindowLongPtrW(hDlg
, DWLP_USER
);
3322 EventLog
= (PEVENTLOG
)((LPPROPSHEETPAGE
)lParam
)->lParam
;
3323 SetWindowLongPtrW(hDlg
, DWLP_USER
, (LONG_PTR
)EventLog
);
3325 InitPropertiesDlg(hDlg
, EventLog
);
3327 PropSheet_UnChanged(GetParent(hDlg
), hDlg
);
3328 return (INT_PTR
)TRUE
;
3332 return (INT_PTR
)TRUE
;
3335 switch (LOWORD(wParam
))
3339 EndDialog(hDlg
, LOWORD(wParam
));
3340 return (INT_PTR
)TRUE
;
3342 case IDC_OVERWRITE_AS_NEEDED
:
3344 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_OVERWRITE_AS_NEEDED
);
3345 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, FALSE
);
3346 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, FALSE
);
3350 case IDC_OVERWRITE_OLDER_THAN
:
3352 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_OVERWRITE_OLDER_THAN
);
3353 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, TRUE
);
3354 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, TRUE
);
3358 case IDC_NO_OVERWRITE
:
3360 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_NO_OVERWRITE
);
3361 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, FALSE
);
3362 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, FALSE
);
3368 L
"Help not implemented yet!",
3370 MB_OK
| MB_ICONINFORMATION
);
3371 return (INT_PTR
)TRUE
;
3379 return (INT_PTR
)FALSE
;
3383 EventLogProperties(HINSTANCE hInstance
, HWND hWndParent
, PEVENTLOGFILTER EventLogFilter
)
3386 PROPSHEETHEADERW psh
;
3387 PROPSHEETPAGEW psp
[1]; // 2
3390 * Bail out if there is no available filter, or if the filter
3391 * contains more than one log.
3393 if (!EventLogFilter
)
3396 EventLogFilter_AddRef(EventLogFilter
);
3398 if (EventLogFilter
->NumOfEventLogs
> 1 ||
3399 EventLogFilter
->EventLogs
[0] == NULL
)
3405 psh
.dwSize
= sizeof(psh
);
3406 psh
.dwFlags
= PSH_PROPSHEETPAGE
/*| PSH_USEICONID */ | PSH_PROPTITLE
| PSH_HASHELP
/*| PSH_NOCONTEXTHELP */ /*| PSH_USECALLBACK */;
3407 psh
.hInstance
= hInstance
;
3408 psh
.hwndParent
= hWndParent
;
3409 // psh.pszIcon = MAKEINTRESOURCEW(IDI_APPICON); // Disabled because it only sets the small icon; the big icon is a stretched version of the small one.
3410 psh
.pszCaption
= EventLogFilter
->EventLogs
[0]->LogName
;
3413 psh
.nPages
= ARRAYSIZE(psp
);
3414 // psh.pfnCallback = PropSheetCallback;
3416 /* Log properties page */
3417 psp
[0].dwSize
= sizeof(psp
[0]);
3418 psp
[0].dwFlags
= PSP_HASHELP
;
3419 psp
[0].hInstance
= hInstance
;
3420 psp
[0].pszTemplate
= MAKEINTRESOURCEW(IDD_LOGPROPERTIES_GENERAL
);
3421 psp
[0].pfnDlgProc
= EventLogPropProc
;
3422 psp
[0].lParam
= (LPARAM
)EventLogFilter
->EventLogs
[0];
3425 /* TODO: Log sources page */
3426 psp
[1].dwSize
= sizeof(psp
[1]);
3427 psp
[1].dwFlags
= PSP_HASHELP
;
3428 psp
[1].hInstance
= hInstance
;
3429 psp
[1].pszTemplate
= MAKEINTRESOURCEW(IDD_GENERAL_PAGE
);
3430 psp
[1].pfnDlgProc
= GeneralPageWndProc
;
3431 psp
[0].lParam
= (LPARAM
)EventLogFilter
->EventLogs
[0];
3434 /* Create the property sheet */
3435 ret
= PropertySheetW(&psh
);
3438 EventLogFilter_Release(EventLogFilter
);
3442 /* Message handler for Event Details dialog */
3443 static HWND hWndDetailsCtrl
= NULL
; // May go into the DWLP_USER
3444 static HWND hWndGrip
= NULL
;
3445 static INT cxMin
, cyMin
; // In window coordinates
3446 static INT cxOld
, cyOld
; // In client coordinates
3449 EventDetails(HWND hDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
3456 INT sbVXSize
, sbHYSize
;
3459 hWndDetailsCtrl
= CreateEventDetailsCtrl(hInst
, hDlg
, lParam
);
3460 if (!hWndDetailsCtrl
)
3463 return (INT_PTR
)TRUE
;
3466 /* Create a size grip if the dialog has a sizing border */
3467 GetClientRect(hDlg
, &rcWnd
);
3468 dwStyle
= GetWindowLongPtrW(hDlg
, GWL_STYLE
);
3469 sbVXSize
= GetSystemMetrics(SM_CXVSCROLL
);
3470 sbHYSize
= GetSystemMetrics(SM_CYHSCROLL
);
3471 if (dwStyle
& WS_THICKFRAME
/* == WS_SIZEBOX */)
3473 hWndGrip
= CreateWindowW(WC_SCROLLBARW
,
3475 WS_CHILD
| WS_VISIBLE
| /**/ WS_CLIPSIBLINGS
| /**/ SBS_SIZEGRIP
| SBS_SIZEBOXBOTTOMRIGHTALIGN
,
3476 rcWnd
.right
- sbVXSize
,
3477 rcWnd
.bottom
- sbHYSize
,
3485 // SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)hWndDetailsCtrl);
3488 * Compute the minimum window size (in window coordinates) by
3489 * adding the widths/heights of the "Help" and "Close" buttons,
3490 * together with the margins, and add some minimal spacing
3491 * between the buttons.
3493 GetWindowRect(hDlg
, &rcWnd
);
3496 GetWindowRect(GetDlgItem(hDlg
, IDHELP
), &rect
);
3497 cxMin
+= (rect
.right
- rect
.left
) + (rect
.left
- rcWnd
.left
); // == (rect.right - rcWnd.left);
3498 cyMin
+= (rect
.bottom
- rect
.top
) + (rcWnd
.bottom
- rect
.bottom
); // == (rcWnd.bottom - rect.top);
3500 GetWindowRect(GetDlgItem(hDlg
, IDOK
), &rect
);
3501 cxMin
+= (rect
.right
- rect
.left
) + (rcWnd
.right
- rect
.right
); // == (rcWnd.right - rect.left);
3502 cyMin
+= (rect
.bottom
- rect
.top
) + (rcWnd
.bottom
- rect
.bottom
); // == (rcWnd.bottom - rect.top);
3505 * Convert the window rect from window to client coordinates
3506 * in order to retrieve the sizes of the left and top margins,
3507 * and add some extra space.
3509 MapWindowPoints(HWND_DESKTOP
/*NULL*/, hDlg
, (LPPOINT
)&rcWnd
, sizeof(RECT
)/sizeof(POINT
));
3511 cxMin
+= -2*rcWnd
.left
; // Minimal spacing between the buttons == 2 * left margin
3512 cyMin
+= -rcWnd
.top
+ 12; // Add some space on top
3514 GetClientRect(hDlg
, &rcWnd
);
3515 cxOld
= rcWnd
.right
- rcWnd
.left
;
3516 cyOld
= rcWnd
.bottom
- rcWnd
.top
;
3518 /* Show event info on dialog control */
3519 SendMessageW(hWndDetailsCtrl
, EVT_DISPLAY
, 0, 0);
3521 // SetWindowPos(hWndDetailsCtrl, NULL,
3523 // (rcWnd.right - rcWnd.left),
3524 // (rcWnd.bottom - rcWnd.top),
3525 // SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
3528 * Hide the placeholder static control and show the event details
3529 * control instead. Note that the placeholder is here so far just
3530 * to get the dimensions right in the dialog resource editor.
3531 * I plan to remove it and use a custom control with a suitable
3532 * window class for it, that would create the event details control
3535 ShowWindow(GetDlgItem(hDlg
, IDC_STATIC
), SW_HIDE
);
3536 ShowWindow(hWndDetailsCtrl
, SW_SHOW
);
3537 return (INT_PTR
)TRUE
;
3541 if (IsWindow(hWndDetailsCtrl
))
3542 DestroyWindow(hWndDetailsCtrl
);
3543 hWndDetailsCtrl
= NULL
;
3544 return (INT_PTR
)TRUE
;
3547 switch (LOWORD(wParam
))
3551 EndDialog(hDlg
, LOWORD(wParam
));
3552 return (INT_PTR
)TRUE
;
3556 L
"Help not implemented yet!",
3558 MB_OK
| MB_ICONINFORMATION
);
3559 return (INT_PTR
)TRUE
;
3567 if (((HWND
)wParam
== hWndGrip
) && (LOWORD(lParam
) == HTCLIENT
))
3569 SetCursor(LoadCursorW(NULL
, IDC_SIZENWSE
));
3570 SetWindowLongPtrW(hDlg
, DWLP_MSGRESULT
, TRUE
);
3571 return (INT_PTR
)TRUE
;
3577 /* Forbid resizing the dialog smaller than its minimal size */
3578 PRECT dragRect
= (PRECT
)lParam
;
3580 if ((wParam
== WMSZ_LEFT
) || (wParam
== WMSZ_TOPLEFT
) || (wParam
== WMSZ_BOTTOMLEFT
))
3582 if (dragRect
->right
- dragRect
->left
< cxMin
)
3583 dragRect
->left
= dragRect
->right
- cxMin
;
3586 if ((wParam
== WMSZ_RIGHT
) || (wParam
== WMSZ_TOPRIGHT
) || (wParam
== WMSZ_BOTTOMRIGHT
))
3588 if (dragRect
->right
- dragRect
->left
< cxMin
)
3589 dragRect
->right
= dragRect
->left
+ cxMin
;
3592 if ((wParam
== WMSZ_TOP
) || (wParam
== WMSZ_TOPLEFT
) || (wParam
== WMSZ_TOPRIGHT
))
3594 if (dragRect
->bottom
- dragRect
->top
< cyMin
)
3595 dragRect
->top
= dragRect
->bottom
- cyMin
;
3598 if ((wParam
== WMSZ_BOTTOM
) || (wParam
== WMSZ_BOTTOMLEFT
) || (wParam
== WMSZ_BOTTOMRIGHT
))
3600 if (dragRect
->bottom
- dragRect
->top
< cyMin
)
3601 dragRect
->bottom
= dragRect
->top
+ cyMin
;
3604 SetWindowLongPtrW(hDlg
, DWLP_MSGRESULT
, TRUE
);
3605 return (INT_PTR
)TRUE
;
3610 INT cx
= LOWORD(lParam
);
3611 INT cy
= HIWORD(lParam
);
3617 hdwp
= BeginDeferWindowPos(4);
3619 /* Resize the event details control window */
3621 hItemWnd
= hWndDetailsCtrl
;
3622 GetWindowRect(hItemWnd
, &rect
);
3623 MapWindowPoints(HWND_DESKTOP
/*NULL*/, hDlg
, (LPPOINT
)&rect
, sizeof(RECT
)/sizeof(POINT
));
3626 hdwp
= DeferWindowPos(hdwp
,
3630 (rect
.right
- rect
.left
) + (cx
- cxOld
),
3631 (rect
.bottom
- rect
.top
) + (cy
- cyOld
),
3632 SWP_NOMOVE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
3634 /* Move the buttons */
3636 hItemWnd
= GetDlgItem(hDlg
, IDHELP
);
3637 GetWindowRect(hItemWnd
, &rect
);
3638 MapWindowPoints(HWND_DESKTOP
/*NULL*/, hDlg
, (LPPOINT
)&rect
, sizeof(RECT
)/sizeof(POINT
));
3641 hdwp
= DeferWindowPos(hdwp
,
3645 rect
.top
+ (cy
- cyOld
),
3647 SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
3649 hItemWnd
= GetDlgItem(hDlg
, IDOK
);
3650 GetWindowRect(hItemWnd
, &rect
);
3651 MapWindowPoints(HWND_DESKTOP
/*NULL*/, hDlg
, (LPPOINT
)&rect
, sizeof(RECT
)/sizeof(POINT
));
3654 hdwp
= DeferWindowPos(hdwp
,
3657 rect
.left
+ (cx
- cxOld
),
3658 rect
.top
+ (cy
- cyOld
),
3660 SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
3662 /* Move the size grip */
3663 if (hWndGrip
&& hdwp
)
3665 GetWindowRect(hWndGrip
, &rect
);
3666 MapWindowPoints(HWND_DESKTOP
/*NULL*/, hDlg
, (LPPOINT
)&rect
, sizeof(RECT
)/sizeof(POINT
));
3668 hdwp
= DeferWindowPos(hdwp
,
3671 rect
.left
+ (cx
- cxOld
),
3672 rect
.top
+ (cy
- cyOld
),
3674 SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
3678 EndDeferWindowPos(hdwp
);
3680 /* Hide the size grip if we are in maximized mode */
3682 ShowWindow(hWndGrip
, (wParam
== SIZE_MAXIMIZED
) ? SW_HIDE
: SW_SHOW
);
3687 SetWindowLongPtrW(hDlg
, DWLP_MSGRESULT
, 0);
3688 return (INT_PTR
)TRUE
;
3692 return (INT_PTR
)FALSE
;