1 /*
2  *  ReactOS Win32 Applications
3  *  Copyright (C) 2007 ReactOS Team
4  *
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.
9  *
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.
14  *
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.
18  */
19 /*
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)
25  *                  Eric Kohl
26  *                  Hermes Belusca-Maito
27  */
28 
29 #include "eventvwr.h"
30 #include "evtdetctl.h"
31 
32 #include <sddl.h> // For ConvertSidToStringSidW
33 #include <shellapi.h>
34 #include <shlwapi.h>
35 
36 // #include "resource.h"
37 
38 #define LVM_PROGRESS    (WM_APP + 1)    // Used by the subclassed ListView
39 
40 static const LPCWSTR szWindowClass       = L"EVENTVWR"; /* The main window class name */
41 static const WCHAR   EVENTLOG_BASE_KEY[] = L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\";
42 
43 /* The 3 system logs that should always exist in the user's system */
44 static const LPCWSTR SystemLogs[] =
45 {
46     L"Application",
47     L"Security",
48     L"System"
49 };
50 
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"
58 
59 #define MAX_LOADSTRING 255
60 
61 #define SPLIT_WIDTH 4
62 
63 /* Globals */
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 */
71 
72 INT  nVSplitPos;                            /* Vertical splitter (1) position */
73 INT  nHSplitPos;                            /* Horizontal splitter (2) position */
74 BYTE bSplit = 0;                            /* Splitter state:
75                                              * 0: No splitting;
76                                              * 1: Vertical splitting;
77                                              * 2: Horizontal splitting.
78                                              */
79 
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 */
87 
88 HTREEITEM htiSystemLogs = NULL, htiAppLogs = NULL, htiUserLogs = NULL;
89 
90 
91 /* Global event records cache for the current active event log filter */
92 DWORD g_TotalRecords = 0;
93 PEVENTLOGRECORD *g_RecordPtrs = NULL;
94 
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;
100 
101 HANDLE hEnumEventsThread = NULL;
102 HANDLE hStopEnumEvent    = NULL;
103 
104 /*
105  * Setting EnumFilter to a valid pointer and raising the hStartEnumEvent event
106  * triggers the event-enumerator thread to perform a new enumeration.
107  */
108 PEVENTLOGFILTER EnumFilter = NULL;
109 HANDLE hStartStopEnumEvent = NULL;  // End-of-application event
110 HANDLE hStartEnumEvent     = NULL;  // Command event
111 
112 /* Default Open/Save-As dialog box */
113 OPENFILENAMEW sfn;
114 
115 
116 /* Forward declarations of functions included in this code module */
117 
118 static DWORD WINAPI
119 StartStopEnumEventsThread(IN LPVOID lpParameter);
120 
121 VOID BuildLogListAndFilterList(IN LPCWSTR lpComputerName);
122 VOID FreeLogList(VOID);
123 VOID FreeLogFilterList(VOID);
124 
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);
130 
131 
132 int APIENTRY
133 wWinMain(HINSTANCE hInstance,
134          HINSTANCE hPrevInstance,
135          LPWSTR    lpCmdLine,
136          int       nCmdShow)
137 {
138     HANDLE hThread;
139     INITCOMMONCONTROLSEX iccx;
140     HMODULE hRichEdit;
141     HACCEL hAccelTable;
142     MSG msg;
143 
144     UNREFERENCED_PARAMETER(hPrevInstance);
145     UNREFERENCED_PARAMETER(lpCmdLine);
146 
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);
153 
154     /* Load the RichEdit DLL to add support for RichEdit controls */
155     hRichEdit = LoadLibraryW(L"riched20.dll");
156     if (!hRichEdit)
157         return -1;
158 
159     msg.wParam = (WPARAM)-1;
160 
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));
167 
168     if (!MyRegisterClass(hInstance))
169         goto Quit;
170 
171     /* Perform application initialization */
172     if (!InitInstance(hInstance, nCmdShow))
173         goto Quit;
174 
175     hAccelTable = LoadAcceleratorsW(hInstance, MAKEINTRESOURCEW(IDA_EVENTVWR));
176 
177     /* Create the Start/Stop enumerator thread */
178     // Manual-reset event
179     hStartStopEnumEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
180     if (!hStartStopEnumEvent)
181         goto Cleanup;
182 
183     // Auto-reset event
184     hStartEnumEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
185     if (!hStartEnumEvent)
186         goto Cleanup;
187 
188     hThread = CreateThread(NULL, 0,
189                            StartStopEnumEventsThread,
190                            NULL, 0, NULL);
191     if (!hThread)
192         goto Cleanup;
193 
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);
200 
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.
203 
204     /* Main message loop */
205     while (GetMessageW(&msg, NULL, 0, 0))
206     {
207         if (!TranslateAcceleratorW(hwndMainWindow, hAccelTable, &msg))
208         {
209             TranslateMessage(&msg);
210             DispatchMessageW(&msg);
211         }
212     }
213 
214     SetEvent(hStartStopEnumEvent);
215     WaitForSingleObject(hThread, INFINITE);
216     CloseHandle(hThread);
217 
218     /* Free the filters list and the event logs list */
219     FreeLogFilterList();
220     FreeLogList();
221 
222 Cleanup:
223     /* Handle cleanup */
224     if (hStartEnumEvent)
225         CloseHandle(hStartEnumEvent);
226     if (hStartStopEnumEvent)
227         CloseHandle(hStartStopEnumEvent);
228 
229 Quit:
230     FreeLibrary(hRichEdit);
231 
232     return (int)msg.wParam;
233 }
234 
235 
236 /* GENERIC HELPER FUNCTIONS ***************************************************/
237 
238 VOID
239 ShowLastWin32Error(VOID)
240 {
241     DWORD dwError;
242     LPWSTR lpMessageBuffer;
243 
244     dwError = GetLastError();
245     if (dwError == ERROR_SUCCESS)
246         return;
247 
248     if (!FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
249                         FORMAT_MESSAGE_FROM_SYSTEM |
250                         FORMAT_MESSAGE_IGNORE_INSERTS,
251                         NULL,
252                         dwError,
253                         LANG_USER_DEFAULT,
254                         (LPWSTR)&lpMessageBuffer,
255                         0, NULL))
256     {
257         return;
258     }
259 
260     MessageBoxW(hwndMainWindow, lpMessageBuffer, szTitle, MB_OK | MB_ICONERROR);
261     LocalFree(lpMessageBuffer);
262 }
263 
264 VOID
265 EventTimeToSystemTime(IN DWORD EventTime,
266                       OUT PSYSTEMTIME pSystemTime)
267 {
268     SYSTEMTIME st1970 = { 1970, 1, 0, 1, 0, 0, 0, 0 };
269     FILETIME ftLocal;
270     union
271     {
272         FILETIME ft;
273         ULONGLONG ll;
274     } u1970, uUCT;
275 
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);
282 }
283 
284 /*
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.
288  */
289 LPWSTR
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,
294     IN DWORD    nSize,
295     IN va_list* Arguments OPTIONAL)
296 {
297     HMODULE hLibrary;
298     DWORD dwLength;
299     LPWSTR lpMsgBuf = NULL;
300 
301     hLibrary = LoadLibraryExW(lpMessageDll, NULL,
302                               /* LOAD_LIBRARY_AS_IMAGE_RESOURCE | */ LOAD_LIBRARY_AS_DATAFILE);
303     if (hLibrary == NULL)
304         return NULL;
305 
306     /* Sanitize dwFlags */
307     dwFlags &= ~FORMAT_MESSAGE_FROM_STRING;
308     dwFlags |= FORMAT_MESSAGE_FROM_HMODULE;
309 
310     _SEH2_TRY
311     {
312         /*
313          * Retrieve the message string without appending extra newlines.
314          * Wrap in SEH to protect from invalid string parameters.
315          */
316         _SEH2_TRY
317         {
318             dwLength = FormatMessageW(dwFlags,
319                                    /* FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
320                                       FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, */
321                                       hLibrary,
322                                       dwMessageId,
323                                       LANG_USER_DEFAULT,
324                                       (LPWSTR)&lpMsgBuf,
325                                       nSize,
326                                       Arguments);
327         }
328         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
329         {
330             dwLength = 0;
331 
332             /*
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.
342              */
343             if (((dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) || lpMsgBuf) &&
344                 !(dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS))
345             {
346                 /* Remove any possible harmful flags and always ignore inserts */
347                 dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY;
348                 dwFlags |=  FORMAT_MESSAGE_IGNORE_INSERTS;
349 
350                 /* If this call also throws an exception, we are really dead */
351                 dwLength = FormatMessageW(dwFlags,
352                                           hLibrary,
353                                           dwMessageId,
354                                           LANG_USER_DEFAULT,
355                                           (LPWSTR)&lpMsgBuf,
356                                           nSize,
357                                           NULL /* Arguments */);
358             }
359         }
360         _SEH2_END;
361     }
362     _SEH2_FINALLY
363     {
364         FreeLibrary(hLibrary);
365     }
366     _SEH2_END;
367 
368     if (dwLength == 0)
369     {
370         ASSERT(lpMsgBuf == NULL);
371         lpMsgBuf = NULL;
372     }
373     else
374     {
375         ASSERT(lpMsgBuf);
376     }
377 
378     return lpMsgBuf;
379 }
380 
381 /*
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.
385  */
386 LPWSTR
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,
391     IN DWORD    nSize,
392     IN va_list* Arguments OPTIONAL)
393 {
394     BOOL Success = FALSE;
395     SIZE_T cbLength;
396     LPWSTR szMessageDllList;
397     LPWSTR szDll;
398     LPWSTR lpMsgBuf = NULL;
399 
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)
406         return NULL;
407     RtlCopyMemory(szMessageDllList, lpMessageDllList, cbLength);
408 
409     /* Loop through the list of message DLLs */
410     szDll = wcstok(szMessageDllList, EVENT_DLL_SEPARATOR);
411     while ((szDll != NULL) && !Success)
412     {
413         // Uses LANG_USER_DEFAULT
414         lpMsgBuf = GetMessageStringFromDll(szDll,
415                                            dwFlags,
416                                            dwMessageId,
417                                            nSize,
418                                            Arguments);
419         if (lpMsgBuf)
420         {
421             /* The ID was found and the message was formatted */
422             Success = TRUE;
423             break;
424         }
425 
426         /*
427          * The DLL could not be loaded, or the message could not be found,
428          * try the next DLL, if any.
429          */
430         szDll = wcstok(NULL, EVENT_DLL_SEPARATOR);
431     }
432 
433     HeapFree(GetProcessHeap(), 0, szMessageDllList);
434 
435     return lpMsgBuf;
436 }
437 
438 
439 typedef struct
440 {
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;
446 
447 DWORD
448 ApplyParameterStringsToMessage(
449     IN LPCWSTR  lpMessageDllList,
450     IN BOOL     bMessagePreFormatted,
451     IN CONST LPCWSTR pMessage,
452     OUT LPWSTR* pFinalMessage)
453 {
454     /*
455      * This code is heavily adapted from the MSDN example:
456      * https://msdn.microsoft.com/en-us/library/windows/desktop/bb427356.aspx
457      * with bugs removed.
458      */
459 
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
464     size_t cch = 0;
465     DWORD i = 0;
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;
469 
470     *pFinalMessage = NULL;
471 
472     /* Determine the number of parameter insertion strings in pMessage */
473     if (bMessagePreFormatted)
474     {
475         while ((pTempMessage = wcschr(pTempMessage, L'%')))
476         {
477             pTempMessage++;
478             if (isdigit(*pTempMessage))
479             {
480                 dwParamCount++;
481                 while (isdigit(*++pTempMessage)) ;
482             }
483         }
484     }
485     else
486     {
487         while ((pTempMessage = wcsstr(pTempMessage, L"%%")))
488         {
489             pTempMessage += 2;
490             if (isdigit(*pTempMessage))
491             {
492                 dwParamCount++;
493                 while (isdigit(*++pTempMessage)) ;
494             }
495         }
496     }
497 
498     /* If there are no parameter insertion strings in pMessage, just return */
499     if (dwParamCount == 0)
500     {
501         // *pFinalMessage = NULL;
502         goto Cleanup;
503     }
504 
505     /* Allocate the array of parameter string format data */
506     pParamData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwParamCount * sizeof(param_strings_format_data));
507     if (!pParamData)
508     {
509         Status = ERROR_OUTOFMEMORY;
510         goto Cleanup;
511     }
512 
513     /*
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.
516      */
517     pTempMessage = (LPWSTR)pMessage;
518     if (bMessagePreFormatted)
519     {
520         while ((pTempMessage = wcschr(pTempMessage, L'%')) && (i < dwParamCount))
521         {
522             pTempMessage++;
523             if (isdigit(*pTempMessage))
524             {
525                 pParamData[i].pStartingAddress = pTempMessage-1;
526                 pParamData[i].pParameterID = (DWORD)_wtol(pTempMessage);
527 
528                 while (isdigit(*++pTempMessage)) ;
529 
530                 pParamData[i].pEndingAddress = pTempMessage;
531                 i++;
532             }
533         }
534     }
535     else
536     {
537         while ((pTempMessage = wcsstr(pTempMessage, L"%%")) && (i < dwParamCount))
538         {
539             pTempMessage += 2;
540             if (isdigit(*pTempMessage))
541             {
542                 pParamData[i].pStartingAddress = pTempMessage-2;
543                 pParamData[i].pParameterID = (DWORD)_wtol(pTempMessage);
544 
545                 while (isdigit(*++pTempMessage)) ;
546 
547                 pParamData[i].pEndingAddress = pTempMessage;
548                 i++;
549             }
550         }
551     }
552 
553     /* Retrieve each parameter string */
554     for (i = 0; i < dwParamCount; i++)
555     {
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,
562                                         0, NULL);
563         if (!pParamData[i].pParameter)
564         {
565             /* Skip the insertion string */
566             continue;
567         }
568 
569         cchParams += wcslen(pParamData[i].pParameter);
570     }
571 
572     /*
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.
575      */
576     cchBuffer = wcslen(pMessage) + cchParams + 1;
577     *pFinalMessage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cchBuffer * sizeof(WCHAR));
578     if (!*pFinalMessage)
579     {
580         Status = ERROR_OUTOFMEMORY;
581         goto Cleanup;
582     }
583 
584     pTempFinalMessage = *pFinalMessage;
585 
586     /* Build the final message string */
587     pTempMessage = (LPWSTR)pMessage;
588     for (i = 0; i < dwParamCount; i++)
589     {
590         /* Append the segment from pMessage */
591         cch = pParamData[i].pStartingAddress - pTempMessage;
592         StringCchCopyNW(pTempFinalMessage, cchBuffer, pTempMessage, cch);
593         pTempMessage = pParamData[i].pEndingAddress;
594         cchBuffer -= cch;
595         pTempFinalMessage += cch;
596 
597         /* Append the parameter string */
598         if (pParamData[i].pParameter)
599         {
600             StringCchCopyW(pTempFinalMessage, cchBuffer, pParamData[i].pParameter);
601             cch = wcslen(pParamData[i].pParameter); // pTempFinalMessage
602         }
603         else
604         {
605             /*
606              * We failed to retrieve the parameter string before, so just
607              * place back the original string placeholder.
608              */
609             cch = pParamData[i].pEndingAddress /* == pTempMessage */ - pParamData[i].pStartingAddress;
610             StringCchCopyNW(pTempFinalMessage, cchBuffer, pParamData[i].pStartingAddress, cch);
611             // cch = wcslen(pTempFinalMessage);
612         }
613         cchBuffer -= cch;
614         pTempFinalMessage += cch;
615     }
616 
617     /* Append the last segment from pMessage */
618     StringCchCopyW(pTempFinalMessage, cchBuffer, pTempMessage);
619 
620 Cleanup:
621 
622     // if (Status != ERROR_SUCCESS)
623         // *pFinalMessage = NULL;
624 
625     if (pParamData)
626     {
627         for (i = 0; i < dwParamCount; i++)
628         {
629             if (pParamData[i].pParameter)
630                 LocalFree(pParamData[i].pParameter);
631         }
632 
633         HeapFree(GetProcessHeap(), 0, pParamData);
634     }
635 
636     return Status;
637 }
638 
639 
640 /*
641  * The following functions were adapted from
642  * shell32!dialogs/filedefext.cpp:``SH_...'' functions.
643  */
644 
645 UINT
646 FormatInteger(LONGLONG Num, LPWSTR pwszResult, UINT cchResultMax)
647 {
648     WCHAR wszNumber[24];
649     WCHAR wszDecimalSep[8], wszThousandSep[8];
650     NUMBERFMTW nf;
651     WCHAR wszGrouping[12];
652     INT cchGrouping;
653     INT cchResult;
654     INT i;
655 
656     // Print the number in uniform mode
657     swprintf(wszNumber, L"%I64u", Num);
658 
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));
662 
663     // Initialize format for printing the number in bytes
664     ZeroMemory(&nf, sizeof(nf));
665     nf.lpDecimalSep = wszDecimalSep;
666     nf.lpThousandSep = wszThousandSep;
667 
668     // Get system string for groups separator
669     cchGrouping = GetLocaleInfoW(LOCALE_USER_DEFAULT,
670                                  LOCALE_SGROUPING,
671                                  wszGrouping,
672                                  _countof(wszGrouping));
673 
674     // Convert grouping specs from string to integer
675     for (i = 0; i < cchGrouping; i++)
676     {
677         WCHAR wch = wszGrouping[i];
678 
679         if (wch >= L'0' && wch <= L'9')
680             nf.Grouping = nf.Grouping * 10 + (wch - L'0');
681         else if (wch != L';')
682             break;
683     }
684 
685     if ((nf.Grouping % 10) == 0)
686         nf.Grouping /= 10;
687     else
688         nf.Grouping *= 10;
689 
690     // Format the number
691     cchResult = GetNumberFormatW(LOCALE_USER_DEFAULT,
692                                  0,
693                                  wszNumber,
694                                  &nf,
695                                  pwszResult,
696                                  cchResultMax);
697 
698     if (!cchResult)
699         return 0;
700 
701     // GetNumberFormatW returns number of characters including UNICODE_NULL
702     return cchResult - 1;
703 }
704 
705 UINT
706 FormatByteSize(LONGLONG cbSize, LPWSTR pwszResult, UINT cchResultMax)
707 {
708     INT cchWritten;
709     LPWSTR pwszEnd;
710     size_t cchRemaining;
711 
712     /* Write formated bytes count */
713     cchWritten = FormatInteger(cbSize, pwszResult, cchResultMax);
714     if (!cchWritten)
715         return 0;
716 
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;
723 
724     return cchResultMax - cchRemaining;
725 }
726 
727 LPWSTR
728 FormatFileSizeWithBytes(const PULARGE_INTEGER lpQwSize, LPWSTR pwszResult, UINT cchResultMax)
729 {
730     UINT cchWritten;
731     LPWSTR pwszEnd;
732     size_t cchRemaining;
733 
734     /* Format bytes in KBs, MBs etc */
735     if (StrFormatByteSizeW(lpQwSize->QuadPart, pwszResult, cchResultMax) == NULL)
736         return NULL;
737 
738     /* If there is less bytes than 1KB, we have nothing to do */
739     if (lpQwSize->QuadPart < 1024)
740         return pwszResult;
741 
742     /* Concatenate " (" */
743     cchWritten = wcslen(pwszResult);
744     pwszEnd = pwszResult + cchWritten;
745     cchRemaining = cchResultMax - cchWritten;
746     StringCchCopyExW(pwszEnd, cchRemaining, L" (", &pwszEnd, &cchRemaining, 0);
747 
748     /* Write formated bytes count */
749     cchWritten = FormatByteSize(lpQwSize->QuadPart, pwszEnd, cchRemaining);
750     pwszEnd += cchWritten;
751     cchRemaining -= cchWritten;
752 
753     /* Copy ")" to the buffer */
754     StringCchCopyW(pwszEnd, cchRemaining, L")");
755 
756     return pwszResult;
757 }
758 
759 /* Adapted from shell32!dialogs/filedefext.cpp:``CFileDefExt::GetFileTimeString'' */
760 BOOL
761 GetFileTimeString(LPFILETIME lpFileTime, LPWSTR pwszResult, UINT cchResult)
762 {
763     FILETIME ft;
764     SYSTEMTIME st;
765     int cchWritten;
766     size_t cchRemaining = cchResult;
767     LPWSTR pwszEnd = pwszResult;
768 
769     if (!FileTimeToLocalFileTime(lpFileTime, &ft) || !FileTimeToSystemTime(&ft, &st))
770         return FALSE;
771 
772     cchWritten = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, pwszEnd, cchRemaining);
773     if (cchWritten)
774         --cchWritten; // GetDateFormatW returns count with terminating zero
775     // else
776         // ERR("GetDateFormatW failed\n");
777 
778     cchRemaining -= cchWritten;
779     pwszEnd += cchWritten;
780 
781     StringCchCopyExW(pwszEnd, cchRemaining, L", ", &pwszEnd, &cchRemaining, 0);
782 
783     cchWritten = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, pwszEnd, cchRemaining);
784     if (cchWritten)
785         --cchWritten; // GetTimeFormatW returns count with terminating zero
786     // else
787         // ERR("GetTimeFormatW failed\n");
788 
789     return TRUE;
790 }
791 
792 
793 HTREEITEM
794 TreeViewAddItem(IN HWND hTreeView,
795                 IN HTREEITEM hParent,
796                 IN LPWSTR lpText,
797                 IN INT Image,
798                 IN INT SelectedImage,
799                 IN LPARAM lParam)
800 {
801     TV_INSERTSTRUCTW Insert;
802 
803     ZeroMemory(&Insert, sizeof(Insert));
804 
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;
812 
813     Insert.item.mask |= TVIF_STATE;
814     Insert.item.stateMask = TVIS_OVERLAYMASK;
815     Insert.item.state = INDEXTOOVERLAYMASK(1);
816 
817     return TreeView_InsertItem(hTreeView, &Insert);
818 }
819 
820 
821 /* LOG HELPER FUNCTIONS *******************************************************/
822 
823 PEVENTLOG
824 AllocEventLog(IN PCWSTR ComputerName OPTIONAL,
825               IN PCWSTR LogName,
826               IN BOOL Permanent)
827 {
828     PEVENTLOG EventLog;
829     UINT cchName;
830 
831     /* Allocate a new event log entry */
832     EventLog = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*EventLog));
833     if (!EventLog)
834         return NULL;
835 
836     /* Allocate the computer name string (optional) and copy it */
837     if (ComputerName)
838     {
839         cchName = wcslen(ComputerName) + 1;
840         EventLog->ComputerName = HeapAlloc(GetProcessHeap(), 0, cchName * sizeof(WCHAR));
841         if (EventLog->ComputerName)
842             StringCchCopyW(EventLog->ComputerName, cchName, ComputerName);
843     }
844 
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)
849     {
850         HeapFree(GetProcessHeap(), 0, EventLog);
851         return NULL;
852     }
853     StringCchCopyW(EventLog->LogName, cchName, LogName);
854 
855     EventLog->Permanent = Permanent;
856 
857     return EventLog;
858 }
859 
860 VOID
861 EventLog_Free(IN PEVENTLOG EventLog)
862 {
863     if (EventLog->LogName)
864         HeapFree(GetProcessHeap(), 0, EventLog->LogName);
865 
866     if (EventLog->FileName)
867         HeapFree(GetProcessHeap(), 0, EventLog->FileName);
868 
869     HeapFree(GetProcessHeap(), 0, EventLog);
870 }
871 
872 
873 PWSTR
874 AllocAndCopyMultiStr(IN PCWSTR MultiStr OPTIONAL)
875 {
876     PWSTR pStr;
877     ULONG Length;
878 
879     if (!MultiStr)
880         return NULL;
881 
882     pStr = (PWSTR)MultiStr;
883     while (*pStr) pStr += (wcslen(pStr) + 1);
884     Length = MultiStr - pStr + 2;
885 
886     pStr = HeapAlloc(GetProcessHeap(), 0, Length * sizeof(WCHAR));
887     // NOTE: If we failed allocating the string, then fall back into no filter!
888     if (pStr)
889         RtlCopyMemory(pStr, MultiStr, Length * sizeof(WCHAR));
890 
891     return pStr;
892 }
893 
894 PEVENTLOGFILTER
895 AllocEventLogFilter(// IN PCWSTR FilterName,
896                     IN BOOL Information,
897                     IN BOOL Warning,
898                     IN BOOL Error,
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)
906 {
907     PEVENTLOGFILTER EventLogFilter;
908 
909     /* Allocate a new event log filter entry, big enough to accommodate the list of logs */
910     EventLogFilter = HeapAlloc(GetProcessHeap(),
911                                HEAP_ZERO_MEMORY,
912                                FIELD_OFFSET(EVENTLOGFILTER, EventLogs[NumOfEventLogs]));
913     if (!EventLogFilter)
914         return NULL;
915 
916     EventLogFilter->Information  = Information;
917     EventLogFilter->Warning      = Warning;
918     EventLogFilter->Error        = Error;
919     EventLogFilter->AuditSuccess = AuditSuccess;
920     EventLogFilter->AuditFailure = AuditFailure;
921 
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);
926 
927     /* Copy the list of event logs */
928     EventLogFilter->NumOfEventLogs = NumOfEventLogs;
929     RtlCopyMemory(EventLogFilter->EventLogs, EventLogs, NumOfEventLogs * sizeof(PEVENTLOG));
930 
931     /* Initialize the filter reference count */
932     EventLogFilter->ReferenceCount = 1;
933 
934     return EventLogFilter;
935 }
936 
937 VOID
938 EventLogFilter_Free(IN PEVENTLOGFILTER EventLogFilter)
939 {
940     if (EventLogFilter->Sources)
941         HeapFree(GetProcessHeap(), 0, EventLogFilter->Sources);
942 
943     if (EventLogFilter->Users)
944         HeapFree(GetProcessHeap(), 0, EventLogFilter->Users);
945 
946     if (EventLogFilter->ComputerNames)
947         HeapFree(GetProcessHeap(), 0, EventLogFilter->ComputerNames);
948 
949     HeapFree(GetProcessHeap(), 0, EventLogFilter);
950 }
951 
952 LONG EventLogFilter_AddRef(IN PEVENTLOGFILTER EventLogFilter)
953 {
954     ASSERT(EventLogFilter);
955     return InterlockedIncrement(&EventLogFilter->ReferenceCount);
956 }
957 
958 LONG EventLogFilter_Release(IN PEVENTLOGFILTER EventLogFilter)
959 {
960     LONG RefCount;
961 
962     ASSERT(EventLogFilter);
963 
964     /* When the reference count reaches zero, delete the filter */
965     RefCount = InterlockedDecrement(&EventLogFilter->ReferenceCount);
966     if (RefCount <= 0)
967     {
968         /* Remove the filter from the list */
969         /** RemoveEntryList(&EventLogFilter->ListEntry); **/
970         EventLogFilter_Free(EventLogFilter);
971     }
972 
973     return RefCount;
974 }
975 
976 void
977 TrimNulls(LPWSTR s)
978 {
979     WCHAR *c;
980 
981     if (s != NULL)
982     {
983         c = s + wcslen(s) - 1;
984         while (c >= s && iswspace(*c))
985             --c;
986         *++c = L'\0';
987     }
988 }
989 
990 BOOL
991 GetEventMessageFileDLL(IN LPCWSTR lpLogName,
992                        IN LPCWSTR SourceName,
993                        IN LPCWSTR EntryName,
994                        OUT PWCHAR lpModuleName) // TODO: Add IN DWORD BufLen
995 {
996     BOOL Success = FALSE;
997     LONG Result;
998     DWORD Type, dwSize;
999     WCHAR szModuleName[MAX_PATH];
1000     WCHAR szKeyName[MAX_PATH];
1001     HKEY hLogKey = NULL;
1002     HKEY hSourceKey = NULL;
1003 
1004     StringCbCopyW(szKeyName, sizeof(szKeyName), EVENTLOG_BASE_KEY);
1005     StringCbCatW(szKeyName, sizeof(szKeyName), lpLogName);
1006 
1007     Result = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
1008                            szKeyName,
1009                            0,
1010                            KEY_READ,
1011                            &hLogKey);
1012     if (Result != ERROR_SUCCESS)
1013         return FALSE;
1014 
1015     Result = RegOpenKeyExW(hLogKey,
1016                            SourceName,
1017                            0,
1018                            KEY_QUERY_VALUE,
1019                            &hSourceKey);
1020     if (Result != ERROR_SUCCESS)
1021     {
1022         RegCloseKey(hLogKey);
1023         return FALSE;
1024     }
1025 
1026     dwSize = sizeof(szModuleName);
1027     Result = RegQueryValueExW(hSourceKey,
1028                               EntryName,
1029                               NULL,
1030                               &Type,
1031                               (LPBYTE)szModuleName,
1032                               &dwSize);
1033     if ((Result != ERROR_SUCCESS) || (Type != REG_EXPAND_SZ && Type != REG_SZ))
1034     {
1035         szModuleName[0] = UNICODE_NULL;
1036     }
1037     else
1038     {
1039         /* NULL-terminate the string and expand it */
1040         szModuleName[dwSize / sizeof(WCHAR) - 1] = UNICODE_NULL;
1041         ExpandEnvironmentStringsW(szModuleName, lpModuleName, ARRAYSIZE(szModuleName));
1042         Success = TRUE;
1043     }
1044 
1045     RegCloseKey(hSourceKey);
1046     RegCloseKey(hLogKey);
1047 
1048     return Success;
1049 }
1050 
1051 BOOL
1052 GetEventCategory(IN LPCWSTR KeyName,
1053                  IN LPCWSTR SourceName,
1054                  IN PEVENTLOGRECORD pevlr,
1055                  OUT PWCHAR CategoryName) // TODO: Add IN DWORD BufLen
1056 {
1057     BOOL Success = FALSE;
1058     WCHAR szMessageDLL[MAX_PATH];
1059     LPWSTR lpMsgBuf = NULL;
1060 
1061     if (!GetEventMessageFileDLL(KeyName, SourceName, EVENT_CATEGORY_MESSAGE_FILE, szMessageDLL))
1062         goto Quit;
1063 
1064     /* Retrieve the message string without appending extra newlines */
1065     lpMsgBuf =
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,
1071                                 NULL);
1072     if (lpMsgBuf)
1073     {
1074         /* Trim the string */
1075         TrimNulls(lpMsgBuf);
1076 
1077         /* Copy the category name */
1078         StringCchCopyW(CategoryName, MAX_PATH, lpMsgBuf);
1079 
1080         /* Free the buffer allocated by FormatMessage */
1081         LocalFree(lpMsgBuf);
1082 
1083         /* The ID was found and the message was formatted */
1084         Success = TRUE;
1085     }
1086 
1087 Quit:
1088     if (!Success)
1089     {
1090         if (pevlr->EventCategory != 0)
1091         {
1092             StringCchPrintfW(CategoryName, MAX_PATH, L"(%lu)", pevlr->EventCategory);
1093             Success = TRUE;
1094         }
1095     }
1096 
1097     return Success;
1098 }
1099 
1100 
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
1106 {
1107     BOOL Success = FALSE;
1108     DWORD i;
1109     size_t cch;
1110     WCHAR SourceModuleName[1024];
1111     WCHAR ParameterModuleName[1024];
1112     BOOL IsParamModNameCached = FALSE;
1113     LPWSTR lpMsgBuf = NULL;
1114     LPWSTR szStringArray, szMessage;
1115     LPWSTR *szArguments;
1116 
1117     /* Get the event string array */
1118     szStringArray = (LPWSTR)((LPBYTE)pevlr + pevlr->StringOffset);
1119 
1120     /* NOTE: GetEventMessageFileDLL can return a comma-separated list of DLLs */
1121     if (!GetEventMessageFileDLL(KeyName, SourceName, EVENT_MESSAGE_FILE, SourceModuleName))
1122         goto Quit;
1123 
1124     /* Allocate space for insertion strings */
1125     szArguments = HeapAlloc(GetProcessHeap(), 0, pevlr->NumStrings * sizeof(LPVOID));
1126     if (!szArguments)
1127         goto Quit;
1128 
1129     if (!IsParamModNameCached)
1130     {
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???
1134     }
1135 
1136     if (IsParamModNameCached)
1137     {
1138         /* Not yet support for reading messages from parameter message DLL */
1139     }
1140 
1141     szMessage = szStringArray;
1142     /*
1143      * HACK:
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
1150      * and two '%'.
1151      */
1152     /* HACK part 1: Compute the full length of the string array */
1153     cch = 0;
1154     for (i = 0; i < pevlr->NumStrings; i++)
1155     {
1156         szMessage += wcslen(szMessage) + 1;
1157     }
1158     cch = szMessage - szStringArray;
1159 
1160     /* HACK part 2: Now do the HACK proper! */
1161     szMessage = szStringArray;
1162     for (i = 0; i < pevlr->NumStrings; i++)
1163     {
1164         lpMsgBuf = szMessage;
1165         while ((lpMsgBuf = wcsstr(lpMsgBuf, L"%%")))
1166         {
1167             if (isdigit(lpMsgBuf[2]))
1168             {
1169                 RtlMoveMemory(lpMsgBuf, lpMsgBuf+1, ((szStringArray + cch) - lpMsgBuf - 1) * sizeof(WCHAR));
1170             }
1171         }
1172 
1173         szArguments[i] = szMessage;
1174         szMessage += wcslen(szMessage) + 1;
1175     }
1176 
1177     /* Retrieve the message string without appending extra newlines */
1178     lpMsgBuf =
1179     GetMessageStringFromDllList(SourceModuleName,
1180                                 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
1181                                 FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_MAX_WIDTH_MASK,
1182                                 pevlr->EventID,
1183                                 0,
1184                                 (va_list*)szArguments);
1185     if (lpMsgBuf)
1186     {
1187         /* Trim the string */
1188         TrimNulls(lpMsgBuf);
1189 
1190         szMessage = NULL;
1191         Success = (ApplyParameterStringsToMessage(ParameterModuleName,
1192                                                   TRUE,
1193                                                   lpMsgBuf,
1194                                                   &szMessage) == ERROR_SUCCESS);
1195         if (Success && szMessage)
1196         {
1197             /* Free the buffer allocated by FormatMessage */
1198             LocalFree(lpMsgBuf);
1199             lpMsgBuf = szMessage;
1200         }
1201 
1202         /* Copy the event text */
1203         StringCchCopyW(EventText, EVENT_MESSAGE_EVENTTEXT_BUFFER, lpMsgBuf);
1204 
1205         /* Free the buffer allocated by FormatMessage */
1206         LocalFree(lpMsgBuf);
1207     }
1208 
1209     HeapFree(GetProcessHeap(), 0, szArguments);
1210 
1211 Quit:
1212     if (!Success)
1213     {
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);
1218 
1219         /* Append the strings */
1220         szMessage = szStringArray;
1221         for (i = 0; i < pevlr->NumStrings; i++)
1222         {
1223             StringCchCatW(EventText, EVENT_MESSAGE_EVENTTEXT_BUFFER, szMessage);
1224             StringCchCatW(EventText, EVENT_MESSAGE_EVENTTEXT_BUFFER, L"\n");
1225             szMessage += wcslen(szMessage) + 1;
1226         }
1227     }
1228 
1229     return Success;
1230 }
1231 
1232 VOID
1233 GetEventType(IN WORD dwEventType,
1234              OUT PWCHAR eventTypeText) // TODO: Add IN DWORD BufLen
1235 {
1236     switch (dwEventType)
1237     {
1238         case EVENTLOG_ERROR_TYPE:
1239             LoadStringW(hInst, IDS_EVENTLOG_ERROR_TYPE, eventTypeText, MAX_LOADSTRING);
1240             break;
1241         case EVENTLOG_WARNING_TYPE:
1242             LoadStringW(hInst, IDS_EVENTLOG_WARNING_TYPE, eventTypeText, MAX_LOADSTRING);
1243             break;
1244         case EVENTLOG_INFORMATION_TYPE:
1245             LoadStringW(hInst, IDS_EVENTLOG_INFORMATION_TYPE, eventTypeText, MAX_LOADSTRING);
1246             break;
1247         case EVENTLOG_SUCCESS:
1248             LoadStringW(hInst, IDS_EVENTLOG_SUCCESS, eventTypeText, MAX_LOADSTRING);
1249             break;
1250         case EVENTLOG_AUDIT_SUCCESS:
1251             LoadStringW(hInst, IDS_EVENTLOG_AUDIT_SUCCESS, eventTypeText, MAX_LOADSTRING);
1252             break;
1253         case EVENTLOG_AUDIT_FAILURE:
1254             LoadStringW(hInst, IDS_EVENTLOG_AUDIT_FAILURE, eventTypeText, MAX_LOADSTRING);
1255             break;
1256         default:
1257             LoadStringW(hInst, IDS_EVENTLOG_UNKNOWN_TYPE, eventTypeText, MAX_LOADSTRING);
1258             break;
1259     }
1260 }
1261 
1262 BOOL
1263 GetEventUserName(IN PEVENTLOGRECORD pelr,
1264                  IN OUT PSID *pLastSid,
1265                  OUT PWCHAR pszUser) // TODO: Add IN DWORD BufLen
1266 {
1267     PSID pCurrentSid;
1268     PWSTR StringSid;
1269     WCHAR szName[1024];
1270     WCHAR szDomain[1024];
1271     SID_NAME_USE peUse;
1272     DWORD cchName = ARRAYSIZE(szName);
1273     DWORD cchDomain = ARRAYSIZE(szDomain);
1274     BOOL Success = FALSE;
1275 
1276     /* Point to the SID */
1277     pCurrentSid = (PSID)((LPBYTE)pelr + pelr->UserSidOffset);
1278 
1279     if (!IsValidSid(pCurrentSid))
1280     {
1281         *pLastSid = NULL;
1282         return FALSE;
1283     }
1284     else if (*pLastSid && EqualSid(*pLastSid, pCurrentSid))
1285     {
1286         return TRUE;
1287     }
1288 
1289     /* User SID */
1290     if (pelr->UserSidLength > 0)
1291     {
1292         /*
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.
1297          */
1298         if (LookupAccountSidW(NULL, // FIXME: Use computer name? From the particular event?
1299                               pCurrentSid,
1300                               szName,
1301                               &cchName,
1302                               szDomain,
1303                               &cchDomain,
1304                               &peUse))
1305         {
1306             StringCchCopyW(pszUser, MAX_PATH, szName);
1307             Success = TRUE;
1308         }
1309         else if (ConvertSidToStringSidW(pCurrentSid, &StringSid))
1310         {
1311             /* Copy the string only if the user-provided buffer is big enough */
1312             if (wcslen(StringSid) + 1 <= MAX_PATH) // + 1 for NULL-terminator
1313             {
1314                 StringCchCopyW(pszUser, MAX_PATH, StringSid);
1315                 Success = TRUE;
1316             }
1317             else
1318             {
1319                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1320                 Success = FALSE;
1321             }
1322 
1323             /* Free the allocated buffer */
1324             LocalFree(StringSid);
1325         }
1326     }
1327 
1328     *pLastSid = Success ? pCurrentSid : NULL;
1329 
1330     return Success;
1331 }
1332 
1333 
1334 static VOID FreeRecords(VOID)
1335 {
1336     DWORD iIndex;
1337 
1338     if (!g_RecordPtrs)
1339         return;
1340 
1341     for (iIndex = 0; iIndex < g_TotalRecords; iIndex++)
1342     {
1343         if (g_RecordPtrs[iIndex])
1344             HeapFree(GetProcessHeap(), 0, g_RecordPtrs[iIndex]);
1345     }
1346     HeapFree(GetProcessHeap(), 0, g_RecordPtrs);
1347     g_RecordPtrs = NULL;
1348     g_TotalRecords = 0;
1349 }
1350 
1351 BOOL
1352 FilterByType(IN PEVENTLOGFILTER EventLogFilter,
1353              IN PEVENTLOGRECORD pevlr)
1354 {
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))
1361     {
1362         return FALSE;
1363     }
1364     return TRUE;
1365 }
1366 
1367 BOOL
1368 FilterByString(IN PCWSTR FilterString, // This is a multi-string
1369                IN PWSTR  String)
1370 {
1371     PCWSTR pStr;
1372 
1373     /* The filter string is NULL so it does not filter anything */
1374     if (!FilterString)
1375         return TRUE;
1376 
1377     /*
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).
1380      */
1381     if (!*FilterString && !*String)
1382         return TRUE;
1383 
1384     // if (*FilterString || *String)
1385 
1386     /*
1387      * If the filter string is empty BUT the source string is not empty,
1388      * OR vice-versa, we cannot have a match.
1389      */
1390     if ( (!*FilterString && *String) || (*FilterString && !*String) )
1391         return FALSE;
1392 
1393     /*
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.
1396      */
1397     // else if (*FilterString && *String)
1398     {
1399         pStr = FilterString;
1400         while (*pStr)
1401         {
1402             if (wcsicmp(pStr, String) == 0)
1403             {
1404                 /* We have a match, break the loop */
1405                 break;
1406             }
1407 
1408             pStr += (wcslen(pStr) + 1);
1409         }
1410         if (!*pStr) // && *String
1411         {
1412             /* We do not have a match */
1413             return FALSE;
1414         }
1415     }
1416 
1417     /* We have a match */
1418     return TRUE;
1419 }
1420 
1421 /*
1422  * The events enumerator thread.
1423  */
1424 static DWORD WINAPI
1425 EnumEventsThread(IN LPVOID lpParameter)
1426 {
1427     PEVENTLOGFILTER EventLogFilter = (PEVENTLOGFILTER)lpParameter;
1428     LPWSTR lpMachineName = NULL; // EventLogFilter->ComputerName;
1429     PEVENTLOG EventLog;
1430 
1431     ULONG LogIndex;
1432     HANDLE hEventLog;
1433     PEVENTLOGRECORD pEvlr = NULL;
1434     PBYTE pEvlrEnd;
1435     PBYTE pEvlrBuffer;
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;
1445 
1446     UINT uStep = 0, uStepAt = 0, uPos = 0;
1447 
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;
1460 
1461     SYSTEMTIME time;
1462     LVITEMW lviEventItem;
1463 
1464     /* Save the current event log filter globally */
1465     EventLogFilter_AddRef(EventLogFilter);
1466     ActiveFilter = EventLogFilter;
1467 
1468     /* Disable list view redraw */
1469     SendMessageW(hwndListView, WM_SETREDRAW, FALSE, 0);
1470 
1471     /* Clear the list view and free the cached records */
1472     ListView_DeleteAllItems(hwndListView);
1473     FreeRecords();
1474 
1475     SendMessageW(hwndListView, LVM_PROGRESS, 0, TRUE);
1476     ProgressBar_SetRange(hwndStatusProgress, 0);
1477     StatusBar_SetText(hwndStatus, 0, NULL);
1478     ShowWindow(hwndStatusProgress, SW_SHOW);
1479 
1480     /* Do a loop over the logs enumerated in the filter */
1481     // FIXME: For now we only support 1 event log per filter!
1482     LogIndex = 0;
1483     // for (LogIndex = 0; LogIndex < EventLogFilter->NumOfEventLogs; ++LogIndex)
1484     {
1485 
1486     EventLog = EventLogFilter->EventLogs[LogIndex];
1487 
1488     /* Open the event log */
1489     if (EventLog->Permanent)
1490         hEventLog = OpenEventLogW(EventLog->ComputerName, EventLog->LogName);
1491     else
1492         hEventLog = OpenBackupEventLogW(EventLog->ComputerName, EventLog->LogName); // FileName
1493 
1494     if (hEventLog == NULL)
1495     {
1496         ShowLastWin32Error();
1497         goto Cleanup;
1498     }
1499 
1500     // GetOldestEventLogRecord(hEventLog, &dwThisRecord);
1501 
1502     /* Get the total number of event log records */
1503     GetNumberOfEventLogRecords(hEventLog, &dwTotalRecords);
1504 
1505     if (dwTotalRecords > 0)
1506     {
1507         EnableMenuItem(hMainMenu, IDM_CLEAR_EVENTS, MF_BYCOMMAND | MF_ENABLED);
1508         EnableMenuItem(hMainMenu, IDM_SAVE_EVENTLOG, MF_BYCOMMAND | MF_ENABLED);
1509     }
1510     else
1511     {
1512         EnableMenuItem(hMainMenu, IDM_CLEAR_EVENTS, MF_BYCOMMAND | MF_GRAYED);
1513         EnableMenuItem(hMainMenu, IDM_SAVE_EVENTLOG, MF_BYCOMMAND | MF_GRAYED);
1514     }
1515 
1516     /* Set up the event records cache */
1517     g_RecordPtrs = HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, dwTotalRecords * sizeof(*g_RecordPtrs));
1518     if (!g_RecordPtrs)
1519     {
1520         // ShowLastWin32Error();
1521         goto Quit;
1522     }
1523     g_TotalRecords = dwTotalRecords;
1524 
1525     if (WaitForSingleObject(hStopEnumEvent, 0) == WAIT_OBJECT_0)
1526         goto Quit;
1527 
1528     LoadStringW(hInst, IDS_NOT_AVAILABLE, szNoUsername, ARRAYSIZE(szNoUsername));
1529     LoadStringW(hInst, IDS_NONE, szNoCategory, ARRAYSIZE(szNoCategory));
1530 
1531     ProgressBar_SetRange(hwndStatusProgress, MAKELPARAM(0, 100));
1532     uStepAt = (dwTotalRecords / 100) + 1;
1533 
1534     dwFlags = EVENTLOG_SEQUENTIAL_READ | (NewestEventsFirst ? EVENTLOG_FORWARDS_READ : EVENTLOG_BACKWARDS_READ);
1535 
1536     /* 0x7ffff is the maximum buffer size ReadEventLog will accept */
1537     dwWanted = 0x7ffff;
1538     pEvlr = HeapAlloc(hProcessHeap, 0, dwWanted);
1539 
1540     if (!pEvlr)
1541         goto Quit;
1542 
1543     while (dwStatus == ERROR_SUCCESS)
1544     {
1545         bResult =  ReadEventLogW(hEventLog, dwFlags, 0, pEvlr, dwWanted, &dwRead, &dwNeeded);
1546         dwStatus = GetLastError();
1547 
1548         if (!bResult && dwStatus == ERROR_INSUFFICIENT_BUFFER)
1549         {
1550             pEvlr = HeapReAlloc(hProcessHeap, 0, pEvlr, dwNeeded);
1551             dwWanted = dwNeeded;
1552 
1553             if (!pEvlr)
1554                 break;
1555 
1556             bResult =  ReadEventLogW(hEventLog, dwFlags, 0, pEvlr, dwNeeded, &dwRead, &dwNeeded);
1557 
1558             if (!bResult)
1559                 break;
1560         }
1561         else if (!bResult)
1562         {
1563             /* exit on other errors (ERROR_HANDLE_EOF) */
1564             break;
1565         }
1566 
1567         pEvlrBuffer = (LPBYTE)pEvlr;
1568         pEvlrEnd = pEvlrBuffer + dwRead;
1569 
1570         while (pEvlrBuffer < pEvlrEnd)
1571         {
1572             PEVENTLOGRECORD pEvlrTmp = (PEVENTLOGRECORD)pEvlrBuffer;
1573             PWSTR lpszUsername, lpszCategoryName;
1574             g_RecordPtrs[dwCurrentRecord] = NULL;
1575 
1576             // ProgressBar_StepIt(hwndStatusProgress);
1577             uStep++;
1578             if (uStep % uStepAt == 0)
1579             {
1580                 ++uPos;
1581                 ProgressBar_SetPos(hwndStatusProgress, uPos);
1582             }
1583 
1584            if (WaitForSingleObject(hStopEnumEvent, 0) == WAIT_OBJECT_0)
1585                 goto Quit;
1586 
1587             /* Filter by event type */
1588             if (!FilterByType(EventLogFilter, pEvlrTmp))
1589                 goto SkipEvent;
1590 
1591             /* Get the event source name and filter it */
1592             lpszSourceName = (LPWSTR)(pEvlrBuffer + sizeof(EVENTLOGRECORD));
1593             if (!FilterByString(EventLogFilter->Sources, lpszSourceName))
1594                 goto SkipEvent;
1595 
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))
1599                 goto SkipEvent;
1600 
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));
1605 
1606             /* Get the username that generated the event, and filter it */
1607             lpszUsername = GetEventUserName(pEvlrTmp, &pLastSid, szUsername) ? szUsername : szNoUsername;
1608 
1609             if (!FilterByString(EventLogFilter->Users, lpszUsername))
1610                 goto SkipEvent;
1611 
1612             // TODO: Filter by event ID and category
1613             GetEventType(pEvlrTmp->EventType, szEventTypeText);
1614 
1615             lpszCategoryName = GetEventCategory(EventLog->LogName, lpszSourceName, pEvlrTmp, szCategory) ? szCategory : szNoCategory;
1616 
1617             StringCbPrintfW(szEventID, sizeof(szEventID), L"%u", (pEvlrTmp->EventID & 0xFFFF));
1618             StringCbPrintfW(szCategoryID, sizeof(szCategoryID), L"%u", pEvlrTmp->EventCategory);
1619 
1620             g_RecordPtrs[dwCurrentRecord] = HeapAlloc(hProcessHeap, 0, pEvlrTmp->Length);
1621             RtlCopyMemory(g_RecordPtrs[dwCurrentRecord], pEvlrTmp, pEvlrTmp->Length);
1622 
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;
1628 
1629             switch (pEvlrTmp->EventType)
1630             {
1631                 case EVENTLOG_SUCCESS:
1632                 case EVENTLOG_INFORMATION_TYPE:
1633                     lviEventItem.iImage = 0;
1634                     break;
1635 
1636                 case EVENTLOG_WARNING_TYPE:
1637                     lviEventItem.iImage = 1;
1638                     break;
1639 
1640                 case EVENTLOG_ERROR_TYPE:
1641                     lviEventItem.iImage = 2;
1642                     break;
1643 
1644                 case EVENTLOG_AUDIT_SUCCESS:
1645                     lviEventItem.iImage = 3;
1646                     break;
1647 
1648                 case EVENTLOG_AUDIT_FAILURE:
1649                     lviEventItem.iImage = 4;
1650                     break;
1651             }
1652 
1653             lviEventItem.iItem = ListView_InsertItem(hwndListView, &lviEventItem);
1654 
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);
1662 
1663 SkipEvent:
1664             pEvlrBuffer += pEvlrTmp->Length;
1665             dwCurrentRecord++;
1666         }
1667     }
1668 
1669 Quit:
1670 
1671     if (pEvlr)
1672         HeapFree(hProcessHeap, 0, pEvlr);
1673 
1674     /* Close the event log */
1675     CloseEventLog(hEventLog);
1676 
1677     } // end-for (LogIndex)
1678 
1679     /* All events loaded */
1680 
1681 Cleanup:
1682 
1683     ShowWindow(hwndStatusProgress, SW_HIDE);
1684     SendMessageW(hwndListView, LVM_PROGRESS, 0, FALSE);
1685 
1686     // FIXME: Use something else instead of EventLog->LogName !!
1687 
1688     /*
1689      * Use a different formatting, whether the event log filter holds
1690      * only one log, or many logs (the latter case is WIP TODO!)
1691      */
1692     if (EventLogFilter->NumOfEventLogs <= 1)
1693     {
1694         StringCchPrintfExW(szWindowTitle,
1695                            ARRAYSIZE(szWindowTitle),
1696                            &lpTitleTemplateEnd,
1697                            &cchRemaining,
1698                            0,
1699                            szTitleTemplate, szTitle, EventLog->LogName); /* i = number of characters written */
1700         dwMaxLength = (DWORD)cchRemaining;
1701         if (!lpMachineName)
1702             GetComputerNameW(lpTitleTemplateEnd, &dwMaxLength);
1703         else
1704             StringCchCopyW(lpTitleTemplateEnd, dwMaxLength, lpMachineName);
1705 
1706         StringCbPrintfW(szStatusText,
1707                         sizeof(szStatusText),
1708                         szStatusBarTemplate,
1709                         EventLog->LogName,
1710                         dwTotalRecords,
1711                         ListView_GetItemCount(hwndListView));
1712     }
1713     else
1714     {
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!!",
1719                     L"Event Log",
1720                     MB_OK | MB_ICONINFORMATION);
1721     }
1722 
1723     /* Update the status bar */
1724     StatusBar_SetText(hwndStatus, 0, szStatusText);
1725 
1726     /* Set the window title */
1727     SetWindowTextW(hwndMainWindow, szWindowTitle);
1728 
1729     /* Resume list view redraw */
1730     SendMessageW(hwndListView, WM_SETREDRAW, TRUE, 0);
1731 
1732     EventLogFilter_Release(EventLogFilter);
1733 
1734     CloseHandle(hStopEnumEvent);
1735     InterlockedExchangePointer((PVOID*)&hStopEnumEvent, NULL);
1736 
1737     return 0;
1738 }
1739 
1740 /*
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.
1744  */
1745 static DWORD WINAPI
1746 StartStopEnumEventsThread(IN LPVOID lpParameter)
1747 {
1748     HANDLE WaitHandles[2];
1749     DWORD  WaitResult;
1750 
1751     WaitHandles[0] = hStartStopEnumEvent; // End-of-application event
1752     WaitHandles[1] = hStartEnumEvent;     // Command event
1753 
1754     while (TRUE)
1755     {
1756         WaitResult = WaitForMultipleObjects(ARRAYSIZE(WaitHandles),
1757                                             WaitHandles,
1758                                             FALSE, // WaitAny
1759                                             INFINITE);
1760         switch (WaitResult)
1761         {
1762             case WAIT_OBJECT_0 + 0:
1763             {
1764                 /* End-of-application event signaled, quit this thread */
1765 
1766                 /* Stop the previous enumeration */
1767                 if (hEnumEventsThread)
1768                 {
1769                     if (hStopEnumEvent)
1770                     {
1771                         SetEvent(hStopEnumEvent);
1772                         WaitForSingleObject(hEnumEventsThread, INFINITE);
1773                         // NOTE: The following is done by the enumeration thread just before terminating.
1774                         // hStopEnumEvent = NULL;
1775                     }
1776 
1777                     CloseHandle(hEnumEventsThread);
1778                     hEnumEventsThread = NULL;
1779                 }
1780 
1781                 /* Clear the list view and free the cached records */
1782                 ListView_DeleteAllItems(hwndListView);
1783                 FreeRecords();
1784 
1785                 /* Reset the active filter */
1786                 ActiveFilter = NULL;
1787 
1788                 return 0;
1789             }
1790 
1791             case WAIT_OBJECT_0 + 1:
1792             {
1793                 /* Restart a new enumeration if needed */
1794                 PEVENTLOGFILTER EventLogFilter;
1795 
1796                 /* Stop the previous enumeration */
1797                 if (hEnumEventsThread)
1798                 {
1799                     if (hStopEnumEvent)
1800                     {
1801                         SetEvent(hStopEnumEvent);
1802                         WaitForSingleObject(hEnumEventsThread, INFINITE);
1803                         // NOTE: The following is done by the enumeration thread just before terminating.
1804                         // hStopEnumEvent = NULL;
1805                     }
1806 
1807                     CloseHandle(hEnumEventsThread);
1808                     hEnumEventsThread = NULL;
1809                 }
1810 
1811                 /* Clear the list view and free the cached records */
1812                 ListView_DeleteAllItems(hwndListView);
1813                 FreeRecords();
1814 
1815                 /* Reset the active filter */
1816                 ActiveFilter = NULL;
1817 
1818                 EventLogFilter = InterlockedExchangePointer((PVOID*)&EnumFilter, NULL);
1819                 if (!EventLogFilter)
1820                     break;
1821 
1822                 // Manual-reset event
1823                 hStopEnumEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
1824                 if (!hStopEnumEvent)
1825                     break;
1826 
1827                 hEnumEventsThread = CreateThread(NULL,
1828                                                  0,
1829                                                  EnumEventsThread,
1830                                                  (LPVOID)EventLogFilter,
1831                                                  CREATE_SUSPENDED,
1832                                                  NULL);
1833                 if (!hEnumEventsThread)
1834                 {
1835                     CloseHandle(hStopEnumEvent);
1836                     hStopEnumEvent = NULL;
1837                     break;
1838                 }
1839                 // CloseHandle(hEnumEventsThread);
1840                 ResumeThread(hEnumEventsThread);
1841 
1842                 break;
1843             }
1844 
1845             default:
1846             {
1847                 /* Unknown command, must never go there! */
1848                 return GetLastError();
1849             }
1850         }
1851     }
1852 
1853     return 0;
1854 }
1855 
1856 VOID
1857 EnumEvents(IN PEVENTLOGFILTER EventLogFilter)
1858 {
1859     /* Signal the enumerator thread we want to enumerate events */
1860     InterlockedExchangePointer((PVOID*)&EnumFilter, EventLogFilter);
1861     SetEvent(hStartEnumEvent);
1862     return;
1863 }
1864 
1865 
1866 PEVENTLOGFILTER
1867 GetSelectedFilter(OUT HTREEITEM* phti OPTIONAL)
1868 {
1869     TVITEMEXW tvItemEx;
1870     HTREEITEM hti;
1871 
1872     if (phti)
1873         *phti = NULL;
1874 
1875     /* Get index of selected item */
1876     hti = TreeView_GetSelection(hwndTreeView);
1877     if (hti == NULL)
1878         return NULL; // No filter
1879 
1880     tvItemEx.mask = TVIF_PARAM;
1881     tvItemEx.hItem = hti;
1882 
1883     TreeView_GetItem(hwndTreeView, &tvItemEx);
1884 
1885     if (phti)
1886         *phti = tvItemEx.hItem;
1887 
1888     return (PEVENTLOGFILTER)tvItemEx.lParam;
1889 }
1890 
1891 
1892 VOID
1893 OpenUserEventLog(VOID)
1894 {
1895     PEVENTLOG EventLog;
1896     PEVENTLOGFILTER EventLogFilter;
1897     HTREEITEM hItem = NULL;
1898     WCHAR szFileName[MAX_PATH];
1899 
1900     ZeroMemory(szFileName, sizeof(szFileName));
1901 
1902     sfn.lpstrFile = szFileName;
1903     sfn.nMaxFile  = ARRAYSIZE(szFileName);
1904 
1905     if (!GetOpenFileNameW(&sfn))
1906         return;
1907     sfn.lpstrFile[sfn.nMaxFile-1] = UNICODE_NULL;
1908 
1909     /* Allocate a new event log entry */
1910     EventLog = AllocEventLog(NULL, sfn.lpstrFile, FALSE);
1911     if (EventLog == NULL)
1912         return;
1913 
1914     /* Allocate a new event log filter entry for this event log */
1915     EventLogFilter = AllocEventLogFilter(// LogName,
1916                                          TRUE, TRUE, TRUE, TRUE, TRUE,
1917                                          NULL, NULL, NULL,
1918                                          1, &EventLog);
1919     if (EventLogFilter == NULL)
1920     {
1921         HeapFree(GetProcessHeap(), 0, EventLog);
1922         return;
1923     }
1924 
1925     /* Add the event log and the filter into their lists */
1926     InsertTailList(&EventLogList, &EventLog->ListEntry);
1927     InsertTailList(&EventLogFilterList, &EventLogFilter->ListEntry);
1928 
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);
1933 
1934     hItem = TreeViewAddItem(hwndTreeView, htiUserLogs,
1935                             szFileName,
1936                             2, 3, (LPARAM)EventLogFilter);
1937 
1938     /* Select the event log */
1939     if (hItem)
1940     {
1941         // TreeView_Expand(hwndTreeView, htiUserLogs, TVE_EXPAND);
1942         TreeView_SelectItem(hwndTreeView, hItem);
1943         TreeView_EnsureVisible(hwndTreeView, hItem);
1944     }
1945     SetFocus(hwndTreeView);
1946 }
1947 
1948 VOID
1949 SaveEventLog(IN PEVENTLOGFILTER EventLogFilter)
1950 {
1951     PEVENTLOG EventLog;
1952     HANDLE hEventLog;
1953     WCHAR szFileName[MAX_PATH];
1954 
1955     /* Bail out if there is no available filter */
1956     if (!EventLogFilter)
1957         return;
1958 
1959     ZeroMemory(szFileName, sizeof(szFileName));
1960 
1961     sfn.lpstrFile = szFileName;
1962     sfn.nMaxFile  = ARRAYSIZE(szFileName);
1963 
1964     if (!GetSaveFileNameW(&sfn))
1965         return;
1966 
1967     EventLogFilter_AddRef(EventLogFilter);
1968 
1969     EventLog = EventLogFilter->EventLogs[0];
1970     hEventLog = OpenEventLogW(EventLog->ComputerName, EventLog->LogName);
1971 
1972     EventLogFilter_Release(EventLogFilter);
1973 
1974     if (!hEventLog)
1975     {
1976         ShowLastWin32Error();
1977         return;
1978     }
1979 
1980     if (!BackupEventLogW(hEventLog, szFileName))
1981         ShowLastWin32Error();
1982 
1983     CloseEventLog(hEventLog);
1984 }
1985 
1986 VOID
1987 CloseUserEventLog(IN PEVENTLOGFILTER EventLogFilter, IN HTREEITEM hti)
1988 {
1989     /* Bail out if there is no available filter */
1990     if (!EventLogFilter)
1991         return;
1992 
1993     if (InterlockedCompareExchangePointer((PVOID*)&ActiveFilter, NULL, NULL) == EventLogFilter)
1994     {
1995         /* Signal the enumerator thread we want to stop enumerating events */
1996         // EnumEvents(NULL);
1997         InterlockedExchangePointer((PVOID*)&EnumFilter, NULL);
1998         SetEvent(hStartEnumEvent);
1999     }
2000 
2001     /*
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.
2005      */
2006     TreeView_DeleteItem(hwndTreeView, hti);
2007 
2008     /* Remove the filter from the list */
2009     RemoveEntryList(&EventLogFilter->ListEntry);
2010     EventLogFilter_Release(EventLogFilter);
2011 
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);
2017 }
2018 
2019 
2020 BOOL
2021 ClearEvents(IN PEVENTLOGFILTER EventLogFilter)
2022 {
2023     BOOL Success;
2024     PEVENTLOG EventLog;
2025     HANDLE hEventLog;
2026     WCHAR szFileName[MAX_PATH];
2027     WCHAR szMessage[MAX_LOADSTRING];
2028 
2029     /* Bail out if there is no available filter */
2030     if (!EventLogFilter)
2031         return FALSE;
2032 
2033     ZeroMemory(szFileName, sizeof(szFileName));
2034     ZeroMemory(szMessage, sizeof(szMessage));
2035 
2036     LoadStringW(hInst, IDS_CLEAREVENTS_MSG, szMessage, ARRAYSIZE(szMessage));
2037 
2038     sfn.lpstrFile = szFileName;
2039     sfn.nMaxFile  = ARRAYSIZE(szFileName);
2040 
2041     switch (MessageBoxW(hwndMainWindow, szMessage, szTitle, MB_YESNOCANCEL | MB_ICONINFORMATION))
2042     {
2043         case IDCANCEL:
2044             return FALSE;
2045 
2046         case IDNO:
2047             sfn.lpstrFile = NULL;
2048             break;
2049 
2050         case IDYES:
2051             if (!GetSaveFileNameW(&sfn))
2052                 return FALSE;
2053             break;
2054     }
2055 
2056     EventLogFilter_AddRef(EventLogFilter);
2057 
2058     EventLog = EventLogFilter->EventLogs[0];
2059     hEventLog = OpenEventLogW(EventLog->ComputerName, EventLog->LogName);
2060 
2061     EventLogFilter_Release(EventLogFilter);
2062 
2063     if (!hEventLog)
2064     {
2065         ShowLastWin32Error();
2066         return FALSE;
2067     }
2068 
2069     Success = ClearEventLogW(hEventLog, sfn.lpstrFile);
2070     if (!Success)
2071         ShowLastWin32Error();
2072 
2073     CloseEventLog(hEventLog);
2074     return Success;
2075 }
2076 
2077 
2078 VOID
2079 Refresh(IN PEVENTLOGFILTER EventLogFilter)
2080 {
2081     /* Bail out if there is no available filter */
2082     if (!EventLogFilter)
2083         return;
2084 
2085     /* Reenumerate the events through the filter */
2086     EnumEvents(EventLogFilter);
2087 }
2088 
2089 
2090 ATOM
2091 MyRegisterClass(HINSTANCE hInstance)
2092 {
2093     WNDCLASSEXW wcex;
2094 
2095     wcex.cbSize = sizeof(wcex);
2096     wcex.style = 0;
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),
2108                                      IMAGE_ICON,
2109                                      16,
2110                                      16,
2111                                      LR_SHARED);
2112 
2113     return RegisterClassExW(&wcex);
2114 }
2115 
2116 
2117 BOOL
2118 GetDisplayNameFileAndID(IN LPCWSTR lpLogName,
2119                         OUT PWCHAR lpModuleName, // TODO: Add IN DWORD BufLen
2120                         OUT PDWORD pdwMessageID)
2121 {
2122     BOOL Success = FALSE;
2123     LONG Result;
2124     HKEY hLogKey;
2125     WCHAR *KeyPath;
2126     SIZE_T cbKeyPath;
2127     DWORD Type, cbData;
2128     DWORD dwMessageID = 0;
2129     WCHAR szModuleName[MAX_PATH];
2130 
2131     /* Use a default value for the message ID */
2132     *pdwMessageID = 0;
2133 
2134     cbKeyPath = (wcslen(EVENTLOG_BASE_KEY) + wcslen(lpLogName) + 1) * sizeof(WCHAR);
2135     KeyPath = HeapAlloc(GetProcessHeap(), 0, cbKeyPath);
2136     if (!KeyPath)
2137         return FALSE;
2138 
2139     StringCbCopyW(KeyPath, cbKeyPath, EVENTLOG_BASE_KEY);
2140     StringCbCatW(KeyPath, cbKeyPath, lpLogName);
2141 
2142     Result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, KeyPath, 0, KEY_QUERY_VALUE, &hLogKey);
2143     HeapFree(GetProcessHeap(), 0, KeyPath);
2144     if (Result != ERROR_SUCCESS)
2145         return FALSE;
2146 
2147     cbData = sizeof(szModuleName);
2148     Result = RegQueryValueExW(hLogKey,
2149                               L"DisplayNameFile",
2150                               NULL,
2151                               &Type,
2152                               (LPBYTE)szModuleName,
2153                               &cbData);
2154     if ((Result != ERROR_SUCCESS) || (Type != REG_EXPAND_SZ && Type != REG_SZ))
2155     {
2156         szModuleName[0] = UNICODE_NULL;
2157     }
2158     else
2159     {
2160         /* NULL-terminate the string and expand it */
2161         szModuleName[cbData / sizeof(WCHAR) - 1] = UNICODE_NULL;
2162         ExpandEnvironmentStringsW(szModuleName, lpModuleName, ARRAYSIZE(szModuleName));
2163         Success = TRUE;
2164     }
2165 
2166     /*
2167      * If we have a 'DisplayNameFile', query for 'DisplayNameID';
2168      * otherwise it's not really useful. 'DisplayNameID' is optional.
2169      */
2170     if (Success)
2171     {
2172         cbData = sizeof(dwMessageID);
2173         Result = RegQueryValueExW(hLogKey,
2174                                   L"DisplayNameID",
2175                                   NULL,
2176                                   &Type,
2177                                   (LPBYTE)&dwMessageID,
2178                                   &cbData);
2179         if ((Result != ERROR_SUCCESS) || (Type != REG_DWORD))
2180             dwMessageID = 0;
2181 
2182         *pdwMessageID = dwMessageID;
2183     }
2184 
2185     RegCloseKey(hLogKey);
2186 
2187     return Success;
2188 }
2189 
2190 
2191 VOID
2192 BuildLogListAndFilterList(IN LPCWSTR lpComputerName)
2193 {
2194     LONG Result;
2195     HKEY hEventLogKey, hLogKey;
2196     DWORD dwNumLogs = 0;
2197     DWORD dwIndex, dwMaxKeyLength;
2198     DWORD Type;
2199     PEVENTLOG EventLog;
2200     PEVENTLOGFILTER EventLogFilter;
2201     LPWSTR LogName = NULL;
2202     WCHAR szModuleName[MAX_PATH];
2203     DWORD lpcName;
2204     DWORD dwMessageID;
2205     LPWSTR lpDisplayName;
2206     HTREEITEM hRootNode = NULL, hItem = NULL, hItemDefault = NULL;
2207 
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)
2212     {
2213         return;
2214     }
2215 
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)
2220     {
2221         goto Quit;
2222     }
2223     if (!dwNumLogs)
2224         goto Quit;
2225 
2226     /* Take the NULL terminator into account */
2227     ++dwMaxKeyLength;
2228 
2229     /* Allocate the temporary buffer */
2230     LogName = HeapAlloc(GetProcessHeap(), 0, dwMaxKeyLength * sizeof(WCHAR));
2231     if (!LogName)
2232         goto Quit;
2233 
2234     /* Enumerate and retrieve each event log name */
2235     for (dwIndex = 0; dwIndex < dwNumLogs; dwIndex++)
2236     {
2237         lpcName = dwMaxKeyLength;
2238         Result = RegEnumKeyExW(hEventLogKey, dwIndex, LogName, &lpcName, NULL, NULL, NULL, NULL);
2239         if (Result != ERROR_SUCCESS)
2240             continue;
2241 
2242         /* Take the NULL terminator into account */
2243         ++lpcName;
2244 
2245         /* Allocate a new event log entry */
2246         EventLog = AllocEventLog(lpComputerName, LogName, TRUE);
2247         if (EventLog == NULL)
2248             continue;
2249 
2250         /* Allocate a new event log filter entry for this event log */
2251         EventLogFilter = AllocEventLogFilter(// LogName,
2252                                              TRUE, TRUE, TRUE, TRUE, TRUE,
2253                                              NULL, NULL, NULL,
2254                                              1, &EventLog);
2255         if (EventLogFilter == NULL)
2256         {
2257             HeapFree(GetProcessHeap(), 0, EventLog);
2258             continue;
2259         }
2260 
2261         /* Add the event log and the filter into their lists */
2262         InsertTailList(&EventLogList, &EventLog->ListEntry);
2263         InsertTailList(&EventLogFilterList, &EventLogFilter->ListEntry);
2264 
2265         EventLog->FileName = NULL;
2266 
2267         /* Retrieve and cache the event log file */
2268         Result = RegOpenKeyExW(hEventLogKey,
2269                                LogName,
2270                                0,
2271                                KEY_QUERY_VALUE,
2272                                &hLogKey);
2273         if (Result == ERROR_SUCCESS)
2274         {
2275             lpcName = 0;
2276             Result = RegQueryValueExW(hLogKey,
2277                                       L"File",
2278                                       NULL,
2279                                       &Type,
2280                                       NULL,
2281                                       &lpcName);
2282             if ((Result != ERROR_SUCCESS) || (Type != REG_EXPAND_SZ && Type != REG_SZ))
2283             {
2284                 // Windows' EventLog uses some kind of default value, we do not.
2285                 EventLog->FileName = NULL;
2286             }
2287             else
2288             {
2289                 lpcName = ROUND_DOWN(lpcName, sizeof(WCHAR));
2290                 EventLog->FileName = HeapAlloc(GetProcessHeap(), 0, lpcName);
2291                 if (EventLog->FileName)
2292                 {
2293                     Result = RegQueryValueExW(hLogKey,
2294                                               L"File",
2295                                               NULL,
2296                                               &Type,
2297                                               (LPBYTE)EventLog->FileName,
2298                                               &lpcName);
2299                     if (Result != ERROR_SUCCESS)
2300                     {
2301                         HeapFree(GetProcessHeap(), 0, EventLog->FileName);
2302                         EventLog->FileName = NULL;
2303                     }
2304                     else
2305                     {
2306                         EventLog->FileName[lpcName / sizeof(WCHAR) - 1] = UNICODE_NULL;
2307                     }
2308                 }
2309             }
2310 
2311             RegCloseKey(hLogKey);
2312         }
2313 
2314         /* Get the display name for the event log */
2315         lpDisplayName = NULL;
2316 
2317         ZeroMemory(szModuleName, sizeof(szModuleName));
2318         if (GetDisplayNameFileAndID(LogName, szModuleName, &dwMessageID))
2319         {
2320             /* Retrieve the message string without appending extra newlines */
2321             lpDisplayName =
2322             GetMessageStringFromDll(szModuleName,
2323                                     FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
2324                                     FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
2325                                     dwMessageID,
2326                                     0,
2327                                     NULL);
2328         }
2329 
2330         /*
2331          * Select the correct tree root node, whether the log is a System
2332          * or an Application log. Default to Application log otherwise.
2333          */
2334         hRootNode = htiAppLogs;
2335         for (lpcName = 0; lpcName < ARRAYSIZE(SystemLogs); ++lpcName)
2336         {
2337             /* Check whether the log name is part of the system logs */
2338             if (wcsicmp(LogName, SystemLogs[lpcName]) == 0)
2339             {
2340                 hRootNode = htiSystemLogs;
2341                 break;
2342             }
2343         }
2344 
2345         hItem = TreeViewAddItem(hwndTreeView, hRootNode,
2346                                 (lpDisplayName ? lpDisplayName : LogName),
2347                                 2, 3, (LPARAM)EventLogFilter);
2348 
2349         /* Try to get the default event log: "Application" */
2350         if ((hItemDefault == NULL) && (wcsicmp(LogName, SystemLogs[0]) == 0))
2351         {
2352             hItemDefault = hItem;
2353         }
2354 
2355         /* Free the buffer allocated by FormatMessage */
2356         if (lpDisplayName)
2357             LocalFree(lpDisplayName);
2358     }
2359 
2360     HeapFree(GetProcessHeap(), 0, LogName);
2361 
2362 Quit:
2363     RegCloseKey(hEventLogKey);
2364 
2365     /* Select the default event log */
2366     if (hItemDefault)
2367     {
2368         // TreeView_Expand(hwndTreeView, hRootNode, TVE_EXPAND);
2369         TreeView_SelectItem(hwndTreeView, hItemDefault);
2370         TreeView_EnsureVisible(hwndTreeView, hItemDefault);
2371     }
2372     SetFocus(hwndTreeView);
2373 
2374     return;
2375 }
2376 
2377 VOID
2378 FreeLogList(VOID)
2379 {
2380     PLIST_ENTRY Entry;
2381     PEVENTLOG EventLog;
2382 
2383     while (!IsListEmpty(&EventLogList))
2384     {
2385         Entry = RemoveHeadList(&EventLogList);
2386         EventLog = (PEVENTLOG)CONTAINING_RECORD(Entry, EVENTLOG, ListEntry);
2387         EventLog_Free(EventLog);
2388     }
2389 
2390     return;
2391 }
2392 
2393 VOID
2394 FreeLogFilterList(VOID)
2395 {
2396     PLIST_ENTRY Entry;
2397     PEVENTLOGFILTER EventLogFilter;
2398 
2399     while (!IsListEmpty(&EventLogFilterList))
2400     {
2401         Entry = RemoveHeadList(&EventLogFilterList);
2402         EventLogFilter = (PEVENTLOGFILTER)CONTAINING_RECORD(Entry, EVENTLOGFILTER, ListEntry);
2403         EventLogFilter_Free(EventLogFilter);
2404     }
2405 
2406     ActiveFilter = NULL;
2407 
2408     return;
2409 }
2410 
2411 BOOL
2412 InitInstance(HINSTANCE hInstance,
2413              int nCmdShow)
2414 {
2415     RECT rcClient, rs;
2416     LONG StatusHeight;
2417     HIMAGELIST hSmall;
2418     LVCOLUMNW lvc = {0};
2419     WCHAR szTemp[256];
2420 
2421     hInst = hInstance; // Store instance handle in our global variable
2422 
2423     /* Create the main window */
2424     hwndMainWindow = CreateWindowW(szWindowClass,
2425                                    szTitle,
2426                                    WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
2427                                    CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
2428                                    NULL,
2429                                    NULL,
2430                                    hInstance,
2431                                    NULL);
2432     if (!hwndMainWindow)
2433         return FALSE;
2434 
2435     /* Create the status bar */
2436     hwndStatus = CreateWindowExW(0,                                  // no extended styles
2437                                  STATUSCLASSNAMEW,                   // status bar
2438                                  L"",                                // no text
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
2445 
2446     GetClientRect(hwndMainWindow, &rcClient);
2447     GetWindowRect(hwndStatus, &rs);
2448     StatusHeight = rs.bottom - rs.top;
2449 
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
2454                                          NULL,                       // no text
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
2459                                          NULL,                       // window ID
2460                                          hInstance,                  // instance
2461                                          NULL);                      // window data
2462     ProgressBar_SetStep(hwndStatusProgress, 1);
2463 
2464     /* Initialize the splitter default positions */
2465     nVSplitPos = 250;
2466     nHSplitPos = 250;
2467 
2468     /* Create the TreeView */
2469     hwndTreeView = CreateWindowExW(WS_EX_CLIENTEDGE,
2470                                    WC_TREEVIEWW,
2471                                    NULL,
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,
2474                                    0, 0,
2475                                    nVSplitPos - SPLIT_WIDTH/2,
2476                                    (rcClient.bottom - rcClient.top) - StatusHeight,
2477                                    hwndMainWindow,
2478                                    NULL,
2479                                    hInstance,
2480                                    NULL);
2481 
2482     /* Create the ImageList */
2483     hSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
2484                               GetSystemMetrics(SM_CYSMICON),
2485                               ILC_COLOR32 | ILC_MASK, // ILC_COLOR24
2486                               1, 1);
2487 
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)));
2493 
2494     /* Assign the ImageList to the Tree View */
2495     TreeView_SetImageList(hwndTreeView, hSmall, TVSIL_NORMAL);
2496 
2497     /* Add the event logs nodes */
2498     // "System Logs"
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);
2504     // "User Logs"
2505     LoadStringW(hInstance, IDS_EVENTLOG_USER, szTemp, ARRAYSIZE(szTemp));
2506     htiUserLogs = TreeViewAddItem(hwndTreeView, NULL, szTemp, 0, 1, (LPARAM)NULL);
2507 
2508     /* Create the Event details pane (optional) */
2509     hwndEventDetails = CreateEventDetailsCtrl(hInst, hwndMainWindow, (LPARAM)NULL);
2510     // hwndEventDetails = NULL;
2511     if (hwndEventDetails)
2512     {
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);
2523     }
2524 
2525     /* Create the ListView */
2526     hwndListView = CreateWindowExW(WS_EX_CLIENTEDGE,
2527                                    WC_LISTVIEWW,
2528                                    NULL,
2529                                    WS_CHILD | WS_VISIBLE | LVS_SHOWSELALWAYS | LVS_REPORT,
2530                                    nVSplitPos + SPLIT_WIDTH/2,
2531                                    0,
2532                                    (rcClient.right - rcClient.left) - nVSplitPos - SPLIT_WIDTH/2,
2533                                    hwndEventDetails ? nHSplitPos - SPLIT_WIDTH/2
2534                                                     : (rcClient.bottom - rcClient.top) - StatusHeight,
2535                                    hwndMainWindow,
2536                                    NULL,
2537                                    hInstance,
2538                                    NULL);
2539 
2540     /* Add the extended ListView styles */
2541     ListView_SetExtendedListViewStyle(hwndListView, LVS_EX_HEADERDRAGDROP | LVS_EX_FULLROWSELECT |LVS_EX_LABELTIP);
2542 
2543     /* Create the ImageList */
2544     hSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
2545                               GetSystemMetrics(SM_CYSMICON),
2546                               ILC_COLOR32 | ILC_MASK, // ILC_COLOR24
2547                               1, 1);
2548 
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)));
2555 
2556     /* Assign the ImageList to the List View */
2557     ListView_SetImageList(hwndListView, hSmall, LVSIL_SMALL);
2558 
2559     /* Now set up the listview with its columns */
2560     lvc.mask = LVCF_TEXT | LVCF_WIDTH;
2561     lvc.cx = 90;
2562     LoadStringW(hInstance,
2563                 IDS_COLUMNTYPE,
2564                 szTemp,
2565                 ARRAYSIZE(szTemp));
2566     lvc.pszText = szTemp;
2567     ListView_InsertColumn(hwndListView, 0, &lvc);
2568 
2569     lvc.cx = 70;
2570     LoadStringW(hInstance,
2571                 IDS_COLUMNDATE,
2572                 szTemp,
2573                 ARRAYSIZE(szTemp));
2574     lvc.pszText = szTemp;
2575     ListView_InsertColumn(hwndListView, 1, &lvc);
2576 
2577     lvc.cx = 70;
2578     LoadStringW(hInstance,
2579                 IDS_COLUMNTIME,
2580                 szTemp,
2581                 ARRAYSIZE(szTemp));
2582     lvc.pszText = szTemp;
2583     ListView_InsertColumn(hwndListView, 2, &lvc);
2584 
2585     lvc.cx = 150;
2586     LoadStringW(hInstance,
2587                 IDS_COLUMNSOURCE,
2588                 szTemp,
2589                 ARRAYSIZE(szTemp));
2590     lvc.pszText = szTemp;
2591     ListView_InsertColumn(hwndListView, 3, &lvc);
2592 
2593     lvc.cx = 100;
2594     LoadStringW(hInstance,
2595                 IDS_COLUMNCATEGORY,
2596                 szTemp,
2597                 ARRAYSIZE(szTemp));
2598     lvc.pszText = szTemp;
2599     ListView_InsertColumn(hwndListView, 4, &lvc);
2600 
2601     lvc.cx = 60;
2602     LoadStringW(hInstance,
2603                 IDS_COLUMNEVENT,
2604                 szTemp,
2605                 ARRAYSIZE(szTemp));
2606     lvc.pszText = szTemp;
2607     ListView_InsertColumn(hwndListView, 5, &lvc);
2608 
2609     lvc.cx = 120;
2610     LoadStringW(hInstance,
2611                 IDS_COLUMNUSER,
2612                 szTemp,
2613                 ARRAYSIZE(szTemp));
2614     lvc.pszText = szTemp;
2615     ListView_InsertColumn(hwndListView, 6, &lvc);
2616 
2617     lvc.cx = 100;
2618     LoadStringW(hInstance,
2619                 IDS_COLUMNCOMPUTER,
2620                 szTemp,
2621                 ARRAYSIZE(szTemp));
2622     lvc.pszText = szTemp;
2623     ListView_InsertColumn(hwndListView, 7, &lvc);
2624 
2625     /* Initialize the save Dialog */
2626     ZeroMemory(&sfn, sizeof(sfn));
2627     ZeroMemory(szSaveFilter, sizeof(szSaveFilter));
2628 
2629     LoadStringW(hInst, IDS_SAVE_FILTER, szSaveFilter, ARRAYSIZE(szSaveFilter));
2630 
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;
2638 
2639     ShowWindow(hwndMainWindow, nCmdShow);
2640     UpdateWindow(hwndMainWindow);
2641 
2642     return TRUE;
2643 }
2644 
2645 VOID ResizeWnd(INT cx, INT cy)
2646 {
2647     HDWP hdwp;
2648     RECT rs;
2649     LONG StatusHeight;
2650 
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;
2655 
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);
2661 
2662     /*
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.
2666      */
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!
2669 
2670     hdwp = BeginDeferWindowPos(3);
2671 
2672     if (hdwp)
2673         hdwp = DeferWindowPos(hdwp,
2674                               hwndTreeView,
2675                               HWND_TOP,
2676                               0, 0,
2677                               nVSplitPos - SPLIT_WIDTH/2,
2678                               cy - StatusHeight,
2679                               SWP_NOZORDER | SWP_NOACTIVATE);
2680 
2681     if (hdwp)
2682         hdwp = DeferWindowPos(hdwp,
2683                               hwndListView,
2684                               HWND_TOP,
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);
2690 
2691     if (hwndEventDetails && hdwp)
2692         hdwp = DeferWindowPos(hdwp,
2693                               hwndEventDetails,
2694                               HWND_TOP,
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);
2700 
2701     if (hdwp)
2702         EndDeferWindowPos(hdwp);
2703 }
2704 
2705 
2706 LRESULT CALLBACK
2707 WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
2708 {
2709     RECT rect;
2710 
2711     switch (uMsg)
2712     {
2713         case WM_CREATE:
2714             hMainMenu = GetMenu(hWnd);
2715             break;
2716 
2717         case WM_DESTROY:
2718             PostQuitMessage(0);
2719             break;
2720 
2721         case WM_NOTIFY:
2722         {
2723             LPNMHDR hdr = (LPNMHDR)lParam;
2724 
2725             if (hdr->hwndFrom == hwndListView)
2726             {
2727                 switch (hdr->code)
2728                 {
2729                     case LVN_ITEMCHANGED:
2730                     {
2731                         LPNMLISTVIEW pnmv = (LPNMLISTVIEW)lParam;
2732 
2733                         if ( (pnmv->uChanged  & LVIF_STATE) && /* The state has changed */
2734                              (pnmv->uNewState & LVIS_SELECTED) /* The item has been (de)selected */ )
2735                         {
2736                             if (hwndEventDetails)
2737                                 SendMessageW(hwndEventDetails, EVT_DISPLAY, 0, 0);
2738                         }
2739                         break;
2740                     }
2741 
2742                     case NM_DBLCLK:
2743                     case NM_RETURN:
2744                         SendMessageW(hWnd, WM_COMMAND, IDM_EVENT_DETAILS, 0);
2745                         break;
2746                 }
2747             }
2748             else if (hdr->hwndFrom == hwndTreeView)
2749             {
2750                 switch (hdr->code)
2751                 {
2752                     case TVN_BEGINLABELEDIT:
2753                     {
2754                         HTREEITEM hItem = ((LPNMTVDISPINFO)lParam)->item.hItem;
2755 
2756                         /* Disable label editing for root nodes */
2757                         return ((hItem == htiSystemLogs) ||
2758                                 (hItem == htiAppLogs)    ||
2759                                 (hItem == htiUserLogs));
2760                     }
2761 
2762                     case TVN_ENDLABELEDIT:
2763                     {
2764                         TVITEMW item = ((LPNMTVDISPINFO)lParam)->item;
2765                         HTREEITEM hItem = item.hItem;
2766 
2767                         /* Disable label editing for root nodes */
2768                         if ((hItem == htiSystemLogs) ||
2769                             (hItem == htiAppLogs)    ||
2770                             (hItem == htiUserLogs))
2771                         {
2772                             return FALSE;
2773                         }
2774 
2775                         if (item.pszText)
2776                         {
2777                             LPWSTR pszText = item.pszText;
2778 
2779                             /* Trim all whitespace */
2780                             while (*pszText && iswspace(*pszText))
2781                                 ++pszText;
2782 
2783                             if (!*pszText)
2784                                 return FALSE;
2785 
2786                             return TRUE;
2787                         }
2788                         else
2789                         {
2790                             return FALSE;
2791                         }
2792                     }
2793 
2794                     case TVN_SELCHANGED:
2795                     {
2796                         PEVENTLOGFILTER EventLogFilter =
2797                             (PEVENTLOGFILTER)((LPNMTREEVIEW)lParam)->itemNew.lParam;
2798 
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???
2802 
2803                         // FIXME: This is a hack!!
2804                         if (hwndEventDetails && EventLogFilter)
2805                         {
2806                             SendMessageW(hwndEventDetails, EVT_SETFILTER, 0, (LPARAM)EventLogFilter);
2807                         }
2808 
2809                         if (EventLogFilter)
2810                         {
2811                             /*
2812                              * If we have selected a filter, enable the menu commands;
2813                              * they will possibly be updated after events enumeration.
2814                              */
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);
2820                         }
2821                         else
2822                         {
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);
2828                         }
2829 
2830                         /*
2831                          * The enumeration thread that is triggered by EnumEvents
2832                          * will set a new value for the 'ActiveFilter'.
2833                          */
2834                         if (EventLogFilter)
2835                             EnumEvents(EventLogFilter);
2836 
2837                         break;
2838                     }
2839                 }
2840             }
2841             break;
2842         }
2843 
2844         case WM_COMMAND:
2845         {
2846             /* Parse the menu selections */
2847             switch (LOWORD(wParam))
2848             {
2849                 case IDM_OPEN_EVENTLOG:
2850                     OpenUserEventLog();
2851                     break;
2852 
2853                 case IDM_SAVE_EVENTLOG:
2854                     SaveEventLog(GetSelectedFilter(NULL));
2855                     break;
2856 
2857                 case IDM_CLOSE_EVENTLOG:
2858                 {
2859                     HTREEITEM hti;
2860                     PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(&hti);
2861                     CloseUserEventLog(EventLogFilter, hti);
2862                     break;
2863                 }
2864 
2865                 case IDM_CLEAR_EVENTS:
2866                 {
2867                     PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(NULL);
2868                     if (EventLogFilter && ClearEvents(EventLogFilter))
2869                         Refresh(EventLogFilter);
2870                     break;
2871                 }
2872 
2873                 case IDM_RENAME_EVENTLOG:
2874                     if (GetFocus() == hwndTreeView)
2875                         TreeView_EditLabel(hwndTreeView, TreeView_GetSelection(hwndTreeView));
2876                     break;
2877 
2878                 case IDM_EVENTLOG_SETTINGS:
2879                 {
2880                     PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(NULL);
2881                     // TODO: Check the returned value?
2882                     if (EventLogFilter)
2883                         EventLogProperties(hInst, hWnd, EventLogFilter);
2884                     break;
2885                 }
2886 
2887                 case IDM_LIST_NEWEST:
2888                 {
2889                     CheckMenuRadioItem(hMainMenu, IDM_LIST_NEWEST, IDM_LIST_OLDEST, IDM_LIST_NEWEST, MF_BYCOMMAND);
2890                     if (!NewestEventsFirst)
2891                     {
2892                         NewestEventsFirst = TRUE;
2893                         Refresh(GetSelectedFilter(NULL));
2894                     }
2895                     break;
2896                 }
2897 
2898                 case IDM_LIST_OLDEST:
2899                 {
2900                     CheckMenuRadioItem(hMainMenu, IDM_LIST_NEWEST, IDM_LIST_OLDEST, IDM_LIST_OLDEST, MF_BYCOMMAND);
2901                     if (NewestEventsFirst)
2902                     {
2903                         NewestEventsFirst = FALSE;
2904                         Refresh(GetSelectedFilter(NULL));
2905                     }
2906                     break;
2907                 }
2908 
2909                 case IDM_EVENT_DETAILS:
2910                 {
2911                     // LPNMITEMACTIVATE lpnmitem = (LPNMITEMACTIVATE)lParam;
2912                     PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(NULL);
2913                     if (/*lpnmitem->iItem != -1 &&*/ EventLogFilter)
2914                     {
2915                         EventLogFilter_AddRef(EventLogFilter);
2916                         DialogBoxParamW(hInst,
2917                                         MAKEINTRESOURCEW(IDD_EVENTDETAILS_DLG),
2918                                         hWnd,
2919                                         EventDetails,
2920                                         (LPARAM)EventLogFilter);
2921                         EventLogFilter_Release(EventLogFilter);
2922                     }
2923                     break;
2924                 }
2925 
2926                 case IDM_REFRESH:
2927                     Refresh(GetSelectedFilter(NULL));
2928                     break;
2929 
2930                 case IDM_ABOUT:
2931                 {
2932                     HICON hIcon;
2933                     WCHAR szCopyright[MAX_LOADSTRING];
2934 
2935                     hIcon = LoadIconW(hInst, MAKEINTRESOURCEW(IDI_EVENTVWR));
2936                     LoadStringW(hInst, IDS_COPYRIGHT, szCopyright, ARRAYSIZE(szCopyright));
2937                     ShellAboutW(hWnd, szTitle, szCopyright, hIcon);
2938                     DeleteObject(hIcon);
2939                     break;
2940                 }
2941 
2942                 case IDM_HELP:
2943                     MessageBoxW(hwndMainWindow,
2944                                 L"Help not implemented yet!",
2945                                 L"Event Log",
2946                                 MB_OK | MB_ICONINFORMATION);
2947                                 break;
2948 
2949                 case IDM_EXIT:
2950                     DestroyWindow(hWnd);
2951                     break;
2952 
2953                 default:
2954                     return DefWindowProcW(hWnd, uMsg, wParam, lParam);
2955             }
2956             break;
2957         }
2958 
2959         case WM_SETCURSOR:
2960             if (LOWORD(lParam) == HTCLIENT)
2961             {
2962                 POINT pt;
2963                 GetCursorPos(&pt);
2964                 ScreenToClient(hWnd, &pt);
2965 
2966                 /* Set the cursor for the vertical splitter */
2967                 if (pt.x >= nVSplitPos - SPLIT_WIDTH/2 && pt.x < nVSplitPos + SPLIT_WIDTH/2 + 1)
2968                 {
2969                     RECT rs;
2970                     GetClientRect(hWnd, &rect);
2971                     GetWindowRect(hwndStatus, &rs);
2972                     if (pt.y >= rect.top && pt.y < rect.bottom - (rs.bottom - rs.top))
2973                     {
2974                         SetCursor(LoadCursorW(NULL, IDC_SIZEWE));
2975                         return TRUE;
2976                     }
2977                 }
2978                 else
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))
2982                 {
2983                     // RECT rs;
2984                     GetClientRect(hWnd, &rect);
2985                     // GetWindowRect(hwndStatus, &rs);
2986                     if (pt.x >= nVSplitPos + SPLIT_WIDTH/2 + 1 /* rect.left + (rs.bottom - rs.top) */ &&
2987                         pt.x < rect.right)
2988                     {
2989                         SetCursor(LoadCursorW(NULL, IDC_SIZENS));
2990                         return TRUE;
2991                     }
2992                 }
2993             }
2994             goto Default;
2995 
2996         case WM_LBUTTONDOWN:
2997         {
2998             INT x = GET_X_LPARAM(lParam);
2999             INT y = GET_Y_LPARAM(lParam);
3000 
3001             /* Reset the splitter state */
3002             bSplit = 0;
3003 
3004             /* Capture the cursor for the vertical splitter */
3005             if (x >= nVSplitPos - SPLIT_WIDTH/2 && x < nVSplitPos + SPLIT_WIDTH/2 + 1)
3006             {
3007                 bSplit = 1;
3008                 SetCapture(hWnd);
3009             }
3010             else
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))
3014             {
3015                 bSplit = 2;
3016                 SetCapture(hWnd);
3017             }
3018             break;
3019         }
3020 
3021         case WM_LBUTTONUP:
3022         case WM_RBUTTONDOWN:
3023             if (GetCapture() == hWnd)
3024             {
3025                 /* Adjust the correct splitter position */
3026                 if (bSplit == 1)
3027                     nVSplitPos = GET_X_LPARAM(lParam);
3028                 else if (bSplit == 2)
3029                     nHSplitPos = GET_Y_LPARAM(lParam);
3030 
3031                 /* If we are splitting, resize the windows */
3032                 if (bSplit != 0)
3033                 {
3034                     GetClientRect(hWnd, &rect);
3035                     ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
3036                 }
3037 
3038                 /* Reset the splitter state */
3039                 bSplit = 0;
3040 
3041                 ReleaseCapture();
3042             }
3043             break;
3044 
3045         case WM_MOUSEMOVE:
3046             if (GetCapture() == hWnd)
3047             {
3048                 /* Move the correct splitter */
3049                 if (bSplit == 1)
3050                 {
3051                     INT x = GET_X_LPARAM(lParam);
3052 
3053                     GetClientRect(hWnd, &rect);
3054 
3055                     x = min(max(x, SPLIT_WIDTH/2), rect.right - rect.left - SPLIT_WIDTH/2);
3056                     if (nVSplitPos != x)
3057                     {
3058                         nVSplitPos = x;
3059                         ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
3060                     }
3061                 }
3062                 else if (bSplit == 2)
3063                 {
3064                     RECT rs;
3065                     INT y = GET_Y_LPARAM(lParam);
3066 
3067                     GetClientRect(hWnd, &rect);
3068                     GetWindowRect(hwndStatus, &rs);
3069 
3070                     y = min(max(y, SPLIT_WIDTH/2), rect.bottom - rect.top - SPLIT_WIDTH/2 - (rs.bottom - rs.top));
3071                     if (nHSplitPos != y)
3072                     {
3073                         nHSplitPos = y;
3074                         ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
3075                     }
3076                 }
3077             }
3078             break;
3079 
3080         case WM_SIZE:
3081         {
3082             if (wParam != SIZE_MINIMIZED)
3083             {
3084                 SendMessageW(hwndStatus, WM_SIZE, 0, 0);
3085                 ResizeWnd(LOWORD(lParam), HIWORD(lParam));
3086                 break;
3087             }
3088             /* Fall through the default case */
3089         }
3090 
3091         default: Default:
3092             return DefWindowProcW(hWnd, uMsg, wParam, lParam);
3093     }
3094 
3095     return 0;
3096 }
3097 
3098 
3099 static
3100 VOID
3101 InitPropertiesDlg(HWND hDlg, PEVENTLOG EventLog)
3102 {
3103     LPWSTR lpLogName = EventLog->LogName;
3104 
3105     DWORD Result, Type;
3106     DWORD dwMaxSize = 0, dwRetention = 0;
3107     BOOL Success;
3108     WIN32_FIND_DATAW FileInfo; // WIN32_FILE_ATTRIBUTE_DATA
3109     ULARGE_INTEGER FileSize;
3110     WCHAR wszBuf[MAX_PATH];
3111     WCHAR szTemp[MAX_LOADSTRING];
3112     LPWSTR FileName;
3113 
3114     HKEY hLogKey;
3115     WCHAR *KeyPath;
3116     DWORD cbData;
3117     SIZE_T cbKeyPath;
3118 
3119     if (EventLog->Permanent)
3120     {
3121 
3122     cbKeyPath = (wcslen(EVENTLOG_BASE_KEY) + wcslen(lpLogName) + 1) * sizeof(WCHAR);
3123     KeyPath = HeapAlloc(GetProcessHeap(), 0, cbKeyPath);
3124     if (!KeyPath)
3125     {
3126         goto Quit;
3127     }
3128 
3129     StringCbCopyW(KeyPath, cbKeyPath, EVENTLOG_BASE_KEY);
3130     StringCbCatW(KeyPath, cbKeyPath, lpLogName);
3131 
3132     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, KeyPath, 0, KEY_QUERY_VALUE, &hLogKey) != ERROR_SUCCESS)
3133     {
3134         HeapFree(GetProcessHeap(), 0, KeyPath);
3135         goto Quit;
3136     }
3137     HeapFree(GetProcessHeap(), 0, KeyPath);
3138 
3139 
3140     cbData = sizeof(dwMaxSize);
3141     Result = RegQueryValueExW(hLogKey,
3142                               L"MaxSize",
3143                               NULL,
3144                               &Type,
3145                               (LPBYTE)&dwMaxSize,
3146                               &cbData);
3147     if ((Result != ERROR_SUCCESS) || (Type != REG_DWORD))
3148     {
3149         // dwMaxSize = 512 * 1024; /* 512 kBytes */
3150         dwMaxSize = 0;
3151     }
3152     /* Convert in KB */
3153     dwMaxSize /= 1024;
3154 
3155     cbData = sizeof(dwRetention);
3156     Result = RegQueryValueExW(hLogKey,
3157                               L"Retention",
3158                               NULL,
3159                               &Type,
3160                               (LPBYTE)&dwRetention,
3161                               &cbData);
3162     if ((Result != ERROR_SUCCESS) || (Type != REG_DWORD))
3163     {
3164         /* On Windows 2003 it is 604800 (secs) == 7 days */
3165         dwRetention = 0;
3166     }
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);
3170 
3171 
3172     RegCloseKey(hLogKey);
3173 
3174     }
3175 
3176 
3177 Quit:
3178 
3179     SetDlgItemTextW(hDlg, IDC_DISPLAYNAME, lpLogName); // FIXME!
3180     SetDlgItemTextW(hDlg, IDC_LOGNAME, lpLogName);
3181 
3182     FileName = EventLog->FileName;
3183     if (FileName && *FileName)
3184     {
3185         ExpandEnvironmentStringsW(FileName, wszBuf, MAX_PATH);
3186         FileName = wszBuf;
3187     }
3188     SetDlgItemTextW(hDlg, IDC_LOGFILE, FileName);
3189 
3190     /*
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.
3195      */
3196 
3197     Success = GetFileAttributesExW(FileName,
3198                                    GetFileExInfoStandard,
3199                                    (LPWIN32_FILE_ATTRIBUTE_DATA)&FileInfo);
3200     if (!Success)
3201     {
3202         HANDLE hFind = FindFirstFileW(FileName, &FileInfo);
3203         Success = (hFind != INVALID_HANDLE_VALUE);
3204         if (Success)
3205             FindClose(hFind);
3206     }
3207 
3208     // Starting there, FileName is invalid (because it uses wszBuf)
3209 
3210     if (Success)
3211     {
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);
3216 
3217         LoadStringW(hInst, IDS_NOT_AVAILABLE, szTemp, ARRAYSIZE(szTemp));
3218 
3219         if (GetFileTimeString(&FileInfo.ftCreationTime, wszBuf, ARRAYSIZE(wszBuf)))
3220             SetDlgItemTextW(hDlg, IDC_CREATED_LABEL, wszBuf);
3221         else
3222             SetDlgItemTextW(hDlg, IDC_CREATED_LABEL, szTemp);
3223 
3224         if (GetFileTimeString(&FileInfo.ftLastWriteTime, wszBuf, ARRAYSIZE(wszBuf)))
3225             SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, wszBuf);
3226         else
3227             SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, szTemp);
3228 
3229         if (GetFileTimeString(&FileInfo.ftLastAccessTime, wszBuf, ARRAYSIZE(wszBuf)))
3230             SetDlgItemTextW(hDlg, IDC_ACCESSED_LABEL, wszBuf);
3231         else
3232             SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, szTemp);
3233     }
3234     else
3235     {
3236         LoadStringW(hInst, IDS_NOT_AVAILABLE, szTemp, ARRAYSIZE(szTemp));
3237 
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);
3242     }
3243 
3244     if (EventLog->Permanent)
3245     {
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));
3248 
3249         SetDlgItemInt(hDlg, IDC_EDIT_MAXLOGSIZE, dwMaxSize, FALSE);
3250         SetDlgItemInt(hDlg, IDC_EDIT_EVENTS_AGE, dwRetention, FALSE);
3251 
3252         if (dwRetention == 0)
3253         {
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);
3257         }
3258         else if (dwRetention == INFINITE)
3259         {
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);
3263         }
3264         else
3265         {
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);
3269         }
3270     }
3271     else
3272     {
3273         // TODO: Hide the unused controls! Or, just use another type of property sheet!
3274     }
3275 }
3276 
3277 /* Message handler for EventLog Properties dialog */
3278 INT_PTR CALLBACK
3279 EventLogPropProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
3280 {
3281     PEVENTLOG EventLog;
3282 
3283     EventLog = (PEVENTLOG)GetWindowLongPtrW(hDlg, DWLP_USER);
3284 
3285     switch (uMsg)
3286     {
3287         case WM_INITDIALOG:
3288         {
3289             EventLog = (PEVENTLOG)((LPPROPSHEETPAGE)lParam)->lParam;
3290             SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)EventLog);
3291 
3292             InitPropertiesDlg(hDlg, EventLog);
3293 
3294             PropSheet_UnChanged(GetParent(hDlg), hDlg);
3295             return (INT_PTR)TRUE;
3296         }
3297 
3298         case WM_DESTROY:
3299             return (INT_PTR)TRUE;
3300 
3301         case WM_COMMAND:
3302             switch (LOWORD(wParam))
3303             {
3304                 case IDOK:
3305                 case IDCANCEL:
3306                     EndDialog(hDlg, LOWORD(wParam));
3307                     return (INT_PTR)TRUE;
3308 
3309                 case IDC_OVERWRITE_AS_NEEDED:
3310                 {
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);
3314                     break;
3315                 }
3316 
3317                 case IDC_OVERWRITE_OLDER_THAN:
3318                 {
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);
3322                     break;
3323                 }
3324 
3325                 case IDC_NO_OVERWRITE:
3326                 {
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);
3330                     break;
3331                 }
3332 
3333                 case IDHELP:
3334                     MessageBoxW(hDlg,
3335                                 L"Help not implemented yet!",
3336                                 L"Event Log",
3337                                 MB_OK | MB_ICONINFORMATION);
3338                     return (INT_PTR)TRUE;
3339 
3340                 default:
3341                     break;
3342             }
3343             break;
3344     }
3345 
3346     return (INT_PTR)FALSE;
3347 }
3348 
3349 INT_PTR
3350 EventLogProperties(HINSTANCE hInstance, HWND hWndParent, PEVENTLOGFILTER EventLogFilter)
3351 {
3352     INT_PTR ret = 0;
3353     PROPSHEETHEADERW psh;
3354     PROPSHEETPAGEW   psp[1]; // 2
3355 
3356     /*
3357      * Bail out if there is no available filter, or if the filter
3358      * contains more than one log.
3359      */
3360     if (!EventLogFilter)
3361         return 0;
3362 
3363     EventLogFilter_AddRef(EventLogFilter);
3364 
3365     if (EventLogFilter->NumOfEventLogs > 1 ||
3366         EventLogFilter->EventLogs[0] == NULL)
3367     {
3368         goto Quit;
3369     }
3370 
3371     /* Header */
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;
3378     psh.nStartPage  = 0;
3379     psh.ppsp        = psp;
3380     psh.nPages      = ARRAYSIZE(psp);
3381     // psh.pfnCallback = PropSheetCallback;
3382 
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];
3390 
3391 #if 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];
3399 #endif
3400 
3401     /* Create the property sheet */
3402     ret = PropertySheetW(&psh);
3403 
3404 Quit:
3405     EventLogFilter_Release(EventLogFilter);
3406     return ret;
3407 }
3408 
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
3414 
3415 INT_PTR CALLBACK
3416 EventDetails(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
3417 {
3418     switch (uMsg)
3419     {
3420         case WM_INITDIALOG:
3421         {
3422             LONG_PTR dwStyle;
3423             INT sbVXSize, sbHYSize;
3424             RECT rcWnd, rect;
3425 
3426             hWndDetailsCtrl = CreateEventDetailsCtrl(hInst, hDlg, lParam);
3427             if (!hWndDetailsCtrl)
3428             {
3429                 EndDialog(hDlg, 0);
3430                 return (INT_PTR)TRUE;
3431             }
3432 
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 */)
3439             {
3440                 hWndGrip = CreateWindowW(WC_SCROLLBARW,
3441                                          NULL,
3442                                          WS_CHILD | WS_VISIBLE | /**/ WS_CLIPSIBLINGS | /**/ SBS_SIZEGRIP | SBS_SIZEBOXBOTTOMRIGHTALIGN,
3443                                          rcWnd.right - sbVXSize,
3444                                          rcWnd.bottom - sbHYSize,
3445                                          sbVXSize, sbHYSize,
3446                                          hDlg,
3447                                          NULL,
3448                                          hInst,
3449                                          NULL);
3450             }
3451 
3452             // SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)hWndDetailsCtrl);
3453 
3454             /*
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.
3459              */
3460             GetWindowRect(hDlg, &rcWnd);
3461             cxMin = cyMin = 0;
3462 
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);
3466 
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);
3470 
3471             /*
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.
3475              */
3476             MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rcWnd, sizeof(RECT)/sizeof(POINT));
3477 
3478             cxMin += -2*rcWnd.left;   // Minimal spacing between the buttons == 2 * left margin
3479             cyMin += -rcWnd.top + 12; // Add some space on top
3480 
3481             GetClientRect(hDlg, &rcWnd);
3482             cxOld = rcWnd.right - rcWnd.left;
3483             cyOld = rcWnd.bottom - rcWnd.top;
3484 
3485             /* Show event info on dialog control */
3486             SendMessageW(hWndDetailsCtrl, EVT_DISPLAY, 0, 0);
3487 
3488             // SetWindowPos(hWndDetailsCtrl, NULL,
3489                          // 0, 0,
3490                          // (rcWnd.right - rcWnd.left),
3491                          // (rcWnd.bottom - rcWnd.top),
3492                          // SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
3493 
3494             /*
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
3500              * instead.
3501              */
3502             ShowWindow(GetDlgItem(hDlg, IDC_STATIC), SW_HIDE);
3503             ShowWindow(hWndDetailsCtrl, SW_SHOW);
3504             return (INT_PTR)TRUE;
3505         }
3506 
3507         case WM_DESTROY:
3508             if (IsWindow(hWndDetailsCtrl))
3509                 DestroyWindow(hWndDetailsCtrl);
3510             hWndDetailsCtrl = NULL;
3511             return (INT_PTR)TRUE;
3512 
3513         case WM_COMMAND:
3514             switch (LOWORD(wParam))
3515             {
3516                 case IDOK:
3517                 case IDCANCEL:
3518                     EndDialog(hDlg, LOWORD(wParam));
3519                     return (INT_PTR)TRUE;
3520 
3521                 case IDHELP:
3522                     MessageBoxW(hDlg,
3523                                 L"Help not implemented yet!",
3524                                 L"Event Log",
3525                                 MB_OK | MB_ICONINFORMATION);
3526                     return (INT_PTR)TRUE;
3527 
3528                 default:
3529                     break;
3530             }
3531             break;
3532 
3533         case WM_SETCURSOR:
3534             if (((HWND)wParam == hWndGrip) && (LOWORD(lParam) == HTCLIENT))
3535             {
3536                 SetCursor(LoadCursorW(NULL, IDC_SIZENWSE));
3537                 SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, TRUE);
3538                 return (INT_PTR)TRUE;
3539             }
3540             break;
3541 
3542         case WM_SIZING:
3543         {
3544             /* Forbid resizing the dialog smaller than its minimal size */
3545             PRECT dragRect = (PRECT)lParam;
3546 
3547             if ((wParam == WMSZ_LEFT) || (wParam == WMSZ_TOPLEFT) || (wParam == WMSZ_BOTTOMLEFT))
3548             {
3549                 if (dragRect->right - dragRect->left < cxMin)
3550                     dragRect->left = dragRect->right - cxMin;
3551             }
3552 
3553             if ((wParam == WMSZ_RIGHT) || (wParam == WMSZ_TOPRIGHT) || (wParam == WMSZ_BOTTOMRIGHT))
3554             {
3555                 if (dragRect->right - dragRect->left < cxMin)
3556                     dragRect->right = dragRect->left + cxMin;
3557             }
3558 
3559             if ((wParam == WMSZ_TOP) || (wParam == WMSZ_TOPLEFT) || (wParam == WMSZ_TOPRIGHT))
3560             {
3561                 if (dragRect->bottom - dragRect->top < cyMin)
3562                     dragRect->top = dragRect->bottom - cyMin;
3563             }
3564 
3565             if ((wParam == WMSZ_BOTTOM) || (wParam == WMSZ_BOTTOMLEFT) || (wParam == WMSZ_BOTTOMRIGHT))
3566             {
3567                 if (dragRect->bottom - dragRect->top < cyMin)
3568                     dragRect->bottom = dragRect->top + cyMin;
3569             }
3570 
3571             SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, TRUE);
3572             return (INT_PTR)TRUE;
3573         }
3574 
3575         case WM_SIZE:
3576         {
3577             INT cx = LOWORD(lParam);
3578             INT cy = HIWORD(lParam);
3579 
3580             HDWP hdwp;
3581             HWND hItemWnd;
3582             RECT rect;
3583 
3584             hdwp = BeginDeferWindowPos(4);
3585 
3586             /* Resize the event details control window */
3587 
3588             hItemWnd = hWndDetailsCtrl;
3589             GetWindowRect(hItemWnd, &rect);
3590             MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
3591 
3592             if (hdwp)
3593                 hdwp = DeferWindowPos(hdwp,
3594                                       hItemWnd,
3595                                       HWND_TOP,
3596                                       0, 0,
3597                                       (rect.right - rect.left) + (cx - cxOld),
3598                                       (rect.bottom - rect.top) + (cy - cyOld),
3599                                       SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
3600 
3601             /* Move the buttons */
3602 
3603             hItemWnd = GetDlgItem(hDlg, IDHELP);
3604             GetWindowRect(hItemWnd, &rect);
3605             MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
3606 
3607             if (hdwp)
3608                 hdwp = DeferWindowPos(hdwp,
3609                                       hItemWnd,
3610                                       HWND_TOP,
3611                                       rect.left,
3612                                       rect.top + (cy - cyOld),
3613                                       0, 0,
3614                                       SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
3615 
3616             hItemWnd = GetDlgItem(hDlg, IDOK);
3617             GetWindowRect(hItemWnd, &rect);
3618             MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
3619 
3620             if (hdwp)
3621                 hdwp = DeferWindowPos(hdwp,
3622                                       hItemWnd,
3623                                       HWND_TOP,
3624                                       rect.left + (cx - cxOld),
3625                                       rect.top  + (cy - cyOld),
3626                                       0, 0,
3627                                       SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
3628 
3629             /* Move the size grip */
3630             if (hWndGrip && hdwp)
3631             {
3632                 GetWindowRect(hWndGrip, &rect);
3633                 MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
3634 
3635                 hdwp = DeferWindowPos(hdwp,
3636                                       hWndGrip,
3637                                       HWND_TOP,
3638                                       rect.left + (cx - cxOld),
3639                                       rect.top  + (cy - cyOld),
3640                                       0, 0,
3641                                       SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
3642             }
3643 
3644             if (hdwp)
3645                 EndDeferWindowPos(hdwp);
3646 
3647             /* Hide the size grip if we are in maximized mode */
3648             if (hWndGrip)
3649                 ShowWindow(hWndGrip, (wParam == SIZE_MAXIMIZED) ? SW_HIDE : SW_SHOW);
3650 
3651             cxOld = cx;
3652             cyOld = cy;
3653 
3654             SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, 0);
3655             return (INT_PTR)TRUE;
3656         }
3657     }
3658 
3659     return (INT_PTR)FALSE;
3660 }
3661