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 LRESULT CALLBACK
WndProc(HWND
, UINT
, WPARAM
, LPARAM
);
128 INT_PTR
EventLogProperties(HINSTANCE
, HWND
, PEVENTLOGFILTER
);
129 INT_PTR CALLBACK
EventDetails(HWND
, UINT
, WPARAM
, LPARAM
);
133 wWinMain(HINSTANCE hInstance
,
134 HINSTANCE hPrevInstance
,
139 INITCOMMONCONTROLSEX iccx
;
144 UNREFERENCED_PARAMETER(hPrevInstance
);
145 UNREFERENCED_PARAMETER(lpCmdLine
);
147 /* Whenever any of the common controls are used in your app,
148 * you must call InitCommonControlsEx() to register the classes
149 * for those controls. */
150 iccx
.dwSize
= sizeof(iccx
);
151 iccx
.dwICC
= ICC_LISTVIEW_CLASSES
;
152 InitCommonControlsEx(&iccx
);
154 /* Load the RichEdit DLL to add support for RichEdit controls */
155 hRichEdit
= LoadLibraryW(L
"riched20.dll");
159 msg
.wParam
= (WPARAM
)-1;
161 /* Initialize global strings */
162 LoadStringW(hInstance
, IDS_APP_TITLE
, szTitle
, ARRAYSIZE(szTitle
));
163 LoadStringW(hInstance
, IDS_APP_TITLE_EX
, szTitleTemplate
, ARRAYSIZE(szTitleTemplate
));
164 LoadStringW(hInstance
, IDS_STATUS_MSG
, szStatusBarTemplate
, ARRAYSIZE(szStatusBarTemplate
));
165 LoadStringW(hInstance
, IDS_LOADING_WAIT
, szLoadingWait
, ARRAYSIZE(szLoadingWait
));
166 LoadStringW(hInstance
, IDS_NO_ITEMS
, szEmptyList
, ARRAYSIZE(szEmptyList
));
168 if (!MyRegisterClass(hInstance
))
171 /* Perform application initialization */
172 if (!InitInstance(hInstance
, nCmdShow
))
175 hAccelTable
= LoadAcceleratorsW(hInstance
, MAKEINTRESOURCEW(IDA_EVENTVWR
));
177 /* Create the Start/Stop enumerator thread */
178 // Manual-reset event
179 hStartStopEnumEvent
= CreateEventW(NULL
, TRUE
, FALSE
, NULL
);
180 if (!hStartStopEnumEvent
)
184 hStartEnumEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
185 if (!hStartEnumEvent
)
188 hThread
= CreateThread(NULL
, 0,
189 StartStopEnumEventsThread
,
194 /* Retrieve the available event logs on this computer and create filters for them */
195 InitializeListHead(&EventLogList
);
196 InitializeListHead(&EventLogFilterList
);
197 // TODO: Implement connection to remote computer...
198 // At the moment we only support the user local computer.
199 BuildLogListAndFilterList(NULL
);
201 // TODO: If the user wants to open an external event log with the Event Log Viewer
202 // (via the command line), it's here that the log should be opened.
204 /* Main message loop */
205 while (GetMessageW(&msg
, NULL
, 0, 0))
207 if (!TranslateAcceleratorW(hwndMainWindow
, hAccelTable
, &msg
))
209 TranslateMessage(&msg
);
210 DispatchMessageW(&msg
);
214 SetEvent(hStartStopEnumEvent
);
215 WaitForSingleObject(hThread
, INFINITE
);
216 CloseHandle(hThread
);
218 /* Free the filters list and the event logs list */
225 CloseHandle(hStartEnumEvent
);
226 if (hStartStopEnumEvent
)
227 CloseHandle(hStartStopEnumEvent
);
230 FreeLibrary(hRichEdit
);
232 return (int)msg
.wParam
;
236 /* GENERIC HELPER FUNCTIONS ***************************************************/
239 ShowLastWin32Error(VOID
)
242 LPWSTR lpMessageBuffer
;
244 dwError
= GetLastError();
245 if (dwError
== ERROR_SUCCESS
)
248 if (!FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER
|
249 FORMAT_MESSAGE_FROM_SYSTEM
|
250 FORMAT_MESSAGE_IGNORE_INSERTS
,
254 (LPWSTR
)&lpMessageBuffer
,
260 MessageBoxW(hwndMainWindow
, lpMessageBuffer
, szTitle
, MB_OK
| MB_ICONERROR
);
261 LocalFree(lpMessageBuffer
);
265 EventTimeToSystemTime(IN DWORD EventTime
,
266 OUT PSYSTEMTIME pSystemTime
)
268 SYSTEMTIME st1970
= { 1970, 1, 0, 1, 0, 0, 0, 0 };
276 uUCT
.ft
.dwHighDateTime
= 0;
277 uUCT
.ft
.dwLowDateTime
= EventTime
;
278 SystemTimeToFileTime(&st1970
, &u1970
.ft
);
279 uUCT
.ll
= uUCT
.ll
* 10000000 + u1970
.ll
;
280 FileTimeToLocalFileTime(&uUCT
.ft
, &ftLocal
);
281 FileTimeToSystemTime(&ftLocal
, pSystemTime
);
285 * This function takes in entry a path to a single DLL, in which
286 * the message string of ID dwMessageId has to be searched.
287 * The other parameters are similar to those of the FormatMessageW API.
290 GetMessageStringFromDll(
291 IN LPCWSTR lpMessageDll
,
292 IN DWORD dwFlags
, // If we always use the same flags, just remove this param...
293 IN DWORD dwMessageId
,
295 IN
va_list* Arguments OPTIONAL
)
299 LPWSTR lpMsgBuf
= NULL
;
301 hLibrary
= LoadLibraryExW(lpMessageDll
, NULL
,
302 /* LOAD_LIBRARY_AS_IMAGE_RESOURCE | */ LOAD_LIBRARY_AS_DATAFILE
);
303 if (hLibrary
== NULL
)
306 /* Sanitize dwFlags */
307 dwFlags
&= ~FORMAT_MESSAGE_FROM_STRING
;
308 dwFlags
|= FORMAT_MESSAGE_FROM_HMODULE
;
313 * Retrieve the message string without appending extra newlines.
314 * Wrap in SEH to protect from invalid string parameters.
318 dwLength
= FormatMessageW(dwFlags
,
319 /* FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
320 FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, */
328 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
333 * An exception occurred while calling FormatMessage, this is usually
334 * the sign that a parameter was invalid, either 'lpMsgBuf' was NULL
335 * but we did not pass the flag FORMAT_MESSAGE_ALLOCATE_BUFFER, or the
336 * array pointer 'Arguments' was NULL or did not contain enough elements,
337 * and we did not pass the flag FORMAT_MESSAGE_IGNORE_INSERTS, and the
338 * message string expected too many inserts.
339 * In this last case only, we can call again FormatMessage but ignore
340 * explicitly the inserts. The string that we will return to the user
341 * will not be pre-formatted.
343 if (((dwFlags
& FORMAT_MESSAGE_ALLOCATE_BUFFER
) || lpMsgBuf
) &&
344 !(dwFlags
& FORMAT_MESSAGE_IGNORE_INSERTS
))
346 /* Remove any possible harmful flags and always ignore inserts */
347 dwFlags
&= ~FORMAT_MESSAGE_ARGUMENT_ARRAY
;
348 dwFlags
|= FORMAT_MESSAGE_IGNORE_INSERTS
;
350 /* If this call also throws an exception, we are really dead */
351 dwLength
= FormatMessageW(dwFlags
,
357 NULL
/* Arguments */);
364 FreeLibrary(hLibrary
);
370 ASSERT(lpMsgBuf
== NULL
);
382 * This function takes in entry a comma-separated list of DLLs, in which
383 * the message string of ID dwMessageId has to be searched.
384 * The other parameters are similar to those of the FormatMessageW API.
387 GetMessageStringFromDllList(
388 IN LPCWSTR lpMessageDllList
,
389 IN DWORD dwFlags
, // If we always use the same flags, just remove this param...
390 IN DWORD dwMessageId
,
392 IN
va_list* Arguments OPTIONAL
)
394 BOOL Success
= FALSE
;
396 LPWSTR szMessageDllList
;
398 LPWSTR lpMsgBuf
= NULL
;
400 /* Allocate a local buffer for the DLL list that can be tokenized */
401 // TODO: Optimize that!! Maybe we can cleverly use lpMessageDllList in read/write mode
402 // and cleverly temporarily replace the ';' by UNICODE_NULL, do our job, then reverse the change.
403 cbLength
= (wcslen(lpMessageDllList
) + 1) * sizeof(WCHAR
);
404 szMessageDllList
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, cbLength
);
405 if (!szMessageDllList
)
407 RtlCopyMemory(szMessageDllList
, lpMessageDllList
, cbLength
);
409 /* Loop through the list of message DLLs */
410 szDll
= wcstok(szMessageDllList
, EVENT_DLL_SEPARATOR
);
411 while ((szDll
!= NULL
) && !Success
)
413 // Uses LANG_USER_DEFAULT
414 lpMsgBuf
= GetMessageStringFromDll(szDll
,
421 /* The ID was found and the message was formatted */
427 * The DLL could not be loaded, or the message could not be found,
428 * try the next DLL, if any.
430 szDll
= wcstok(NULL
, EVENT_DLL_SEPARATOR
);
433 HeapFree(GetProcessHeap(), 0, szMessageDllList
);
441 LPWSTR pStartingAddress
; // Pointer to the beginning of a parameter string in pMessage
442 LPWSTR pEndingAddress
; // Pointer to the end of a parameter string in pMessage
443 DWORD pParameterID
; // Parameter identifier found in pMessage
444 LPWSTR pParameter
; // Actual parameter string
445 } param_strings_format_data
;
448 ApplyParameterStringsToMessage(
449 IN LPCWSTR lpMessageDllList
,
450 IN BOOL bMessagePreFormatted
,
451 IN CONST LPCWSTR pMessage
,
452 OUT LPWSTR
* pFinalMessage
)
455 * This code is heavily adapted from the MSDN example:
456 * https://msdn.microsoft.com/en-us/library/windows/desktop/bb427356.aspx
460 DWORD Status
= ERROR_SUCCESS
;
461 DWORD dwParamCount
= 0; // Number of insertion strings found in pMessage
462 size_t cchBuffer
= 0; // Size of the buffer in characters
463 size_t cchParams
= 0; // Number of characters in all the parameter strings
466 param_strings_format_data
* pParamData
= NULL
; // Array of pointers holding information about each parameter string in pMessage
467 LPWSTR pTempMessage
= (LPWSTR
)pMessage
;
468 LPWSTR pTempFinalMessage
= NULL
;
470 *pFinalMessage
= NULL
;
472 /* Determine the number of parameter insertion strings in pMessage */
473 if (bMessagePreFormatted
)
475 while ((pTempMessage
= wcschr(pTempMessage
, L
'%')))
478 if (isdigit(*pTempMessage
))
481 while (isdigit(*++pTempMessage
)) ;
487 while ((pTempMessage
= wcsstr(pTempMessage
, L
"%%")))
490 if (isdigit(*pTempMessage
))
493 while (isdigit(*++pTempMessage
)) ;
498 /* If there are no parameter insertion strings in pMessage, just return */
499 if (dwParamCount
== 0)
501 // *pFinalMessage = NULL;
505 /* Allocate the array of parameter string format data */
506 pParamData
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, dwParamCount
* sizeof(param_strings_format_data
));
509 Status
= ERROR_OUTOFMEMORY
;
514 * Retrieve each parameter in pMessage and the beginning and end of the
515 * insertion string, as well as the message identifier of the parameter.
517 pTempMessage
= (LPWSTR
)pMessage
;
518 if (bMessagePreFormatted
)
520 while ((pTempMessage
= wcschr(pTempMessage
, L
'%')) && (i
< dwParamCount
))
523 if (isdigit(*pTempMessage
))
525 pParamData
[i
].pStartingAddress
= pTempMessage
-1;
526 pParamData
[i
].pParameterID
= (DWORD
)_wtol(pTempMessage
);
528 while (isdigit(*++pTempMessage
)) ;
530 pParamData
[i
].pEndingAddress
= pTempMessage
;
537 while ((pTempMessage
= wcsstr(pTempMessage
, L
"%%")) && (i
< dwParamCount
))
540 if (isdigit(*pTempMessage
))
542 pParamData
[i
].pStartingAddress
= pTempMessage
-2;
543 pParamData
[i
].pParameterID
= (DWORD
)_wtol(pTempMessage
);
545 while (isdigit(*++pTempMessage
)) ;
547 pParamData
[i
].pEndingAddress
= pTempMessage
;
553 /* Retrieve each parameter string */
554 for (i
= 0; i
< dwParamCount
; i
++)
556 // pParamData[i].pParameter = GetMessageString(pParamData[i].pParameterID, 0, NULL);
557 pParamData
[i
].pParameter
=
558 GetMessageStringFromDllList(lpMessageDllList
,
559 FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_HMODULE
|
560 FORMAT_MESSAGE_IGNORE_INSERTS
| FORMAT_MESSAGE_MAX_WIDTH_MASK
,
561 pParamData
[i
].pParameterID
,
563 if (!pParamData
[i
].pParameter
)
565 /* Skip the insertion string */
569 cchParams
+= wcslen(pParamData
[i
].pParameter
);
573 * Allocate the final message buffer, the size of which is based on the
574 * length of the original message and the length of each parameter string.
576 cchBuffer
= wcslen(pMessage
) + cchParams
+ 1;
577 *pFinalMessage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, cchBuffer
* sizeof(WCHAR
));
580 Status
= ERROR_OUTOFMEMORY
;
584 pTempFinalMessage
= *pFinalMessage
;
586 /* Build the final message string */
587 pTempMessage
= (LPWSTR
)pMessage
;
588 for (i
= 0; i
< dwParamCount
; i
++)
590 /* Append the segment from pMessage */
591 cch
= pParamData
[i
].pStartingAddress
- pTempMessage
;
592 StringCchCopyNW(pTempFinalMessage
, cchBuffer
, pTempMessage
, cch
);
593 pTempMessage
= pParamData
[i
].pEndingAddress
;
595 pTempFinalMessage
+= cch
;
597 /* Append the parameter string */
598 if (pParamData
[i
].pParameter
)
600 StringCchCopyW(pTempFinalMessage
, cchBuffer
, pParamData
[i
].pParameter
);
601 cch
= wcslen(pParamData
[i
].pParameter
); // pTempFinalMessage
606 * We failed to retrieve the parameter string before, so just
607 * place back the original string placeholder.
609 cch
= pParamData
[i
].pEndingAddress
/* == pTempMessage */ - pParamData
[i
].pStartingAddress
;
610 StringCchCopyNW(pTempFinalMessage
, cchBuffer
, pParamData
[i
].pStartingAddress
, cch
);
611 // cch = wcslen(pTempFinalMessage);
614 pTempFinalMessage
+= cch
;
617 /* Append the last segment from pMessage */
618 StringCchCopyW(pTempFinalMessage
, cchBuffer
, pTempMessage
);
622 // if (Status != ERROR_SUCCESS)
623 // *pFinalMessage = NULL;
627 for (i
= 0; i
< dwParamCount
; i
++)
629 if (pParamData
[i
].pParameter
)
630 LocalFree(pParamData
[i
].pParameter
);
633 HeapFree(GetProcessHeap(), 0, pParamData
);
641 * The following functions were adapted from
642 * shell32!dialogs/filedefext.cpp:``SH_...'' functions.
646 FormatInteger(LONGLONG Num
, LPWSTR pwszResult
, UINT cchResultMax
)
649 WCHAR wszDecimalSep
[8], wszThousandSep
[8];
651 WCHAR wszGrouping
[12];
656 // Print the number in uniform mode
657 swprintf(wszNumber
, L
"%I64u", Num
);
659 // Get system strings for decimal and thousand separators.
660 GetLocaleInfoW(LOCALE_USER_DEFAULT
, LOCALE_SDECIMAL
, wszDecimalSep
, _countof(wszDecimalSep
));
661 GetLocaleInfoW(LOCALE_USER_DEFAULT
, LOCALE_STHOUSAND
, wszThousandSep
, _countof(wszThousandSep
));
663 // Initialize format for printing the number in bytes
664 ZeroMemory(&nf
, sizeof(nf
));
665 nf
.lpDecimalSep
= wszDecimalSep
;
666 nf
.lpThousandSep
= wszThousandSep
;
668 // Get system string for groups separator
669 cchGrouping
= GetLocaleInfoW(LOCALE_USER_DEFAULT
,
672 _countof(wszGrouping
));
674 // Convert grouping specs from string to integer
675 for (i
= 0; i
< cchGrouping
; i
++)
677 WCHAR wch
= wszGrouping
[i
];
679 if (wch
>= L
'0' && wch
<= L
'9')
680 nf
.Grouping
= nf
.Grouping
* 10 + (wch
- L
'0');
681 else if (wch
!= L
';')
685 if ((nf
.Grouping
% 10) == 0)
691 cchResult
= GetNumberFormatW(LOCALE_USER_DEFAULT
,
701 // GetNumberFormatW returns number of characters including UNICODE_NULL
702 return cchResult
- 1;
706 FormatByteSize(LONGLONG cbSize
, LPWSTR pwszResult
, UINT cchResultMax
)
712 /* Write formated bytes count */
713 cchWritten
= FormatInteger(cbSize
, pwszResult
, cchResultMax
);
717 /* Copy " bytes" to buffer */
718 pwszEnd
= pwszResult
+ cchWritten
;
719 cchRemaining
= cchResultMax
- cchWritten
;
720 StringCchCopyExW(pwszEnd
, cchRemaining
, L
" ", &pwszEnd
, &cchRemaining
, 0);
721 cchWritten
= LoadStringW(hInst
, IDS_BYTES_FORMAT
, pwszEnd
, cchRemaining
);
722 cchRemaining
-= cchWritten
;
724 return cchResultMax
- cchRemaining
;
728 FormatFileSizeWithBytes(const PULARGE_INTEGER lpQwSize
, LPWSTR pwszResult
, UINT cchResultMax
)
734 /* Format bytes in KBs, MBs etc */
735 if (StrFormatByteSizeW(lpQwSize
->QuadPart
, pwszResult
, cchResultMax
) == NULL
)
738 /* If there is less bytes than 1KB, we have nothing to do */
739 if (lpQwSize
->QuadPart
< 1024)
742 /* Concatenate " (" */
743 cchWritten
= wcslen(pwszResult
);
744 pwszEnd
= pwszResult
+ cchWritten
;
745 cchRemaining
= cchResultMax
- cchWritten
;
746 StringCchCopyExW(pwszEnd
, cchRemaining
, L
" (", &pwszEnd
, &cchRemaining
, 0);
748 /* Write formated bytes count */
749 cchWritten
= FormatByteSize(lpQwSize
->QuadPart
, pwszEnd
, cchRemaining
);
750 pwszEnd
+= cchWritten
;
751 cchRemaining
-= cchWritten
;
753 /* Copy ")" to the buffer */
754 StringCchCopyW(pwszEnd
, cchRemaining
, L
")");
759 /* Adapted from shell32!dialogs/filedefext.cpp:``CFileDefExt::GetFileTimeString'' */
761 GetFileTimeString(LPFILETIME lpFileTime
, LPWSTR pwszResult
, UINT cchResult
)
766 size_t cchRemaining
= cchResult
;
767 LPWSTR pwszEnd
= pwszResult
;
769 if (!FileTimeToLocalFileTime(lpFileTime
, &ft
) || !FileTimeToSystemTime(&ft
, &st
))
772 cchWritten
= GetDateFormatW(LOCALE_USER_DEFAULT
, DATE_LONGDATE
, &st
, NULL
, pwszEnd
, cchRemaining
);
774 --cchWritten
; // GetDateFormatW returns count with terminating zero
776 // ERR("GetDateFormatW failed\n");
778 cchRemaining
-= cchWritten
;
779 pwszEnd
+= cchWritten
;
781 StringCchCopyExW(pwszEnd
, cchRemaining
, L
", ", &pwszEnd
, &cchRemaining
, 0);
783 cchWritten
= GetTimeFormatW(LOCALE_USER_DEFAULT
, 0, &st
, NULL
, pwszEnd
, cchRemaining
);
785 --cchWritten
; // GetTimeFormatW returns count with terminating zero
787 // ERR("GetTimeFormatW failed\n");
794 TreeViewAddItem(IN HWND hTreeView
,
795 IN HTREEITEM hParent
,
798 IN INT SelectedImage
,
801 TV_INSERTSTRUCTW Insert
;
803 ZeroMemory(&Insert
, sizeof(Insert
));
805 Insert
.item
.mask
= TVIF_TEXT
| TVIF_PARAM
| TVIF_IMAGE
| TVIF_SELECTEDIMAGE
;
806 Insert
.hInsertAfter
= TVI_LAST
;
807 Insert
.hParent
= hParent
;
808 Insert
.item
.pszText
= lpText
;
809 Insert
.item
.iImage
= Image
;
810 Insert
.item
.iSelectedImage
= SelectedImage
;
811 Insert
.item
.lParam
= lParam
;
813 Insert
.item
.mask
|= TVIF_STATE
;
814 Insert
.item
.stateMask
= TVIS_OVERLAYMASK
;
815 Insert
.item
.state
= INDEXTOOVERLAYMASK(1);
817 return TreeView_InsertItem(hTreeView
, &Insert
);
821 /* LOG HELPER FUNCTIONS *******************************************************/
824 AllocEventLog(IN PCWSTR ComputerName OPTIONAL
,
831 /* Allocate a new event log entry */
832 EventLog
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*EventLog
));
836 /* Allocate the computer name string (optional) and copy it */
839 cchName
= wcslen(ComputerName
) + 1;
840 EventLog
->ComputerName
= HeapAlloc(GetProcessHeap(), 0, cchName
* sizeof(WCHAR
));
841 if (EventLog
->ComputerName
)
842 StringCchCopyW(EventLog
->ComputerName
, cchName
, ComputerName
);
845 /* Allocate the event log name string and copy it */
846 cchName
= wcslen(LogName
) + 1;
847 EventLog
->LogName
= HeapAlloc(GetProcessHeap(), 0, cchName
* sizeof(WCHAR
));
848 if (!EventLog
->LogName
)
850 HeapFree(GetProcessHeap(), 0, EventLog
);
853 StringCchCopyW(EventLog
->LogName
, cchName
, LogName
);
855 EventLog
->Permanent
= Permanent
;
861 EventLog_Free(IN PEVENTLOG EventLog
)
863 if (EventLog
->LogName
)
864 HeapFree(GetProcessHeap(), 0, EventLog
->LogName
);
866 if (EventLog
->FileName
)
867 HeapFree(GetProcessHeap(), 0, EventLog
->FileName
);
869 HeapFree(GetProcessHeap(), 0, EventLog
);
874 AllocAndCopyMultiStr(IN PCWSTR MultiStr OPTIONAL
)
882 pStr
= (PWSTR
)MultiStr
;
883 while (*pStr
) pStr
+= (wcslen(pStr
) + 1);
884 Length
= MultiStr
- pStr
+ 2;
886 pStr
= HeapAlloc(GetProcessHeap(), 0, Length
* sizeof(WCHAR
));
887 // NOTE: If we failed allocating the string, then fall back into no filter!
889 RtlCopyMemory(pStr
, MultiStr
, Length
* sizeof(WCHAR
));
895 AllocEventLogFilter(// IN PCWSTR FilterName,
899 IN BOOL AuditSuccess
,
900 IN BOOL AuditFailure
,
901 IN PCWSTR Sources OPTIONAL
,
902 IN PCWSTR Users OPTIONAL
,
903 IN PCWSTR ComputerNames OPTIONAL
,
904 IN ULONG NumOfEventLogs
,
905 IN PEVENTLOG
* EventLogs
)
907 PEVENTLOGFILTER EventLogFilter
;
909 /* Allocate a new event log filter entry, big enough to accommodate the list of logs */
910 EventLogFilter
= HeapAlloc(GetProcessHeap(),
912 FIELD_OFFSET(EVENTLOGFILTER
, EventLogs
[NumOfEventLogs
]));
916 EventLogFilter
->Information
= Information
;
917 EventLogFilter
->Warning
= Warning
;
918 EventLogFilter
->Error
= Error
;
919 EventLogFilter
->AuditSuccess
= AuditSuccess
;
920 EventLogFilter
->AuditFailure
= AuditFailure
;
922 /* Allocate and copy the sources, users, and computers multi-strings */
923 EventLogFilter
->Sources
= AllocAndCopyMultiStr(Sources
);
924 EventLogFilter
->Users
= AllocAndCopyMultiStr(Users
);
925 EventLogFilter
->ComputerNames
= AllocAndCopyMultiStr(ComputerNames
);
927 /* Copy the list of event logs */
928 EventLogFilter
->NumOfEventLogs
= NumOfEventLogs
;
929 RtlCopyMemory(EventLogFilter
->EventLogs
, EventLogs
, NumOfEventLogs
* sizeof(PEVENTLOG
));
931 /* Initialize the filter reference count */
932 EventLogFilter
->ReferenceCount
= 1;
934 return EventLogFilter
;
938 EventLogFilter_Free(IN PEVENTLOGFILTER EventLogFilter
)
940 if (EventLogFilter
->Sources
)
941 HeapFree(GetProcessHeap(), 0, EventLogFilter
->Sources
);
943 if (EventLogFilter
->Users
)
944 HeapFree(GetProcessHeap(), 0, EventLogFilter
->Users
);
946 if (EventLogFilter
->ComputerNames
)
947 HeapFree(GetProcessHeap(), 0, EventLogFilter
->ComputerNames
);
949 HeapFree(GetProcessHeap(), 0, EventLogFilter
);
952 LONG
EventLogFilter_AddRef(IN PEVENTLOGFILTER EventLogFilter
)
954 ASSERT(EventLogFilter
);
955 return InterlockedIncrement(&EventLogFilter
->ReferenceCount
);
958 LONG
EventLogFilter_Release(IN PEVENTLOGFILTER EventLogFilter
)
962 ASSERT(EventLogFilter
);
964 /* When the reference count reaches zero, delete the filter */
965 RefCount
= InterlockedDecrement(&EventLogFilter
->ReferenceCount
);
968 /* Remove the filter from the list */
969 /** RemoveEntryList(&EventLogFilter->ListEntry); **/
970 EventLogFilter_Free(EventLogFilter
);
983 c
= s
+ wcslen(s
) - 1;
984 while (c
>= s
&& iswspace(*c
))
991 GetEventMessageFileDLL(IN LPCWSTR lpLogName
,
992 IN LPCWSTR SourceName
,
993 IN LPCWSTR EntryName
,
994 OUT PWCHAR lpModuleName
) // TODO: Add IN DWORD BufLen
996 BOOL Success
= FALSE
;
999 WCHAR szModuleName
[MAX_PATH
];
1000 WCHAR szKeyName
[MAX_PATH
];
1001 HKEY hLogKey
= NULL
;
1002 HKEY hSourceKey
= NULL
;
1004 StringCbCopyW(szKeyName
, sizeof(szKeyName
), EVENTLOG_BASE_KEY
);
1005 StringCbCatW(szKeyName
, sizeof(szKeyName
), lpLogName
);
1007 Result
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
,
1012 if (Result
!= ERROR_SUCCESS
)
1015 Result
= RegOpenKeyExW(hLogKey
,
1020 if (Result
!= ERROR_SUCCESS
)
1022 RegCloseKey(hLogKey
);
1026 dwSize
= sizeof(szModuleName
);
1027 Result
= RegQueryValueExW(hSourceKey
,
1031 (LPBYTE
)szModuleName
,
1033 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_EXPAND_SZ
&& Type
!= REG_SZ
))
1035 szModuleName
[0] = UNICODE_NULL
;
1039 /* NULL-terminate the string and expand it */
1040 szModuleName
[dwSize
/ sizeof(WCHAR
) - 1] = UNICODE_NULL
;
1041 ExpandEnvironmentStringsW(szModuleName
, lpModuleName
, ARRAYSIZE(szModuleName
));
1045 RegCloseKey(hSourceKey
);
1046 RegCloseKey(hLogKey
);
1052 GetEventCategory(IN LPCWSTR KeyName
,
1053 IN LPCWSTR SourceName
,
1054 IN PEVENTLOGRECORD pevlr
,
1055 OUT PWCHAR CategoryName
) // TODO: Add IN DWORD BufLen
1057 BOOL Success
= FALSE
;
1058 WCHAR szMessageDLL
[MAX_PATH
];
1059 LPWSTR lpMsgBuf
= NULL
;
1061 if (!GetEventMessageFileDLL(KeyName
, SourceName
, EVENT_CATEGORY_MESSAGE_FILE
, szMessageDLL
))
1064 /* Retrieve the message string without appending extra newlines */
1066 GetMessageStringFromDllList(szMessageDLL
,
1067 FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_HMODULE
|
1068 FORMAT_MESSAGE_IGNORE_INSERTS
| FORMAT_MESSAGE_MAX_WIDTH_MASK
,
1069 pevlr
->EventCategory
,
1070 EVENT_MESSAGE_FILE_BUFFER
,
1074 /* Trim the string */
1075 TrimNulls(lpMsgBuf
);
1077 /* Copy the category name */
1078 StringCchCopyW(CategoryName
, MAX_PATH
, lpMsgBuf
);
1080 /* Free the buffer allocated by FormatMessage */
1081 LocalFree(lpMsgBuf
);
1083 /* The ID was found and the message was formatted */
1090 if (pevlr
->EventCategory
!= 0)
1092 StringCchPrintfW(CategoryName
, MAX_PATH
, L
"(%lu)", pevlr
->EventCategory
);
1101 BOOL
// NOTE: Used by evtdetctl.c
1102 GetEventMessage(IN LPCWSTR KeyName
,
1103 IN LPCWSTR SourceName
,
1104 IN PEVENTLOGRECORD pevlr
,
1105 OUT PWCHAR EventText
) // TODO: Add IN DWORD BufLen
1107 BOOL Success
= FALSE
;
1110 WCHAR SourceModuleName
[1024];
1111 WCHAR ParameterModuleName
[1024];
1112 BOOL IsParamModNameCached
= FALSE
;
1113 LPWSTR lpMsgBuf
= NULL
;
1114 LPWSTR szStringArray
, szMessage
;
1115 LPWSTR
*szArguments
;
1117 /* Get the event string array */
1118 szStringArray
= (LPWSTR
)((LPBYTE
)pevlr
+ pevlr
->StringOffset
);
1120 /* NOTE: GetEventMessageFileDLL can return a comma-separated list of DLLs */
1121 if (!GetEventMessageFileDLL(KeyName
, SourceName
, EVENT_MESSAGE_FILE
, SourceModuleName
))
1124 /* Allocate space for insertion strings */
1125 szArguments
= HeapAlloc(GetProcessHeap(), 0, pevlr
->NumStrings
* sizeof(LPVOID
));
1129 if (!IsParamModNameCached
)
1131 /* Now that the parameter file list is loaded, no need to reload it at the next run! */
1132 IsParamModNameCached
= GetEventMessageFileDLL(KeyName
, SourceName
, EVENT_PARAMETER_MESSAGE_FILE
, ParameterModuleName
);
1133 // FIXME: If the string loading failed the first time, no need to retry it just after???
1136 if (IsParamModNameCached
)
1138 /* Not yet support for reading messages from parameter message DLL */
1141 szMessage
= szStringArray
;
1144 * We do some hackish preformatting of the cached event strings...
1145 * That's because after we pass the string to FormatMessage
1146 * (via GetMessageStringFromDllList) with the FORMAT_MESSAGE_ARGUMENT_ARRAY
1147 * flag, instead of ignoring the insertion parameters and do the formatting
1148 * by ourselves. Therefore, the resulting string should have the parameter
1149 * string placeholders starting with a single '%' instead of a mix of one
1152 /* HACK part 1: Compute the full length of the string array */
1154 for (i
= 0; i
< pevlr
->NumStrings
; i
++)
1156 szMessage
+= wcslen(szMessage
) + 1;
1158 cch
= szMessage
- szStringArray
;
1160 /* HACK part 2: Now do the HACK proper! */
1161 szMessage
= szStringArray
;
1162 for (i
= 0; i
< pevlr
->NumStrings
; i
++)
1164 lpMsgBuf
= szMessage
;
1165 while ((lpMsgBuf
= wcsstr(lpMsgBuf
, L
"%%")))
1167 if (isdigit(lpMsgBuf
[2]))
1169 RtlMoveMemory(lpMsgBuf
, lpMsgBuf
+1, ((szStringArray
+ cch
) - lpMsgBuf
- 1) * sizeof(WCHAR
));
1173 szArguments
[i
] = szMessage
;
1174 szMessage
+= wcslen(szMessage
) + 1;
1177 /* Retrieve the message string without appending extra newlines */
1179 GetMessageStringFromDllList(SourceModuleName
,
1180 FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_HMODULE
|
1181 FORMAT_MESSAGE_ARGUMENT_ARRAY
| FORMAT_MESSAGE_MAX_WIDTH_MASK
,
1184 (va_list*)szArguments
);
1187 /* Trim the string */
1188 TrimNulls(lpMsgBuf
);
1191 Success
= (ApplyParameterStringsToMessage(ParameterModuleName
,
1194 &szMessage
) == ERROR_SUCCESS
);
1195 if (Success
&& szMessage
)
1197 /* Free the buffer allocated by FormatMessage */
1198 LocalFree(lpMsgBuf
);
1199 lpMsgBuf
= szMessage
;
1202 /* Copy the event text */
1203 StringCchCopyW(EventText
, EVENT_MESSAGE_EVENTTEXT_BUFFER
, lpMsgBuf
);
1205 /* Free the buffer allocated by FormatMessage */
1206 LocalFree(lpMsgBuf
);
1209 HeapFree(GetProcessHeap(), 0, szArguments
);
1214 /* Get a read-only pointer to the "event-not-found" string */
1215 lpMsgBuf
= HeapAlloc(GetProcessHeap(), 0, EVENT_MESSAGE_EVENTTEXT_BUFFER
* sizeof(WCHAR
));
1216 LoadStringW(hInst
, IDS_EVENTSTRINGIDNOTFOUND
, lpMsgBuf
, EVENT_MESSAGE_EVENTTEXT_BUFFER
);
1217 StringCchPrintfW(EventText
, EVENT_MESSAGE_EVENTTEXT_BUFFER
, lpMsgBuf
, (pevlr
->EventID
& 0xFFFF), SourceName
);
1219 /* Append the strings */
1220 szMessage
= szStringArray
;
1221 for (i
= 0; i
< pevlr
->NumStrings
; i
++)
1223 StringCchCatW(EventText
, EVENT_MESSAGE_EVENTTEXT_BUFFER
, szMessage
);
1224 StringCchCatW(EventText
, EVENT_MESSAGE_EVENTTEXT_BUFFER
, L
"\n");
1225 szMessage
+= wcslen(szMessage
) + 1;
1233 GetEventType(IN WORD dwEventType
,
1234 OUT PWCHAR eventTypeText
) // TODO: Add IN DWORD BufLen
1236 switch (dwEventType
)
1238 case EVENTLOG_ERROR_TYPE
:
1239 LoadStringW(hInst
, IDS_EVENTLOG_ERROR_TYPE
, eventTypeText
, MAX_LOADSTRING
);
1241 case EVENTLOG_WARNING_TYPE
:
1242 LoadStringW(hInst
, IDS_EVENTLOG_WARNING_TYPE
, eventTypeText
, MAX_LOADSTRING
);
1244 case EVENTLOG_INFORMATION_TYPE
:
1245 LoadStringW(hInst
, IDS_EVENTLOG_INFORMATION_TYPE
, eventTypeText
, MAX_LOADSTRING
);
1247 case EVENTLOG_SUCCESS
:
1248 LoadStringW(hInst
, IDS_EVENTLOG_SUCCESS
, eventTypeText
, MAX_LOADSTRING
);
1250 case EVENTLOG_AUDIT_SUCCESS
:
1251 LoadStringW(hInst
, IDS_EVENTLOG_AUDIT_SUCCESS
, eventTypeText
, MAX_LOADSTRING
);
1253 case EVENTLOG_AUDIT_FAILURE
:
1254 LoadStringW(hInst
, IDS_EVENTLOG_AUDIT_FAILURE
, eventTypeText
, MAX_LOADSTRING
);
1257 LoadStringW(hInst
, IDS_EVENTLOG_UNKNOWN_TYPE
, eventTypeText
, MAX_LOADSTRING
);
1263 GetEventUserName(IN PEVENTLOGRECORD pelr
,
1264 IN OUT PSID pLastSid
,
1265 OUT PWCHAR pszUser
) // TODO: Add IN DWORD BufLen
1270 WCHAR szDomain
[1024];
1272 DWORD cchName
= ARRAYSIZE(szName
);
1273 DWORD cchDomain
= ARRAYSIZE(szDomain
);
1276 /* Point to the SID */
1277 pCurrentSid
= (PSID
)((LPBYTE
)pelr
+ pelr
->UserSidOffset
);
1279 if (!IsValidSid(pCurrentSid
))
1284 else if (pLastSid
&& EqualSid(pLastSid
, pCurrentSid
))
1290 if (pelr
->UserSidLength
> 0)
1293 * Try to retrieve the user account name and domain name corresponding
1294 * to the SID. If it cannot be retrieved, try to convert the SID to a
1295 * string-form. It should not be bigger than the user-provided buffer
1296 * 'pszUser', otherwise we return an error.
1298 if (LookupAccountSidW(NULL
, // FIXME: Use computer name? From the particular event?
1306 StringCchCopyW(pszUser
, MAX_PATH
, szName
);
1309 else if (ConvertSidToStringSidW(pCurrentSid
, &StringSid
))
1311 /* Copy the string only if the user-provided buffer is big enough */
1312 if (wcslen(StringSid
) + 1 <= MAX_PATH
) // + 1 for NULL-terminator
1314 StringCchCopyW(pszUser
, MAX_PATH
, StringSid
);
1319 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1323 /* Free the allocated buffer */
1324 LocalFree(StringSid
);
1328 pLastSid
= Success
? pCurrentSid
: NULL
;
1334 static VOID
FreeRecords(VOID
)
1341 for (iIndex
= 0; iIndex
< g_TotalRecords
; iIndex
++)
1343 if (g_RecordPtrs
[iIndex
])
1344 HeapFree(GetProcessHeap(), 0, g_RecordPtrs
[iIndex
]);
1346 HeapFree(GetProcessHeap(), 0, g_RecordPtrs
);
1347 g_RecordPtrs
= NULL
;
1352 FilterByType(IN PEVENTLOGFILTER EventLogFilter
,
1353 IN PEVENTLOGRECORD pevlr
)
1355 if ((pevlr
->EventType
== EVENTLOG_SUCCESS
&& !EventLogFilter
->Information
) ||
1356 (pevlr
->EventType
== EVENTLOG_INFORMATION_TYPE
&& !EventLogFilter
->Information
) ||
1357 (pevlr
->EventType
== EVENTLOG_WARNING_TYPE
&& !EventLogFilter
->Warning
) ||
1358 (pevlr
->EventType
== EVENTLOG_ERROR_TYPE
&& !EventLogFilter
->Error
) ||
1359 (pevlr
->EventType
== EVENTLOG_AUDIT_SUCCESS
&& !EventLogFilter
->AuditSuccess
) ||
1360 (pevlr
->EventType
== EVENTLOG_AUDIT_FAILURE
&& !EventLogFilter
->AuditFailure
))
1368 FilterByString(IN PCWSTR FilterString
, // This is a multi-string
1373 /* The filter string is NULL so it does not filter anything */
1378 * If the filter string filters for an empty string AND the source string
1379 * is an empty string, we have a match (particular case of the last one).
1381 if (!*FilterString
&& !*String
)
1384 // if (*FilterString || *String)
1387 * If the filter string is empty BUT the source string is not empty,
1388 * OR vice-versa, we cannot have a match.
1390 if ( (!*FilterString
&& *String
) || (*FilterString
&& !*String
) )
1394 * If the filter string filters for at least a non-empty string,
1395 * browse it and search for a string that matches the source string.
1397 // else if (*FilterString && *String)
1399 pStr
= FilterString
;
1402 if (wcsicmp(pStr
, String
) == 0)
1404 /* We have a match, break the loop */
1408 pStr
+= (wcslen(pStr
) + 1);
1410 if (!*pStr
) // && *String
1412 /* We do not have a match */
1417 /* We have a match */
1422 * The events enumerator thread.
1425 EnumEventsThread(IN LPVOID lpParameter
)
1427 PEVENTLOGFILTER EventLogFilter
= (PEVENTLOGFILTER
)lpParameter
;
1428 LPWSTR lpMachineName
= NULL
; // EventLogFilter->ComputerName;
1433 PEVENTLOGRECORD pEvlr
;
1436 DWORD dwWanted
, dwRead
, dwNeeded
, dwStatus
= ERROR_SUCCESS
;
1437 DWORD dwTotalRecords
= 0, dwCurrentRecord
= 0;
1438 DWORD dwFlags
, dwMaxLength
;
1439 size_t cchRemaining
;
1440 LPWSTR lpszSourceName
;
1441 LPWSTR lpszComputerName
;
1442 BOOL bResult
= TRUE
; /* Read succeeded */
1443 HANDLE hProcessHeap
= GetProcessHeap();
1444 PSID pLastSid
= NULL
;
1446 UINT uStep
= 0, uStepAt
= 0, uPos
= 0;
1448 WCHAR szWindowTitle
[MAX_PATH
];
1449 WCHAR szStatusText
[MAX_PATH
];
1450 WCHAR szLocalDate
[MAX_PATH
];
1451 WCHAR szLocalTime
[MAX_PATH
];
1452 WCHAR szEventID
[MAX_PATH
];
1453 WCHAR szEventTypeText
[MAX_LOADSTRING
];
1454 WCHAR szCategoryID
[MAX_PATH
];
1455 WCHAR szUsername
[MAX_PATH
];
1456 WCHAR szNoUsername
[MAX_PATH
];
1457 WCHAR szCategory
[MAX_PATH
];
1458 WCHAR szNoCategory
[MAX_PATH
];
1459 PWCHAR lpTitleTemplateEnd
;
1462 LVITEMW lviEventItem
;
1464 /* Save the current event log filter globally */
1465 EventLogFilter_AddRef(EventLogFilter
);
1466 ActiveFilter
= EventLogFilter
;
1468 /* Disable list view redraw */
1469 SendMessageW(hwndListView
, WM_SETREDRAW
, FALSE
, 0);
1471 /* Clear the list view and free the cached records */
1472 ListView_DeleteAllItems(hwndListView
);
1475 SendMessageW(hwndListView
, LVM_PROGRESS
, 0, TRUE
);
1476 ProgressBar_SetRange(hwndStatusProgress
, 0);
1477 StatusBar_SetText(hwndStatus
, 0, NULL
);
1478 ShowWindow(hwndStatusProgress
, SW_SHOW
);
1480 /* Do a loop over the logs enumerated in the filter */
1481 // FIXME: For now we only support 1 event log per filter!
1483 // for (LogIndex = 0; LogIndex < EventLogFilter->NumOfEventLogs; ++LogIndex)
1486 EventLog
= EventLogFilter
->EventLogs
[LogIndex
];
1488 /* Open the event log */
1489 if (EventLog
->Permanent
)
1490 hEventLog
= OpenEventLogW(EventLog
->ComputerName
, EventLog
->LogName
);
1492 hEventLog
= OpenBackupEventLogW(EventLog
->ComputerName
, EventLog
->LogName
); // FileName
1494 if (hEventLog
== NULL
)
1496 ShowLastWin32Error();
1500 // GetOldestEventLogRecord(hEventLog, &dwThisRecord);
1502 /* Get the total number of event log records */
1503 GetNumberOfEventLogRecords(hEventLog
, &dwTotalRecords
);
1505 if (dwTotalRecords
> 0)
1507 EnableMenuItem(hMainMenu
, IDM_CLEAR_EVENTS
, MF_BYCOMMAND
| MF_ENABLED
);
1508 EnableMenuItem(hMainMenu
, IDM_SAVE_EVENTLOG
, MF_BYCOMMAND
| MF_ENABLED
);
1512 EnableMenuItem(hMainMenu
, IDM_CLEAR_EVENTS
, MF_BYCOMMAND
| MF_GRAYED
);
1513 EnableMenuItem(hMainMenu
, IDM_SAVE_EVENTLOG
, MF_BYCOMMAND
| MF_GRAYED
);
1516 /* Set up the event records cache */
1517 g_RecordPtrs
= HeapAlloc(hProcessHeap
, HEAP_ZERO_MEMORY
, dwTotalRecords
* sizeof(*g_RecordPtrs
));
1520 // ShowLastWin32Error();
1523 g_TotalRecords
= dwTotalRecords
;
1525 if (WaitForSingleObject(hStopEnumEvent
, 0) == WAIT_OBJECT_0
)
1528 LoadStringW(hInst
, IDS_NOT_AVAILABLE
, szNoUsername
, ARRAYSIZE(szNoUsername
));
1529 LoadStringW(hInst
, IDS_NONE
, szNoCategory
, ARRAYSIZE(szNoCategory
));
1531 ProgressBar_SetRange(hwndStatusProgress
, MAKELPARAM(0, 100));
1532 uStepAt
= (dwTotalRecords
/ 100) + 1;
1534 dwFlags
= EVENTLOG_SEQUENTIAL_READ
| (NewestEventsFirst
? EVENTLOG_FORWARDS_READ
: EVENTLOG_BACKWARDS_READ
);
1536 /* 0x7ffff is the maximum buffer size ReadEventLog will accept */
1538 pEvlr
= HeapAlloc(hProcessHeap
, 0, dwWanted
);
1543 while (dwStatus
== ERROR_SUCCESS
)
1545 bResult
= ReadEventLogW(hEventLog
, dwFlags
, 0, pEvlr
, dwWanted
, &dwRead
, &dwNeeded
);
1546 dwStatus
= GetLastError();
1548 if (!bResult
&& dwStatus
== ERROR_INSUFFICIENT_BUFFER
)
1550 pEvlr
= HeapReAlloc(hProcessHeap
, 0, pEvlr
, dwNeeded
);
1551 dwWanted
= dwNeeded
;
1556 bResult
= ReadEventLogW(hEventLog
, dwFlags
, 0, pEvlr
, dwNeeded
, &dwRead
, &dwNeeded
);
1563 /* exit on other errors (ERROR_HANDLE_EOF) */
1567 pEvlrBuffer
= (LPBYTE
)pEvlr
;
1568 pEvlrEnd
= pEvlrBuffer
+ dwRead
;
1570 while (pEvlrBuffer
< pEvlrEnd
)
1572 PEVENTLOGRECORD pEvlrTmp
= (PEVENTLOGRECORD
)pEvlrBuffer
;
1573 PWSTR lpszUsername
, lpszCategoryName
;
1574 g_RecordPtrs
[dwCurrentRecord
] = NULL
;
1576 // ProgressBar_StepIt(hwndStatusProgress);
1578 if (uStep
% uStepAt
== 0)
1581 ProgressBar_SetPos(hwndStatusProgress
, uPos
);
1584 if (WaitForSingleObject(hStopEnumEvent
, 0) == WAIT_OBJECT_0
)
1587 /* Filter by event type */
1588 if (!FilterByType(EventLogFilter
, pEvlrTmp
))
1591 /* Get the event source name and filter it */
1592 lpszSourceName
= (LPWSTR
)(pEvlrBuffer
+ sizeof(EVENTLOGRECORD
));
1593 if (!FilterByString(EventLogFilter
->Sources
, lpszSourceName
))
1596 /* Get the computer name and filter it */
1597 lpszComputerName
= (LPWSTR
)(pEvlrBuffer
+ sizeof(EVENTLOGRECORD
) + (wcslen(lpszSourceName
) + 1) * sizeof(WCHAR
));
1598 if (!FilterByString(EventLogFilter
->ComputerNames
, lpszComputerName
))
1601 /* Compute the event time */
1602 EventTimeToSystemTime(pEvlrTmp
->TimeWritten
, &time
);
1603 GetDateFormatW(LOCALE_USER_DEFAULT
, DATE_SHORTDATE
, &time
, NULL
, szLocalDate
, ARRAYSIZE(szLocalDate
));
1604 GetTimeFormatW(LOCALE_USER_DEFAULT
, 0, &time
, NULL
, szLocalTime
, ARRAYSIZE(szLocalTime
));
1606 /* Get the username that generated the event, and filter it */
1607 lpszUsername
= GetEventUserName(pEvlrTmp
, pLastSid
, szUsername
) ? szUsername
: szNoUsername
;
1609 if (!FilterByString(EventLogFilter
->Users
, lpszUsername
))
1612 // TODO: Filter by event ID and category
1613 GetEventType(pEvlrTmp
->EventType
, szEventTypeText
);
1615 lpszCategoryName
= GetEventCategory(EventLog
->LogName
, lpszSourceName
, pEvlrTmp
, szCategory
) ? szCategory
: szNoCategory
;
1617 StringCbPrintfW(szEventID
, sizeof(szEventID
), L
"%u", (pEvlrTmp
->EventID
& 0xFFFF));
1618 StringCbPrintfW(szCategoryID
, sizeof(szCategoryID
), L
"%u", pEvlrTmp
->EventCategory
);
1620 g_RecordPtrs
[dwCurrentRecord
] = HeapAlloc(hProcessHeap
, 0, pEvlrTmp
->Length
);
1621 RtlCopyMemory(g_RecordPtrs
[dwCurrentRecord
], pEvlrTmp
, pEvlrTmp
->Length
);
1623 lviEventItem
.mask
= LVIF_IMAGE
| LVIF_TEXT
| LVIF_PARAM
;
1624 lviEventItem
.iItem
= 0;
1625 lviEventItem
.iSubItem
= 0;
1626 lviEventItem
.lParam
= (LPARAM
)g_RecordPtrs
[dwCurrentRecord
];
1627 lviEventItem
.pszText
= szEventTypeText
;
1629 switch (pEvlrTmp
->EventType
)
1631 case EVENTLOG_SUCCESS
:
1632 case EVENTLOG_INFORMATION_TYPE
:
1633 lviEventItem
.iImage
= 0;
1636 case EVENTLOG_WARNING_TYPE
:
1637 lviEventItem
.iImage
= 1;
1640 case EVENTLOG_ERROR_TYPE
:
1641 lviEventItem
.iImage
= 2;
1644 case EVENTLOG_AUDIT_SUCCESS
:
1645 lviEventItem
.iImage
= 3;
1648 case EVENTLOG_AUDIT_FAILURE
:
1649 lviEventItem
.iImage
= 4;
1653 lviEventItem
.iItem
= ListView_InsertItem(hwndListView
, &lviEventItem
);
1655 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 1, szLocalDate
);
1656 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 2, szLocalTime
);
1657 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 3, lpszSourceName
);
1658 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 4, lpszCategoryName
);
1659 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 5, szEventID
);
1660 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 6, lpszUsername
);
1661 ListView_SetItemText(hwndListView
, lviEventItem
.iItem
, 7, lpszComputerName
);
1664 pEvlrBuffer
+= pEvlrTmp
->Length
;
1672 HeapFree(hProcessHeap
, 0, pEvlr
);
1674 /* Close the event log */
1675 CloseEventLog(hEventLog
);
1677 } // end-for (LogIndex)
1679 /* All events loaded */
1683 ShowWindow(hwndStatusProgress
, SW_HIDE
);
1684 SendMessageW(hwndListView
, LVM_PROGRESS
, 0, FALSE
);
1686 // FIXME: Use something else instead of EventLog->LogName !!
1689 * Use a different formatting, whether the event log filter holds
1690 * only one log, or many logs (the latter case is WIP TODO!)
1692 if (EventLogFilter
->NumOfEventLogs
<= 1)
1694 StringCchPrintfExW(szWindowTitle
,
1695 ARRAYSIZE(szWindowTitle
),
1696 &lpTitleTemplateEnd
,
1699 szTitleTemplate
, szTitle
, EventLog
->LogName
); /* i = number of characters written */
1700 dwMaxLength
= (DWORD
)cchRemaining
;
1702 GetComputerNameW(lpTitleTemplateEnd
, &dwMaxLength
);
1704 StringCchCopyW(lpTitleTemplateEnd
, dwMaxLength
, lpMachineName
);
1706 StringCbPrintfW(szStatusText
,
1707 sizeof(szStatusText
),
1708 szStatusBarTemplate
,
1711 ListView_GetItemCount(hwndListView
));
1715 // TODO: Use a different title & implement filtering for multi-log filters !!
1716 // (EventLogFilter->NumOfEventLogs > 1)
1717 MessageBoxW(hwndMainWindow
,
1718 L
"Many-logs filtering is not implemented yet!!",
1720 MB_OK
| MB_ICONINFORMATION
);
1723 /* Update the status bar */
1724 StatusBar_SetText(hwndStatus
, 0, szStatusText
);
1726 /* Set the window title */
1727 SetWindowTextW(hwndMainWindow
, szWindowTitle
);
1729 /* Resume list view redraw */
1730 SendMessageW(hwndListView
, WM_SETREDRAW
, TRUE
, 0);
1732 EventLogFilter_Release(EventLogFilter
);
1734 CloseHandle(hStopEnumEvent
);
1735 InterlockedExchangePointer((PVOID
*)&hStopEnumEvent
, NULL
);
1741 * The purpose of this thread is to serialize the creation of the events
1742 * enumeration thread, since the Event Log Viewer currently only supports
1743 * one view, one event list, one enumeration.
1746 StartStopEnumEventsThread(IN LPVOID lpParameter
)
1748 HANDLE WaitHandles
[2];
1751 WaitHandles
[0] = hStartStopEnumEvent
; // End-of-application event
1752 WaitHandles
[1] = hStartEnumEvent
; // Command event
1756 WaitResult
= WaitForMultipleObjects(ARRAYSIZE(WaitHandles
),
1762 case WAIT_OBJECT_0
+ 0:
1764 /* End-of-application event signaled, quit this thread */
1766 /* Stop the previous enumeration */
1767 if (hEnumEventsThread
)
1771 SetEvent(hStopEnumEvent
);
1772 WaitForSingleObject(hEnumEventsThread
, INFINITE
);
1773 // NOTE: The following is done by the enumeration thread just before terminating.
1774 // hStopEnumEvent = NULL;
1777 CloseHandle(hEnumEventsThread
);
1778 hEnumEventsThread
= NULL
;
1781 /* Clear the list view and free the cached records */
1782 ListView_DeleteAllItems(hwndListView
);
1785 /* Reset the active filter */
1786 ActiveFilter
= NULL
;
1791 case WAIT_OBJECT_0
+ 1:
1793 /* Restart a new enumeration if needed */
1794 PEVENTLOGFILTER EventLogFilter
;
1796 /* Stop the previous enumeration */
1797 if (hEnumEventsThread
)
1801 SetEvent(hStopEnumEvent
);
1802 WaitForSingleObject(hEnumEventsThread
, INFINITE
);
1803 // NOTE: The following is done by the enumeration thread just before terminating.
1804 // hStopEnumEvent = NULL;
1807 CloseHandle(hEnumEventsThread
);
1808 hEnumEventsThread
= NULL
;
1811 /* Clear the list view and free the cached records */
1812 ListView_DeleteAllItems(hwndListView
);
1815 /* Reset the active filter */
1816 ActiveFilter
= NULL
;
1818 EventLogFilter
= InterlockedExchangePointer((PVOID
*)&EnumFilter
, NULL
);
1819 if (!EventLogFilter
)
1822 // Manual-reset event
1823 hStopEnumEvent
= CreateEventW(NULL
, TRUE
, FALSE
, NULL
);
1824 if (!hStopEnumEvent
)
1827 hEnumEventsThread
= CreateThread(NULL
,
1830 (LPVOID
)EventLogFilter
,
1833 if (!hEnumEventsThread
)
1835 CloseHandle(hStopEnumEvent
);
1836 hStopEnumEvent
= NULL
;
1839 // CloseHandle(hEnumEventsThread);
1840 ResumeThread(hEnumEventsThread
);
1847 /* Unknown command, must never go there! */
1848 return GetLastError();
1857 EnumEvents(IN PEVENTLOGFILTER EventLogFilter
)
1859 /* Signal the enumerator thread we want to enumerate events */
1860 InterlockedExchangePointer((PVOID
*)&EnumFilter
, EventLogFilter
);
1861 SetEvent(hStartEnumEvent
);
1867 GetSelectedFilter(OUT HTREEITEM
* phti OPTIONAL
)
1875 /* Get index of selected item */
1876 hti
= TreeView_GetSelection(hwndTreeView
);
1878 return NULL
; // No filter
1880 tvItemEx
.mask
= TVIF_PARAM
;
1881 tvItemEx
.hItem
= hti
;
1883 TreeView_GetItem(hwndTreeView
, &tvItemEx
);
1886 *phti
= tvItemEx
.hItem
;
1888 return (PEVENTLOGFILTER
)tvItemEx
.lParam
;
1893 OpenUserEventLog(VOID
)
1896 PEVENTLOGFILTER EventLogFilter
;
1897 HTREEITEM hItem
= NULL
;
1898 WCHAR szFileName
[MAX_PATH
];
1900 ZeroMemory(szFileName
, sizeof(szFileName
));
1902 sfn
.lpstrFile
= szFileName
;
1903 sfn
.nMaxFile
= ARRAYSIZE(szFileName
);
1905 if (!GetOpenFileNameW(&sfn
))
1907 sfn
.lpstrFile
[sfn
.nMaxFile
-1] = UNICODE_NULL
;
1909 /* Allocate a new event log entry */
1910 EventLog
= AllocEventLog(NULL
, sfn
.lpstrFile
, FALSE
);
1911 if (EventLog
== NULL
)
1914 /* Allocate a new event log filter entry for this event log */
1915 EventLogFilter
= AllocEventLogFilter(// LogName,
1916 TRUE
, TRUE
, TRUE
, TRUE
, TRUE
,
1919 if (EventLogFilter
== NULL
)
1921 HeapFree(GetProcessHeap(), 0, EventLog
);
1925 /* Add the event log and the filter into their lists */
1926 InsertTailList(&EventLogList
, &EventLog
->ListEntry
);
1927 InsertTailList(&EventLogFilterList
, &EventLogFilter
->ListEntry
);
1929 /* Retrieve and cache the event log file */
1930 EventLog
->FileName
= HeapAlloc(GetProcessHeap(), 0, sfn
.nMaxFile
* sizeof(WCHAR
));
1931 if (EventLog
->FileName
)
1932 StringCchCopyW(EventLog
->FileName
, sfn
.nMaxFile
, sfn
.lpstrFile
);
1934 hItem
= TreeViewAddItem(hwndTreeView
, htiUserLogs
,
1936 2, 3, (LPARAM
)EventLogFilter
);
1938 /* Select the event log */
1941 // TreeView_Expand(hwndTreeView, htiUserLogs, TVE_EXPAND);
1942 TreeView_SelectItem(hwndTreeView
, hItem
);
1943 TreeView_EnsureVisible(hwndTreeView
, hItem
);
1945 SetFocus(hwndTreeView
);
1949 SaveEventLog(IN PEVENTLOGFILTER EventLogFilter
)
1953 WCHAR szFileName
[MAX_PATH
];
1955 /* Bail out if there is no available filter */
1956 if (!EventLogFilter
)
1959 ZeroMemory(szFileName
, sizeof(szFileName
));
1961 sfn
.lpstrFile
= szFileName
;
1962 sfn
.nMaxFile
= ARRAYSIZE(szFileName
);
1964 if (!GetSaveFileNameW(&sfn
))
1967 EventLogFilter_AddRef(EventLogFilter
);
1969 EventLog
= EventLogFilter
->EventLogs
[0];
1970 hEventLog
= OpenEventLogW(EventLog
->ComputerName
, EventLog
->LogName
);
1972 EventLogFilter_Release(EventLogFilter
);
1976 ShowLastWin32Error();
1980 if (!BackupEventLogW(hEventLog
, szFileName
))
1981 ShowLastWin32Error();
1983 CloseEventLog(hEventLog
);
1987 CloseUserEventLog(IN PEVENTLOGFILTER EventLogFilter
, IN HTREEITEM hti
)
1989 /* Bail out if there is no available filter */
1990 if (!EventLogFilter
)
1993 if (InterlockedCompareExchangePointer((PVOID
*)&ActiveFilter
, NULL
, NULL
) == EventLogFilter
)
1995 /* Signal the enumerator thread we want to stop enumerating events */
1996 // EnumEvents(NULL);
1997 InterlockedExchangePointer((PVOID
*)&EnumFilter
, NULL
);
1998 SetEvent(hStartEnumEvent
);
2002 * The deletion of the item automatically triggers a TVN_SELCHANGED
2003 * notification, that will reset the ActiveFilter (in case the item
2004 * selected is a filter). Otherwise we reset it there.
2006 TreeView_DeleteItem(hwndTreeView
, hti
);
2008 /* Remove the filter from the list */
2009 RemoveEntryList(&EventLogFilter
->ListEntry
);
2010 EventLogFilter_Release(EventLogFilter
);
2012 // /* Select the default event log */
2013 // // TreeView_Expand(hwndTreeView, htiUserLogs, TVE_EXPAND);
2014 // TreeView_SelectItem(hwndTreeView, hItem);
2015 // TreeView_EnsureVisible(hwndTreeView, hItem);
2016 SetFocus(hwndTreeView
);
2021 ClearEvents(IN PEVENTLOGFILTER EventLogFilter
)
2026 WCHAR szFileName
[MAX_PATH
];
2027 WCHAR szMessage
[MAX_LOADSTRING
];
2029 /* Bail out if there is no available filter */
2030 if (!EventLogFilter
)
2033 ZeroMemory(szFileName
, sizeof(szFileName
));
2034 ZeroMemory(szMessage
, sizeof(szMessage
));
2036 LoadStringW(hInst
, IDS_CLEAREVENTS_MSG
, szMessage
, ARRAYSIZE(szMessage
));
2038 sfn
.lpstrFile
= szFileName
;
2039 sfn
.nMaxFile
= ARRAYSIZE(szFileName
);
2041 switch (MessageBoxW(hwndMainWindow
, szMessage
, szTitle
, MB_YESNOCANCEL
| MB_ICONINFORMATION
))
2047 sfn
.lpstrFile
= NULL
;
2051 if (!GetSaveFileNameW(&sfn
))
2056 EventLogFilter_AddRef(EventLogFilter
);
2058 EventLog
= EventLogFilter
->EventLogs
[0];
2059 hEventLog
= OpenEventLogW(EventLog
->ComputerName
, EventLog
->LogName
);
2061 EventLogFilter_Release(EventLogFilter
);
2065 ShowLastWin32Error();
2069 Success
= ClearEventLogW(hEventLog
, sfn
.lpstrFile
);
2071 ShowLastWin32Error();
2073 CloseEventLog(hEventLog
);
2079 Refresh(IN PEVENTLOGFILTER EventLogFilter
)
2081 /* Bail out if there is no available filter */
2082 if (!EventLogFilter
)
2085 /* Reenumerate the events through the filter */
2086 EnumEvents(EventLogFilter
);
2091 MyRegisterClass(HINSTANCE hInstance
)
2095 wcex
.cbSize
= sizeof(wcex
);
2097 wcex
.lpfnWndProc
= WndProc
;
2098 wcex
.cbClsExtra
= 0;
2099 wcex
.cbWndExtra
= 0;
2100 wcex
.hInstance
= hInstance
;
2101 wcex
.hIcon
= LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_EVENTVWR
));
2102 wcex
.hCursor
= LoadCursorW(NULL
, MAKEINTRESOURCEW(IDC_ARROW
));
2103 wcex
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1); // COLOR_WINDOW + 1
2104 wcex
.lpszMenuName
= MAKEINTRESOURCEW(IDM_EVENTVWR
);
2105 wcex
.lpszClassName
= szWindowClass
;
2106 wcex
.hIconSm
= (HICON
)LoadImageW(hInstance
,
2107 MAKEINTRESOURCEW(IDI_EVENTVWR
),
2113 return RegisterClassExW(&wcex
);
2118 GetDisplayNameFileAndID(IN LPCWSTR lpLogName
,
2119 OUT PWCHAR lpModuleName
, // TODO: Add IN DWORD BufLen
2120 OUT PDWORD pdwMessageID
)
2122 BOOL Success
= FALSE
;
2128 DWORD dwMessageID
= 0;
2129 WCHAR szModuleName
[MAX_PATH
];
2131 /* Use a default value for the message ID */
2134 cbKeyPath
= (wcslen(EVENTLOG_BASE_KEY
) + wcslen(lpLogName
) + 1) * sizeof(WCHAR
);
2135 KeyPath
= HeapAlloc(GetProcessHeap(), 0, cbKeyPath
);
2139 StringCbCopyW(KeyPath
, cbKeyPath
, EVENTLOG_BASE_KEY
);
2140 StringCbCatW(KeyPath
, cbKeyPath
, lpLogName
);
2142 Result
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, KeyPath
, 0, KEY_QUERY_VALUE
, &hLogKey
);
2143 HeapFree(GetProcessHeap(), 0, KeyPath
);
2144 if (Result
!= ERROR_SUCCESS
)
2147 cbData
= sizeof(szModuleName
);
2148 Result
= RegQueryValueExW(hLogKey
,
2152 (LPBYTE
)szModuleName
,
2154 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_EXPAND_SZ
&& Type
!= REG_SZ
))
2156 szModuleName
[0] = UNICODE_NULL
;
2160 /* NULL-terminate the string and expand it */
2161 szModuleName
[cbData
/ sizeof(WCHAR
) - 1] = UNICODE_NULL
;
2162 ExpandEnvironmentStringsW(szModuleName
, lpModuleName
, ARRAYSIZE(szModuleName
));
2167 * If we have a 'DisplayNameFile', query for 'DisplayNameID';
2168 * otherwise it's not really useful. 'DisplayNameID' is optional.
2172 cbData
= sizeof(dwMessageID
);
2173 Result
= RegQueryValueExW(hLogKey
,
2177 (LPBYTE
)&dwMessageID
,
2179 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_DWORD
))
2182 *pdwMessageID
= dwMessageID
;
2185 RegCloseKey(hLogKey
);
2192 BuildLogListAndFilterList(IN LPCWSTR lpComputerName
)
2195 HKEY hEventLogKey
, hLogKey
;
2196 DWORD dwNumLogs
= 0;
2197 DWORD dwIndex
, dwMaxKeyLength
;
2200 PEVENTLOGFILTER EventLogFilter
;
2201 LPWSTR LogName
= NULL
;
2202 WCHAR szModuleName
[MAX_PATH
];
2205 LPWSTR lpDisplayName
;
2206 HTREEITEM hRootNode
= NULL
, hItem
= NULL
, hItemDefault
= NULL
;
2208 /* Open the EventLog key */
2209 // FIXME: Use local or remote computer
2210 Result
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, EVENTLOG_BASE_KEY
, 0, KEY_READ
, &hEventLogKey
);
2211 if (Result
!= ERROR_SUCCESS
)
2216 /* Retrieve the number of event logs enumerated as registry keys */
2217 Result
= RegQueryInfoKeyW(hEventLogKey
, NULL
, NULL
, NULL
, &dwNumLogs
, &dwMaxKeyLength
,
2218 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
2219 if (Result
!= ERROR_SUCCESS
)
2226 /* Take the NULL terminator into account */
2229 /* Allocate the temporary buffer */
2230 LogName
= HeapAlloc(GetProcessHeap(), 0, dwMaxKeyLength
* sizeof(WCHAR
));
2234 /* Enumerate and retrieve each event log name */
2235 for (dwIndex
= 0; dwIndex
< dwNumLogs
; dwIndex
++)
2237 lpcName
= dwMaxKeyLength
;
2238 Result
= RegEnumKeyExW(hEventLogKey
, dwIndex
, LogName
, &lpcName
, NULL
, NULL
, NULL
, NULL
);
2239 if (Result
!= ERROR_SUCCESS
)
2242 /* Take the NULL terminator into account */
2245 /* Allocate a new event log entry */
2246 EventLog
= AllocEventLog(lpComputerName
, LogName
, TRUE
);
2247 if (EventLog
== NULL
)
2250 /* Allocate a new event log filter entry for this event log */
2251 EventLogFilter
= AllocEventLogFilter(// LogName,
2252 TRUE
, TRUE
, TRUE
, TRUE
, TRUE
,
2255 if (EventLogFilter
== NULL
)
2257 HeapFree(GetProcessHeap(), 0, EventLog
);
2261 /* Add the event log and the filter into their lists */
2262 InsertTailList(&EventLogList
, &EventLog
->ListEntry
);
2263 InsertTailList(&EventLogFilterList
, &EventLogFilter
->ListEntry
);
2265 EventLog
->FileName
= NULL
;
2267 /* Retrieve and cache the event log file */
2268 Result
= RegOpenKeyExW(hEventLogKey
,
2273 if (Result
== ERROR_SUCCESS
)
2276 Result
= RegQueryValueExW(hLogKey
,
2282 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_EXPAND_SZ
&& Type
!= REG_SZ
))
2284 // Windows' EventLog uses some kind of default value, we do not.
2285 EventLog
->FileName
= NULL
;
2289 lpcName
= ROUND_DOWN(lpcName
, sizeof(WCHAR
));
2290 EventLog
->FileName
= HeapAlloc(GetProcessHeap(), 0, lpcName
);
2291 if (EventLog
->FileName
)
2293 Result
= RegQueryValueExW(hLogKey
,
2297 (LPBYTE
)EventLog
->FileName
,
2299 if (Result
!= ERROR_SUCCESS
)
2301 HeapFree(GetProcessHeap(), 0, EventLog
->FileName
);
2302 EventLog
->FileName
= NULL
;
2306 EventLog
->FileName
[lpcName
/ sizeof(WCHAR
) - 1] = UNICODE_NULL
;
2311 RegCloseKey(hLogKey
);
2314 /* Get the display name for the event log */
2315 lpDisplayName
= NULL
;
2317 ZeroMemory(szModuleName
, sizeof(szModuleName
));
2318 if (GetDisplayNameFileAndID(LogName
, szModuleName
, &dwMessageID
))
2320 /* Retrieve the message string without appending extra newlines */
2322 GetMessageStringFromDll(szModuleName
,
2323 FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_HMODULE
|
2324 FORMAT_MESSAGE_IGNORE_INSERTS
| FORMAT_MESSAGE_MAX_WIDTH_MASK
,
2331 * Select the correct tree root node, whether the log is a System
2332 * or an Application log. Default to Application log otherwise.
2334 hRootNode
= htiAppLogs
;
2335 for (lpcName
= 0; lpcName
< ARRAYSIZE(SystemLogs
); ++lpcName
)
2337 /* Check whether the log name is part of the system logs */
2338 if (wcsicmp(LogName
, SystemLogs
[lpcName
]) == 0)
2340 hRootNode
= htiSystemLogs
;
2345 hItem
= TreeViewAddItem(hwndTreeView
, hRootNode
,
2346 (lpDisplayName
? lpDisplayName
: LogName
),
2347 2, 3, (LPARAM
)EventLogFilter
);
2349 /* Try to get the default event log: "Application" */
2350 if ((hItemDefault
== NULL
) && (wcsicmp(LogName
, SystemLogs
[0]) == 0))
2352 hItemDefault
= hItem
;
2355 /* Free the buffer allocated by FormatMessage */
2357 LocalFree(lpDisplayName
);
2360 HeapFree(GetProcessHeap(), 0, LogName
);
2363 RegCloseKey(hEventLogKey
);
2365 /* Select the default event log */
2368 // TreeView_Expand(hwndTreeView, hRootNode, TVE_EXPAND);
2369 TreeView_SelectItem(hwndTreeView
, hItemDefault
);
2370 TreeView_EnsureVisible(hwndTreeView
, hItemDefault
);
2372 SetFocus(hwndTreeView
);
2383 while (!IsListEmpty(&EventLogList
))
2385 Entry
= RemoveHeadList(&EventLogList
);
2386 EventLog
= (PEVENTLOG
)CONTAINING_RECORD(Entry
, EVENTLOG
, ListEntry
);
2387 EventLog_Free(EventLog
);
2394 FreeLogFilterList(VOID
)
2397 PEVENTLOGFILTER EventLogFilter
;
2399 while (!IsListEmpty(&EventLogFilterList
))
2401 Entry
= RemoveHeadList(&EventLogFilterList
);
2402 EventLogFilter
= (PEVENTLOGFILTER
)CONTAINING_RECORD(Entry
, EVENTLOGFILTER
, ListEntry
);
2403 EventLogFilter_Free(EventLogFilter
);
2406 ActiveFilter
= NULL
;
2412 InitInstance(HINSTANCE hInstance
,
2418 LVCOLUMNW lvc
= {0};
2421 hInst
= hInstance
; // Store instance handle in our global variable
2423 /* Create the main window */
2424 hwndMainWindow
= CreateWindowW(szWindowClass
,
2426 WS_OVERLAPPEDWINDOW
| WS_CLIPCHILDREN
,
2427 CW_USEDEFAULT
, 0, CW_USEDEFAULT
, 0,
2432 if (!hwndMainWindow
)
2435 /* Create the status bar */
2436 hwndStatus
= CreateWindowExW(0, // no extended styles
2437 STATUSCLASSNAMEW
, // status bar
2439 WS_CHILD
| WS_VISIBLE
| CCS_BOTTOM
| SBARS_SIZEGRIP
, // styles
2440 0, 0, 0, 0, // x, y, cx, cy
2441 hwndMainWindow
, // parent window
2442 (HMENU
)100, // window ID
2443 hInstance
, // instance
2444 NULL
); // window data
2446 GetClientRect(hwndMainWindow
, &rcClient
);
2447 GetWindowRect(hwndStatus
, &rs
);
2448 StatusHeight
= rs
.bottom
- rs
.top
;
2450 /* Create a progress bar in the status bar (hidden by default) */
2451 StatusBar_GetItemRect(hwndStatus
, 0, &rs
);
2452 hwndStatusProgress
= CreateWindowExW(0, // no extended styles
2453 PROGRESS_CLASSW
, // status bar
2455 WS_CHILD
| PBS_SMOOTH
, // styles
2456 rs
.left
, rs
.top
, // x, y
2457 rs
.right
-rs
.left
, rs
.bottom
-rs
.top
, // cx, cy
2458 hwndStatus
, // parent window
2460 hInstance
, // instance
2461 NULL
); // window data
2462 ProgressBar_SetStep(hwndStatusProgress
, 1);
2464 /* Initialize the splitter default positions */
2468 /* Create the TreeView */
2469 hwndTreeView
= CreateWindowExW(WS_EX_CLIENTEDGE
,
2472 // WS_CHILD | WS_VISIBLE | TVS_HASLINES | TVS_SHOWSELALWAYS,
2473 WS_CHILD
| WS_VISIBLE
| /* WS_TABSTOP | */ TVS_HASLINES
| TVS_HASBUTTONS
| TVS_LINESATROOT
| TVS_EDITLABELS
| TVS_SHOWSELALWAYS
,
2475 nVSplitPos
- SPLIT_WIDTH
/2,
2476 (rcClient
.bottom
- rcClient
.top
) - StatusHeight
,
2482 /* Create the ImageList */
2483 hSmall
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
),
2484 GetSystemMetrics(SM_CYSMICON
),
2485 ILC_COLOR32
| ILC_MASK
, // ILC_COLOR24
2488 /* Add event type icons to the ImageList: closed/opened folder, event log (normal/viewed) */
2489 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_CLOSED_CATEGORY
)));
2490 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_OPENED_CATEGORY
)));
2491 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_EVENTLOG
)));
2492 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_EVENTVWR
)));
2494 /* Assign the ImageList to the Tree View */
2495 TreeView_SetImageList(hwndTreeView
, hSmall
, TVSIL_NORMAL
);
2497 /* Add the event logs nodes */
2499 LoadStringW(hInstance
, IDS_EVENTLOG_SYSTEM
, szTemp
, ARRAYSIZE(szTemp
));
2500 htiSystemLogs
= TreeViewAddItem(hwndTreeView
, NULL
, szTemp
, 0, 1, (LPARAM
)NULL
);
2501 // "Application Logs"
2502 LoadStringW(hInstance
, IDS_EVENTLOG_APP
, szTemp
, ARRAYSIZE(szTemp
));
2503 htiAppLogs
= TreeViewAddItem(hwndTreeView
, NULL
, szTemp
, 0, 1, (LPARAM
)NULL
);
2505 LoadStringW(hInstance
, IDS_EVENTLOG_USER
, szTemp
, ARRAYSIZE(szTemp
));
2506 htiUserLogs
= TreeViewAddItem(hwndTreeView
, NULL
, szTemp
, 0, 1, (LPARAM
)NULL
);
2508 /* Create the Event details pane (optional) */
2509 hwndEventDetails
= CreateEventDetailsCtrl(hInst
, hwndMainWindow
, (LPARAM
)NULL
);
2510 // hwndEventDetails = NULL;
2511 if (hwndEventDetails
)
2513 // SetWindowLongPtrW(hwndEventDetails, GWL_STYLE,
2514 // GetWindowLongPtrW(hwndEventDetails, GWL_STYLE) | WS_BORDER);
2515 SetWindowLongPtrW(hwndEventDetails
, GWL_EXSTYLE
,
2516 GetWindowLongPtrW(hwndEventDetails
, GWL_EXSTYLE
) | WS_EX_CLIENTEDGE
);
2517 SetWindowPos(hwndEventDetails
, NULL
,
2518 nVSplitPos
+ SPLIT_WIDTH
/2,
2519 nHSplitPos
+ SPLIT_WIDTH
/2,
2520 (rcClient
.right
- rcClient
.left
) - nVSplitPos
- SPLIT_WIDTH
/2,
2521 (rcClient
.bottom
- rcClient
.top
) - nHSplitPos
- SPLIT_WIDTH
/2 - StatusHeight
,
2522 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_SHOWWINDOW
);
2525 /* Create the ListView */
2526 hwndListView
= CreateWindowExW(WS_EX_CLIENTEDGE
,
2529 WS_CHILD
| WS_VISIBLE
| LVS_SHOWSELALWAYS
| LVS_REPORT
,
2530 nVSplitPos
+ SPLIT_WIDTH
/2,
2532 (rcClient
.right
- rcClient
.left
) - nVSplitPos
- SPLIT_WIDTH
/2,
2533 hwndEventDetails
? nHSplitPos
- SPLIT_WIDTH
/2
2534 : (rcClient
.bottom
- rcClient
.top
) - StatusHeight
,
2540 /* Add the extended ListView styles */
2541 ListView_SetExtendedListViewStyle(hwndListView
, LVS_EX_HEADERDRAGDROP
| LVS_EX_FULLROWSELECT
|LVS_EX_LABELTIP
);
2543 /* Create the ImageList */
2544 hSmall
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
),
2545 GetSystemMetrics(SM_CYSMICON
),
2546 ILC_COLOR32
| ILC_MASK
, // ILC_COLOR24
2549 /* Add event type icons to the ImageList */
2550 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_INFORMATIONICON
)));
2551 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_WARNINGICON
)));
2552 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_ERRORICON
)));
2553 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_AUDITSUCCESSICON
)));
2554 ImageList_AddIcon(hSmall
, LoadIconW(hInstance
, MAKEINTRESOURCEW(IDI_AUDITFAILUREICON
)));
2556 /* Assign the ImageList to the List View */
2557 ListView_SetImageList(hwndListView
, hSmall
, LVSIL_SMALL
);
2559 /* Now set up the listview with its columns */
2560 lvc
.mask
= LVCF_TEXT
| LVCF_WIDTH
;
2562 LoadStringW(hInstance
,
2566 lvc
.pszText
= szTemp
;
2567 ListView_InsertColumn(hwndListView
, 0, &lvc
);
2570 LoadStringW(hInstance
,
2574 lvc
.pszText
= szTemp
;
2575 ListView_InsertColumn(hwndListView
, 1, &lvc
);
2578 LoadStringW(hInstance
,
2582 lvc
.pszText
= szTemp
;
2583 ListView_InsertColumn(hwndListView
, 2, &lvc
);
2586 LoadStringW(hInstance
,
2590 lvc
.pszText
= szTemp
;
2591 ListView_InsertColumn(hwndListView
, 3, &lvc
);
2594 LoadStringW(hInstance
,
2598 lvc
.pszText
= szTemp
;
2599 ListView_InsertColumn(hwndListView
, 4, &lvc
);
2602 LoadStringW(hInstance
,
2606 lvc
.pszText
= szTemp
;
2607 ListView_InsertColumn(hwndListView
, 5, &lvc
);
2610 LoadStringW(hInstance
,
2614 lvc
.pszText
= szTemp
;
2615 ListView_InsertColumn(hwndListView
, 6, &lvc
);
2618 LoadStringW(hInstance
,
2622 lvc
.pszText
= szTemp
;
2623 ListView_InsertColumn(hwndListView
, 7, &lvc
);
2625 /* Initialize the save Dialog */
2626 ZeroMemory(&sfn
, sizeof(sfn
));
2627 ZeroMemory(szSaveFilter
, sizeof(szSaveFilter
));
2629 LoadStringW(hInst
, IDS_SAVE_FILTER
, szSaveFilter
, ARRAYSIZE(szSaveFilter
));
2631 sfn
.lStructSize
= sizeof(sfn
);
2632 sfn
.hwndOwner
= hwndMainWindow
;
2633 sfn
.hInstance
= hInstance
;
2634 sfn
.lpstrFilter
= szSaveFilter
;
2635 sfn
.lpstrInitialDir
= NULL
;
2636 sfn
.Flags
= OFN_HIDEREADONLY
| OFN_SHAREAWARE
;
2637 sfn
.lpstrDefExt
= NULL
;
2639 ShowWindow(hwndMainWindow
, nCmdShow
);
2640 UpdateWindow(hwndMainWindow
);
2645 VOID
ResizeWnd(INT cx
, INT cy
)
2651 /* Resize the status bar -- now done in WM_SIZE */
2652 // SendMessageW(hwndStatus, WM_SIZE, 0, 0);
2653 GetWindowRect(hwndStatus
, &rs
);
2654 StatusHeight
= rs
.bottom
- rs
.top
;
2656 /* Move the progress bar */
2657 StatusBar_GetItemRect(hwndStatus
, 0, &rs
);
2658 MoveWindow(hwndStatusProgress
,
2659 rs
.left
, rs
.top
, rs
.right
-rs
.left
, rs
.bottom
-rs
.top
,
2660 IsWindowVisible(hwndStatusProgress
) ? TRUE
: FALSE
);
2663 * TODO: Adjust the splitter positions:
2664 * - Vertical splitter (1) : fixed position from the left window side.
2665 * - Horizontal splitter (2): fixed position from the bottom window side.
2667 nVSplitPos
= min(max(nVSplitPos
, SPLIT_WIDTH
/2), cx
- SPLIT_WIDTH
/2); // OK
2668 nHSplitPos
= min(max(nHSplitPos
, SPLIT_WIDTH
/2), cy
- SPLIT_WIDTH
/2 - StatusHeight
); // FIXME!
2670 hdwp
= BeginDeferWindowPos(3);
2673 hdwp
= DeferWindowPos(hdwp
,
2677 nVSplitPos
- SPLIT_WIDTH
/2,
2679 SWP_NOZORDER
| SWP_NOACTIVATE
);
2682 hdwp
= DeferWindowPos(hdwp
,
2685 nVSplitPos
+ SPLIT_WIDTH
/2, 0,
2686 cx
- nVSplitPos
- SPLIT_WIDTH
/2,
2687 hwndEventDetails
? nHSplitPos
- SPLIT_WIDTH
/2
2688 : cy
- StatusHeight
,
2689 SWP_NOZORDER
| SWP_NOACTIVATE
);
2691 if (hwndEventDetails
&& hdwp
)
2692 hdwp
= DeferWindowPos(hdwp
,
2695 nVSplitPos
+ SPLIT_WIDTH
/2,
2696 nHSplitPos
+ SPLIT_WIDTH
/2,
2697 cx
- nVSplitPos
- SPLIT_WIDTH
/2,
2698 cy
- nHSplitPos
- SPLIT_WIDTH
/2 - StatusHeight
,
2699 SWP_NOZORDER
| SWP_NOACTIVATE
);
2702 EndDeferWindowPos(hdwp
);
2707 WndProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
2714 hMainMenu
= GetMenu(hWnd
);
2723 LPNMHDR hdr
= (LPNMHDR
)lParam
;
2725 if (hdr
->hwndFrom
== hwndListView
)
2729 case LVN_ITEMCHANGED
:
2731 LPNMLISTVIEW pnmv
= (LPNMLISTVIEW
)lParam
;
2733 if ( (pnmv
->uChanged
& LVIF_STATE
) && /* The state has changed */
2734 (pnmv
->uNewState
& LVIS_SELECTED
) /* The item has been (de)selected */ )
2736 if (hwndEventDetails
)
2737 SendMessageW(hwndEventDetails
, EVT_DISPLAY
, 0, 0);
2744 SendMessageW(hWnd
, WM_COMMAND
, IDM_EVENT_DETAILS
, 0);
2748 else if (hdr
->hwndFrom
== hwndTreeView
)
2752 case TVN_BEGINLABELEDIT
:
2754 HTREEITEM hItem
= ((LPNMTVDISPINFO
)lParam
)->item
.hItem
;
2756 /* Disable label editing for root nodes */
2757 return ((hItem
== htiSystemLogs
) ||
2758 (hItem
== htiAppLogs
) ||
2759 (hItem
== htiUserLogs
));
2762 case TVN_ENDLABELEDIT
:
2764 TVITEMW item
= ((LPNMTVDISPINFO
)lParam
)->item
;
2765 HTREEITEM hItem
= item
.hItem
;
2767 /* Disable label editing for root nodes */
2768 if ((hItem
== htiSystemLogs
) ||
2769 (hItem
== htiAppLogs
) ||
2770 (hItem
== htiUserLogs
))
2777 LPWSTR pszText
= item
.pszText
;
2779 /* Trim all whitespace */
2780 while (*pszText
&& iswspace(*pszText
))
2794 case TVN_SELCHANGED
:
2796 PEVENTLOGFILTER EventLogFilter
=
2797 (PEVENTLOGFILTER
)((LPNMTREEVIEW
)lParam
)->itemNew
.lParam
;
2799 // FIXME: It might be nice to reference here the filter,
2800 // so that we don't have to reference/dereference it many times
2801 // in the other functions???
2803 // FIXME: This is a hack!!
2804 if (hwndEventDetails
&& EventLogFilter
)
2806 SendMessageW(hwndEventDetails
, EVT_SETFILTER
, 0, (LPARAM
)EventLogFilter
);
2812 * If we have selected a filter, enable the menu commands;
2813 * they will possibly be updated after events enumeration.
2815 EnableMenuItem(hMainMenu
, IDM_SAVE_EVENTLOG
, MF_BYCOMMAND
| MF_ENABLED
);
2816 EnableMenuItem(hMainMenu
, IDM_CLOSE_EVENTLOG
, MF_BYCOMMAND
| MF_ENABLED
);
2817 EnableMenuItem(hMainMenu
, IDM_CLEAR_EVENTS
, MF_BYCOMMAND
| MF_ENABLED
);
2818 EnableMenuItem(hMainMenu
, IDM_RENAME_EVENTLOG
, MF_BYCOMMAND
| MF_ENABLED
);
2819 EnableMenuItem(hMainMenu
, IDM_EVENTLOG_SETTINGS
, MF_BYCOMMAND
| MF_ENABLED
);
2823 EnableMenuItem(hMainMenu
, IDM_SAVE_EVENTLOG
, MF_BYCOMMAND
| MF_GRAYED
);
2824 EnableMenuItem(hMainMenu
, IDM_CLOSE_EVENTLOG
, MF_BYCOMMAND
| MF_GRAYED
);
2825 EnableMenuItem(hMainMenu
, IDM_CLEAR_EVENTS
, MF_BYCOMMAND
| MF_GRAYED
);
2826 EnableMenuItem(hMainMenu
, IDM_RENAME_EVENTLOG
, MF_BYCOMMAND
| MF_GRAYED
);
2827 EnableMenuItem(hMainMenu
, IDM_EVENTLOG_SETTINGS
, MF_BYCOMMAND
| MF_GRAYED
);
2831 * The enumeration thread that is triggered by EnumEvents
2832 * will set a new value for the 'ActiveFilter'.
2835 EnumEvents(EventLogFilter
);
2846 /* Parse the menu selections */
2847 switch (LOWORD(wParam
))
2849 case IDM_OPEN_EVENTLOG
:
2853 case IDM_SAVE_EVENTLOG
:
2854 SaveEventLog(GetSelectedFilter(NULL
));
2857 case IDM_CLOSE_EVENTLOG
:
2860 PEVENTLOGFILTER EventLogFilter
= GetSelectedFilter(&hti
);
2861 CloseUserEventLog(EventLogFilter
, hti
);
2865 case IDM_CLEAR_EVENTS
:
2867 PEVENTLOGFILTER EventLogFilter
= GetSelectedFilter(NULL
);
2868 if (EventLogFilter
&& ClearEvents(EventLogFilter
))
2869 Refresh(EventLogFilter
);
2873 case IDM_RENAME_EVENTLOG
:
2874 if (GetFocus() == hwndTreeView
)
2875 TreeView_EditLabel(hwndTreeView
, TreeView_GetSelection(hwndTreeView
));
2878 case IDM_EVENTLOG_SETTINGS
:
2880 PEVENTLOGFILTER EventLogFilter
= GetSelectedFilter(NULL
);
2881 // TODO: Check the returned value?
2883 EventLogProperties(hInst
, hWnd
, EventLogFilter
);
2887 case IDM_LIST_NEWEST
:
2889 CheckMenuRadioItem(hMainMenu
, IDM_LIST_NEWEST
, IDM_LIST_OLDEST
, IDM_LIST_NEWEST
, MF_BYCOMMAND
);
2890 if (!NewestEventsFirst
)
2892 NewestEventsFirst
= TRUE
;
2893 Refresh(GetSelectedFilter(NULL
));
2898 case IDM_LIST_OLDEST
:
2900 CheckMenuRadioItem(hMainMenu
, IDM_LIST_NEWEST
, IDM_LIST_OLDEST
, IDM_LIST_OLDEST
, MF_BYCOMMAND
);
2901 if (NewestEventsFirst
)
2903 NewestEventsFirst
= FALSE
;
2904 Refresh(GetSelectedFilter(NULL
));
2909 case IDM_EVENT_DETAILS
:
2911 // LPNMITEMACTIVATE lpnmitem = (LPNMITEMACTIVATE)lParam;
2912 PEVENTLOGFILTER EventLogFilter
= GetSelectedFilter(NULL
);
2913 if (/*lpnmitem->iItem != -1 &&*/ EventLogFilter
)
2915 EventLogFilter_AddRef(EventLogFilter
);
2916 DialogBoxParamW(hInst
,
2917 MAKEINTRESOURCEW(IDD_EVENTDETAILS_DLG
),
2920 (LPARAM
)EventLogFilter
);
2921 EventLogFilter_Release(EventLogFilter
);
2927 Refresh(GetSelectedFilter(NULL
));
2933 WCHAR szCopyright
[MAX_LOADSTRING
];
2935 hIcon
= LoadIconW(hInst
, MAKEINTRESOURCEW(IDI_EVENTVWR
));
2936 LoadStringW(hInst
, IDS_COPYRIGHT
, szCopyright
, ARRAYSIZE(szCopyright
));
2937 ShellAboutW(hWnd
, szTitle
, szCopyright
, hIcon
);
2938 DeleteObject(hIcon
);
2943 MessageBoxW(hwndMainWindow
,
2944 L
"Help not implemented yet!",
2946 MB_OK
| MB_ICONINFORMATION
);
2950 DestroyWindow(hWnd
);
2954 return DefWindowProcW(hWnd
, uMsg
, wParam
, lParam
);
2960 if (LOWORD(lParam
) == HTCLIENT
)
2964 ScreenToClient(hWnd
, &pt
);
2966 /* Set the cursor for the vertical splitter */
2967 if (pt
.x
>= nVSplitPos
- SPLIT_WIDTH
/2 && pt
.x
< nVSplitPos
+ SPLIT_WIDTH
/2 + 1)
2970 GetClientRect(hWnd
, &rect
);
2971 GetWindowRect(hwndStatus
, &rs
);
2972 if (pt
.y
>= rect
.top
&& pt
.y
< rect
.bottom
- (rs
.bottom
- rs
.top
))
2974 SetCursor(LoadCursorW(NULL
, IDC_SIZEWE
));
2979 /* Set the cursor for the horizontal splitter, if the Event details pane is displayed */
2980 if (hwndEventDetails
&&
2981 (pt
.y
>= nHSplitPos
- SPLIT_WIDTH
/2 && pt
.y
< nHSplitPos
+ SPLIT_WIDTH
/2 + 1))
2984 GetClientRect(hWnd
, &rect
);
2985 // GetWindowRect(hwndStatus, &rs);
2986 if (pt
.x
>= nVSplitPos
+ SPLIT_WIDTH
/2 + 1 /* rect.left + (rs.bottom - rs.top) */ &&
2989 SetCursor(LoadCursorW(NULL
, IDC_SIZENS
));
2996 case WM_LBUTTONDOWN
:
2998 INT x
= GET_X_LPARAM(lParam
);
2999 INT y
= GET_Y_LPARAM(lParam
);
3001 /* Reset the splitter state */
3004 /* Capture the cursor for the vertical splitter */
3005 if (x
>= nVSplitPos
- SPLIT_WIDTH
/2 && x
< nVSplitPos
+ SPLIT_WIDTH
/2 + 1)
3011 /* Capture the cursor for the horizontal splitter, if the Event details pane is displayed */
3012 if (hwndEventDetails
&&
3013 (y
>= nHSplitPos
- SPLIT_WIDTH
/2 && y
< nHSplitPos
+ SPLIT_WIDTH
/2 + 1))
3022 case WM_RBUTTONDOWN
:
3023 if (GetCapture() == hWnd
)
3025 /* Adjust the correct splitter position */
3027 nVSplitPos
= GET_X_LPARAM(lParam
);
3028 else if (bSplit
== 2)
3029 nHSplitPos
= GET_Y_LPARAM(lParam
);
3031 /* If we are splitting, resize the windows */
3034 GetClientRect(hWnd
, &rect
);
3035 ResizeWnd(rect
.right
- rect
.left
, rect
.bottom
- rect
.top
);
3038 /* Reset the splitter state */
3046 if (GetCapture() == hWnd
)
3048 /* Move the correct splitter */
3051 INT x
= GET_X_LPARAM(lParam
);
3053 GetClientRect(hWnd
, &rect
);
3055 x
= min(max(x
, SPLIT_WIDTH
/2), rect
.right
- rect
.left
- SPLIT_WIDTH
/2);
3056 if (nVSplitPos
!= x
)
3059 ResizeWnd(rect
.right
- rect
.left
, rect
.bottom
- rect
.top
);
3062 else if (bSplit
== 2)
3065 INT y
= GET_Y_LPARAM(lParam
);
3067 GetClientRect(hWnd
, &rect
);
3068 GetWindowRect(hwndStatus
, &rs
);
3070 y
= min(max(y
, SPLIT_WIDTH
/2), rect
.bottom
- rect
.top
- SPLIT_WIDTH
/2 - (rs
.bottom
- rs
.top
));
3071 if (nHSplitPos
!= y
)
3074 ResizeWnd(rect
.right
- rect
.left
, rect
.bottom
- rect
.top
);
3082 if (wParam
!= SIZE_MINIMIZED
)
3084 SendMessageW(hwndStatus
, WM_SIZE
, 0, 0);
3085 ResizeWnd(LOWORD(lParam
), HIWORD(lParam
));
3088 /* Fall through the default case */
3092 return DefWindowProcW(hWnd
, uMsg
, wParam
, lParam
);
3101 InitPropertiesDlg(HWND hDlg
, PEVENTLOG EventLog
)
3103 LPWSTR lpLogName
= EventLog
->LogName
;
3106 DWORD dwMaxSize
= 0, dwRetention
= 0;
3108 WIN32_FIND_DATAW FileInfo
; // WIN32_FILE_ATTRIBUTE_DATA
3109 ULARGE_INTEGER FileSize
;
3110 WCHAR wszBuf
[MAX_PATH
];
3111 WCHAR szTemp
[MAX_LOADSTRING
];
3119 if (EventLog
->Permanent
)
3122 cbKeyPath
= (wcslen(EVENTLOG_BASE_KEY
) + wcslen(lpLogName
) + 1) * sizeof(WCHAR
);
3123 KeyPath
= HeapAlloc(GetProcessHeap(), 0, cbKeyPath
);
3129 StringCbCopyW(KeyPath
, cbKeyPath
, EVENTLOG_BASE_KEY
);
3130 StringCbCatW(KeyPath
, cbKeyPath
, lpLogName
);
3132 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE
, KeyPath
, 0, KEY_QUERY_VALUE
, &hLogKey
) != ERROR_SUCCESS
)
3134 HeapFree(GetProcessHeap(), 0, KeyPath
);
3137 HeapFree(GetProcessHeap(), 0, KeyPath
);
3140 cbData
= sizeof(dwMaxSize
);
3141 Result
= RegQueryValueExW(hLogKey
,
3147 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_DWORD
))
3149 // dwMaxSize = 512 * 1024; /* 512 kBytes */
3155 cbData
= sizeof(dwRetention
);
3156 Result
= RegQueryValueExW(hLogKey
,
3160 (LPBYTE
)&dwRetention
,
3162 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_DWORD
))
3164 /* On Windows 2003 it is 604800 (secs) == 7 days */
3167 /* Convert in days, rounded up */ // ROUND_UP
3168 // dwRetention = ROUND_UP(dwRetention, 24*3600) / (24*3600);
3169 dwRetention
= (dwRetention
+ 24*3600 - 1) / (24*3600);
3172 RegCloseKey(hLogKey
);
3179 SetDlgItemTextW(hDlg
, IDC_DISPLAYNAME
, lpLogName
); // FIXME!
3180 SetDlgItemTextW(hDlg
, IDC_LOGNAME
, lpLogName
);
3182 FileName
= EventLog
->FileName
;
3183 if (FileName
&& *FileName
)
3185 ExpandEnvironmentStringsW(FileName
, wszBuf
, MAX_PATH
);
3188 SetDlgItemTextW(hDlg
, IDC_LOGFILE
, FileName
);
3191 * The general problem here (and in the shell as well) is that
3192 * GetFileAttributesEx fails for files that are opened without
3193 * shared access. To retrieve file information for those we need
3194 * to use something else: FindFirstFile, on the full file name.
3197 Success
= GetFileAttributesExW(FileName
,
3198 GetFileExInfoStandard
,
3199 (LPWIN32_FILE_ATTRIBUTE_DATA
)&FileInfo
);
3202 HANDLE hFind
= FindFirstFileW(FileName
, &FileInfo
);
3203 Success
= (hFind
!= INVALID_HANDLE_VALUE
);
3208 // Starting there, FileName is invalid (because it uses wszBuf)
3212 FileSize
.u
.LowPart
= FileInfo
.nFileSizeLow
;
3213 FileSize
.u
.HighPart
= FileInfo
.nFileSizeHigh
;
3214 if (FormatFileSizeWithBytes(&FileSize
, wszBuf
, ARRAYSIZE(wszBuf
)))
3215 SetDlgItemTextW(hDlg
, IDC_SIZE_LABEL
, wszBuf
);
3217 LoadStringW(hInst
, IDS_NOT_AVAILABLE
, szTemp
, ARRAYSIZE(szTemp
));
3219 if (GetFileTimeString(&FileInfo
.ftCreationTime
, wszBuf
, ARRAYSIZE(wszBuf
)))
3220 SetDlgItemTextW(hDlg
, IDC_CREATED_LABEL
, wszBuf
);
3222 SetDlgItemTextW(hDlg
, IDC_CREATED_LABEL
, szTemp
);
3224 if (GetFileTimeString(&FileInfo
.ftLastWriteTime
, wszBuf
, ARRAYSIZE(wszBuf
)))
3225 SetDlgItemTextW(hDlg
, IDC_MODIFIED_LABEL
, wszBuf
);
3227 SetDlgItemTextW(hDlg
, IDC_MODIFIED_LABEL
, szTemp
);
3229 if (GetFileTimeString(&FileInfo
.ftLastAccessTime
, wszBuf
, ARRAYSIZE(wszBuf
)))
3230 SetDlgItemTextW(hDlg
, IDC_ACCESSED_LABEL
, wszBuf
);
3232 SetDlgItemTextW(hDlg
, IDC_MODIFIED_LABEL
, szTemp
);
3236 LoadStringW(hInst
, IDS_NOT_AVAILABLE
, szTemp
, ARRAYSIZE(szTemp
));
3238 SetDlgItemTextW(hDlg
, IDC_SIZE_LABEL
, szTemp
);
3239 SetDlgItemTextW(hDlg
, IDC_CREATED_LABEL
, szTemp
);
3240 SetDlgItemTextW(hDlg
, IDC_MODIFIED_LABEL
, szTemp
);
3241 SetDlgItemTextW(hDlg
, IDC_ACCESSED_LABEL
, szTemp
);
3244 if (EventLog
->Permanent
)
3246 SendDlgItemMessageW(hDlg
, IDC_UPDOWN_MAXLOGSIZE
, UDM_SETRANGE32
, (WPARAM
)1, (LPARAM
)0x3FFFC0);
3247 SendDlgItemMessageW(hDlg
, IDC_UPDOWN_EVENTS_AGE
, UDM_SETRANGE
, 0, (LPARAM
)MAKELONG(365, 1));
3249 SetDlgItemInt(hDlg
, IDC_EDIT_MAXLOGSIZE
, dwMaxSize
, FALSE
);
3250 SetDlgItemInt(hDlg
, IDC_EDIT_EVENTS_AGE
, dwRetention
, FALSE
);
3252 if (dwRetention
== 0)
3254 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_OVERWRITE_AS_NEEDED
);
3255 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, FALSE
);
3256 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, FALSE
);
3258 else if (dwRetention
== INFINITE
)
3260 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_NO_OVERWRITE
);
3261 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, FALSE
);
3262 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, FALSE
);
3266 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_OVERWRITE_OLDER_THAN
);
3267 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, TRUE
);
3268 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, TRUE
);
3273 // TODO: Hide the unused controls! Or, just use another type of property sheet!
3277 /* Message handler for EventLog Properties dialog */
3279 EventLogPropProc(HWND hDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
3283 EventLog
= (PEVENTLOG
)GetWindowLongPtrW(hDlg
, DWLP_USER
);
3289 EventLog
= (PEVENTLOG
)((LPPROPSHEETPAGE
)lParam
)->lParam
;
3290 SetWindowLongPtrW(hDlg
, DWLP_USER
, (LONG_PTR
)EventLog
);
3292 InitPropertiesDlg(hDlg
, EventLog
);
3294 PropSheet_UnChanged(GetParent(hDlg
), hDlg
);
3295 return (INT_PTR
)TRUE
;
3299 return (INT_PTR
)TRUE
;
3302 switch (LOWORD(wParam
))
3306 EndDialog(hDlg
, LOWORD(wParam
));
3307 return (INT_PTR
)TRUE
;
3309 case IDC_OVERWRITE_AS_NEEDED
:
3311 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_OVERWRITE_AS_NEEDED
);
3312 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, FALSE
);
3313 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, FALSE
);
3317 case IDC_OVERWRITE_OLDER_THAN
:
3319 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_OVERWRITE_OLDER_THAN
);
3320 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, TRUE
);
3321 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, TRUE
);
3325 case IDC_NO_OVERWRITE
:
3327 CheckRadioButton(hDlg
, IDC_OVERWRITE_AS_NEEDED
, IDC_NO_OVERWRITE
, IDC_NO_OVERWRITE
);
3328 EnableDlgItem(hDlg
, IDC_EDIT_EVENTS_AGE
, FALSE
);
3329 EnableDlgItem(hDlg
, IDC_UPDOWN_EVENTS_AGE
, FALSE
);
3335 L
"Help not implemented yet!",
3337 MB_OK
| MB_ICONINFORMATION
);
3338 return (INT_PTR
)TRUE
;
3346 return (INT_PTR
)FALSE
;
3350 EventLogProperties(HINSTANCE hInstance
, HWND hWndParent
, PEVENTLOGFILTER EventLogFilter
)
3353 PROPSHEETHEADERW psh
;
3354 PROPSHEETPAGEW psp
[1]; // 2
3357 * Bail out if there is no available filter, or if the filter
3358 * contains more than one log.
3360 if (!EventLogFilter
)
3363 EventLogFilter_AddRef(EventLogFilter
);
3365 if (EventLogFilter
->NumOfEventLogs
> 1 ||
3366 EventLogFilter
->EventLogs
[0] == NULL
)
3372 psh
.dwSize
= sizeof(psh
);
3373 psh
.dwFlags
= PSH_PROPSHEETPAGE
/*| PSH_USEICONID */ | PSH_PROPTITLE
| PSH_HASHELP
/*| PSH_NOCONTEXTHELP */ /*| PSH_USECALLBACK */;
3374 psh
.hInstance
= hInstance
;
3375 psh
.hwndParent
= hWndParent
;
3376 // psh.pszIcon = MAKEINTRESOURCEW(IDI_APPICON); // Disabled because it only sets the small icon; the big icon is a stretched version of the small one.
3377 psh
.pszCaption
= EventLogFilter
->EventLogs
[0]->LogName
;
3380 psh
.nPages
= ARRAYSIZE(psp
);
3381 // psh.pfnCallback = PropSheetCallback;
3383 /* Log properties page */
3384 psp
[0].dwSize
= sizeof(psp
[0]);
3385 psp
[0].dwFlags
= PSP_HASHELP
;
3386 psp
[0].hInstance
= hInstance
;
3387 psp
[0].pszTemplate
= MAKEINTRESOURCEW(IDD_LOGPROPERTIES_GENERAL
);
3388 psp
[0].pfnDlgProc
= EventLogPropProc
;
3389 psp
[0].lParam
= (LPARAM
)EventLogFilter
->EventLogs
[0];
3392 /* TODO: Log sources page */
3393 psp
[1].dwSize
= sizeof(psp
[1]);
3394 psp
[1].dwFlags
= PSP_HASHELP
;
3395 psp
[1].hInstance
= hInstance
;
3396 psp
[1].pszTemplate
= MAKEINTRESOURCEW(IDD_GENERAL_PAGE
);
3397 psp
[1].pfnDlgProc
= GeneralPageWndProc
;
3398 psp
[0].lParam
= (LPARAM
)EventLogFilter
->EventLogs
[0];
3401 /* Create the property sheet */
3402 ret
= PropertySheetW(&psh
);
3405 EventLogFilter_Release(EventLogFilter
);
3409 /* Message handler for Event Details dialog */
3410 static HWND hWndDetailsCtrl
= NULL
; // May go into the DWLP_USER
3411 static HWND hWndGrip
= NULL
;
3412 static INT cxMin
, cyMin
; // In window coordinates
3413 static INT cxOld
, cyOld
; // In client coordinates
3416 EventDetails(HWND hDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
3423 INT sbVXSize
, sbHYSize
;
3426 hWndDetailsCtrl
= CreateEventDetailsCtrl(hInst
, hDlg
, lParam
);
3427 if (!hWndDetailsCtrl
)
3430 return (INT_PTR
)TRUE
;
3433 /* Create a size grip if the dialog has a sizing border */
3434 GetClientRect(hDlg
, &rcWnd
);
3435 dwStyle
= GetWindowLongPtrW(hDlg
, GWL_STYLE
);
3436 sbVXSize
= GetSystemMetrics(SM_CXVSCROLL
);
3437 sbHYSize
= GetSystemMetrics(SM_CYHSCROLL
);
3438 if (dwStyle
& WS_THICKFRAME
/* == WS_SIZEBOX */)
3440 hWndGrip
= CreateWindowW(WC_SCROLLBARW
,
3442 WS_CHILD
| WS_VISIBLE
| /**/ WS_CLIPSIBLINGS
| /**/ SBS_SIZEGRIP
| SBS_SIZEBOXBOTTOMRIGHTALIGN
,
3443 rcWnd
.right
- sbVXSize
,
3444 rcWnd
.bottom
- sbHYSize
,
3452 // SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)hWndDetailsCtrl);
3455 * Compute the minimum window size (in window coordinates) by
3456 * adding the widths/heights of the "Help" and "Close" buttons,
3457 * together with the margins, and add some minimal spacing
3458 * between the buttons.
3460 GetWindowRect(hDlg
, &rcWnd
);
3463 GetWindowRect(GetDlgItem(hDlg
, IDHELP
), &rect
);
3464 cxMin
+= (rect
.right
- rect
.left
) + (rect
.left
- rcWnd
.left
); // == (rect.right - rcWnd.left);
3465 cyMin
+= (rect
.bottom
- rect
.top
) + (rcWnd
.bottom
- rect
.bottom
); // == (rcWnd.bottom - rect.top);
3467 GetWindowRect(GetDlgItem(hDlg
, IDOK
), &rect
);
3468 cxMin
+= (rect
.right
- rect
.left
) + (rcWnd
.right
- rect
.right
); // == (rcWnd.right - rect.left);
3469 cyMin
+= (rect
.bottom
- rect
.top
) + (rcWnd
.bottom
- rect
.bottom
); // == (rcWnd.bottom - rect.top);
3472 * Convert the window rect from window to client coordinates
3473 * in order to retrieve the sizes of the left and top margins,
3474 * and add some extra space.
3476 MapWindowPoints(HWND_DESKTOP
/*NULL*/, hDlg
, (LPPOINT
)&rcWnd
, sizeof(RECT
)/sizeof(POINT
));
3478 cxMin
+= -2*rcWnd
.left
; // Minimal spacing between the buttons == 2 * left margin
3479 cyMin
+= -rcWnd
.top
+ 12; // Add some space on top
3481 GetClientRect(hDlg
, &rcWnd
);
3482 cxOld
= rcWnd
.right
- rcWnd
.left
;
3483 cyOld
= rcWnd
.bottom
- rcWnd
.top
;
3485 /* Show event info on dialog control */
3486 SendMessageW(hWndDetailsCtrl
, EVT_DISPLAY
, 0, 0);
3488 // SetWindowPos(hWndDetailsCtrl, NULL,
3490 // (rcWnd.right - rcWnd.left),
3491 // (rcWnd.bottom - rcWnd.top),
3492 // SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
3495 * Hide the placeholder static control and show the event details
3496 * control instead. Note that the placeholder is here so far just
3497 * to get the dimensions right in the dialog resource editor.
3498 * I plan to remove it and use a custom control with a suitable
3499 * window class for it, that would create the event details control
3502 ShowWindow(GetDlgItem(hDlg
, IDC_STATIC
), SW_HIDE
);
3503 ShowWindow(hWndDetailsCtrl
, SW_SHOW
);
3504 return (INT_PTR
)TRUE
;
3508 if (IsWindow(hWndDetailsCtrl
))
3509 DestroyWindow(hWndDetailsCtrl
);
3510 hWndDetailsCtrl
= NULL
;
3511 return (INT_PTR
)TRUE
;
3514 switch (LOWORD(wParam
))
3518 EndDialog(hDlg
, LOWORD(wParam
));
3519 return (INT_PTR
)TRUE
;
3523 L
"Help not implemented yet!",
3525 MB_OK
| MB_ICONINFORMATION
);
3526 return (INT_PTR
)TRUE
;
3534 if (((HWND
)wParam
== hWndGrip
) && (LOWORD(lParam
) == HTCLIENT
))
3536 SetCursor(LoadCursorW(NULL
, IDC_SIZENWSE
));
3537 SetWindowLongPtrW(hDlg
, DWLP_MSGRESULT
, TRUE
);
3538 return (INT_PTR
)TRUE
;
3544 /* Forbid resizing the dialog smaller than its minimal size */
3545 PRECT dragRect
= (PRECT
)lParam
;
3547 if ((wParam
== WMSZ_LEFT
) || (wParam
== WMSZ_TOPLEFT
) || (wParam
== WMSZ_BOTTOMLEFT
))
3549 if (dragRect
->right
- dragRect
->left
< cxMin
)
3550 dragRect
->left
= dragRect
->right
- cxMin
;
3553 if ((wParam
== WMSZ_RIGHT
) || (wParam
== WMSZ_TOPRIGHT
) || (wParam
== WMSZ_BOTTOMRIGHT
))
3555 if (dragRect
->right
- dragRect
->left
< cxMin
)
3556 dragRect
->right
= dragRect
->left
+ cxMin
;
3559 if ((wParam
== WMSZ_TOP
) || (wParam
== WMSZ_TOPLEFT
) || (wParam
== WMSZ_TOPRIGHT
))
3561 if (dragRect
->bottom
- dragRect
->top
< cyMin
)
3562 dragRect
->top
= dragRect
->bottom
- cyMin
;
3565 if ((wParam
== WMSZ_BOTTOM
) || (wParam
== WMSZ_BOTTOMLEFT
) || (wParam
== WMSZ_BOTTOMRIGHT
))
3567 if (dragRect
->bottom
- dragRect
->top
< cyMin
)
3568 dragRect
->bottom
= dragRect
->top
+ cyMin
;
3571 SetWindowLongPtrW(hDlg
, DWLP_MSGRESULT
, TRUE
);
3572 return (INT_PTR
)TRUE
;
3577 INT cx
= LOWORD(lParam
);
3578 INT cy
= HIWORD(lParam
);
3584 hdwp
= BeginDeferWindowPos(4);
3586 /* Resize the event details control window */
3588 hItemWnd
= hWndDetailsCtrl
;
3589 GetWindowRect(hItemWnd
, &rect
);
3590 MapWindowPoints(HWND_DESKTOP
/*NULL*/, hDlg
, (LPPOINT
)&rect
, sizeof(RECT
)/sizeof(POINT
));
3593 hdwp
= DeferWindowPos(hdwp
,
3597 (rect
.right
- rect
.left
) + (cx
- cxOld
),
3598 (rect
.bottom
- rect
.top
) + (cy
- cyOld
),
3599 SWP_NOMOVE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
3601 /* Move the buttons */
3603 hItemWnd
= GetDlgItem(hDlg
, IDHELP
);
3604 GetWindowRect(hItemWnd
, &rect
);
3605 MapWindowPoints(HWND_DESKTOP
/*NULL*/, hDlg
, (LPPOINT
)&rect
, sizeof(RECT
)/sizeof(POINT
));
3608 hdwp
= DeferWindowPos(hdwp
,
3612 rect
.top
+ (cy
- cyOld
),
3614 SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
3616 hItemWnd
= GetDlgItem(hDlg
, IDOK
);
3617 GetWindowRect(hItemWnd
, &rect
);
3618 MapWindowPoints(HWND_DESKTOP
/*NULL*/, hDlg
, (LPPOINT
)&rect
, sizeof(RECT
)/sizeof(POINT
));
3621 hdwp
= DeferWindowPos(hdwp
,
3624 rect
.left
+ (cx
- cxOld
),
3625 rect
.top
+ (cy
- cyOld
),
3627 SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
3629 /* Move the size grip */
3630 if (hWndGrip
&& hdwp
)
3632 GetWindowRect(hWndGrip
, &rect
);
3633 MapWindowPoints(HWND_DESKTOP
/*NULL*/, hDlg
, (LPPOINT
)&rect
, sizeof(RECT
)/sizeof(POINT
));
3635 hdwp
= DeferWindowPos(hdwp
,
3638 rect
.left
+ (cx
- cxOld
),
3639 rect
.top
+ (cy
- cyOld
),
3641 SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
3645 EndDeferWindowPos(hdwp
);
3647 /* Hide the size grip if we are in maximized mode */
3649 ShowWindow(hWndGrip
, (wParam
== SIZE_MAXIMIZED
) ? SW_HIDE
: SW_SHOW
);
3654 SetWindowLongPtrW(hDlg
, DWLP_MSGRESULT
, 0);
3655 return (INT_PTR
)TRUE
;
3659 return (INT_PTR
)FALSE
;