ReactOS  0.4.15-dev-425-gc40b086
eventvwr.c
Go to the documentation of this file.
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 EVENTVWR_WNDCLASS = L"EVENTVWR"; /* The main window class name */
41 static const LPCWSTR EVENTLOG_BASE_KEY = L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\";
42 static const LPCWSTR EVNTVWR_PARAM_KEY = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Event Viewer";
43 
44 /* The 3 system logs that should always exist in the user's system */
45 static const LPCWSTR SystemLogs[] =
46 {
47  L"Application",
48  L"Security",
49  L"System"
50 };
51 
52 /* MessageFile message buffer size */
53 #define EVENT_MESSAGE_EVENTTEXT_BUFFER 1024*10 // NOTE: Used by evtdetctl.c
54 #define EVENT_MESSAGE_FILE_BUFFER 1024*10
55 #define EVENT_DLL_SEPARATOR L";"
56 #define EVENT_CATEGORY_MESSAGE_FILE L"CategoryMessageFile"
57 #define EVENT_MESSAGE_FILE L"EventMessageFile"
58 #define EVENT_PARAMETER_MESSAGE_FILE L"ParameterMessageFile"
59 
60 #define MAX_LOADSTRING 255
61 
62 #define SPLIT_WIDTH 4
63 
64 /* Globals */
65 HINSTANCE hInst; /* Current instance */
66 WCHAR szTitle[MAX_LOADSTRING]; /* The title bar text */
67 WCHAR szTitleTemplate[MAX_LOADSTRING]; /* The logged-on title bar text */
68 WCHAR szStatusBarTemplate[MAX_LOADSTRING]; /* The status bar text */
69 WCHAR szLoadingWait[MAX_LOADSTRING]; /* The "Loading, please wait..." text */
70 WCHAR szEmptyList[MAX_LOADSTRING]; /* The "There are no items to show in this view" text */
71 WCHAR szSaveFilter[MAX_LOADSTRING]; /* Filter Mask for the save Dialog */
72 
73 INT nVSplitPos; /* Vertical splitter (1) position */
74 INT nHSplitPos; /* Horizontal splitter (2) position */
75 BYTE bSplit = 0; /* Splitter state:
76  * 0: No splitting;
77  * 1: Vertical splitting;
78  * 2: Horizontal splitting.
79  */
80 
81 HWND hwndMainWindow = NULL; /* Main window */
82 HWND hwndTreeView; /* TreeView control */
83 HWND hwndListView; /* ListView control */ // NOTE: Used by evtdetctl.c
84 HWND hwndEventDetails; /* Event details pane */
85 HWND hwndStatus; /* Status bar */
86 HWND hwndStatusProgress; /* Progress bar in the status bar */
87 HMENU hMainMenu; /* The application's main menu */
88 
90 
91 LPWSTR lpComputerName = NULL; /* NULL: local user computer (default) */
92 LPWSTR lpszzUserLogsToLoad = NULL; /* The list of user logs to load at startup (multi-string) */
94 
95 HKEY hkMachine = NULL; // Registry handle to the HKEY_LOCAL_MACHINE key of the remote computer registry.
96 
97 /* Global event records cache for the current active event log filter */
100 
101 /* Lists of event logs and event log filters */
105 
108 
109 /*
110  * Setting EnumFilter to a valid pointer and raising the hStartEnumEvent event
111  * triggers the event-enumerator thread to perform a new enumeration.
112  */
114 HANDLE hStartStopEnumEvent = NULL; // End-of-application event
115 HANDLE hStartEnumEvent = NULL; // Command event
116 
117 /* Default Open/Save-As dialog box */
119 
120 
121 /* Event Viewer Application Settings */
122 typedef struct _SETTINGS
123 {
124  BOOL bShowDetailsPane; /* Show (TRUE) or Hide (FALSE) the events details pane */
125  BOOL bShowGrid; /* Show (TRUE) or Hide (FALSE) the events view grid */
126  BOOL bSaveSettings; /* Save (TRUE) or do not save (FALSE) current settings on exit */
127  BOOL bNewestEventsFirst; /* Sort the displayed events the newest ones first (TRUE) or last (FALSE) */
128  INT nVSplitPos; /* Vertical splitter position */
129  INT nHSplitPos; /* Horizontal splitter position */
131 } SETTINGS, *PSETTINGS;
132 
134 
135 
136 /* Forward declarations of functions included in this code module */
137 
138 static DWORD WINAPI
140 
141 VOID OpenUserEventLogFile(IN LPCWSTR lpszFileName);
142 
146 
152 
153 
154 /* MAIN FUNCTIONS *************************************************************/
155 
156 VOID
158 {
159  LPWSTR lpMessageBuffer;
160 
161  if (dwError == ERROR_SUCCESS)
162  return;
163 
167  NULL,
168  dwError,
170  (LPWSTR)&lpMessageBuffer,
171  0, NULL))
172  {
173  return;
174  }
175 
176  MessageBoxW(hwndMainWindow, lpMessageBuffer, szTitle, MB_OK | MB_ICONERROR);
177  LocalFree(lpMessageBuffer);
178 }
179 
180 VOID
182 {
184  LPCWSTR lpUsage;
185  INT iUsageLen = LoadStringW(hInst, IDS_USAGE, (LPWSTR)&lpUsage, 0);
186 
187  if (iUsageLen == 0)
188  return;
189 
190  lpBuffer = HeapAlloc(GetProcessHeap(), 0, (iUsageLen + 1) * sizeof(WCHAR));
191  if (!lpBuffer)
192  return;
193 
194  StringCchCopyNW(lpBuffer, iUsageLen + 1, lpUsage, iUsageLen);
196 
198 }
199 
200 BOOL
202 {
203  BOOL Success = FALSE;
204  INT i, argc;
205  LPWSTR* argv;
206 
207  /* Skip any leading whitespace */
208  if (lpCmdLine)
209  {
210  while (iswspace(*lpCmdLine))
211  ++lpCmdLine;
212  }
213 
214  /* No command line means no processing needed */
215  if (!lpCmdLine || !*lpCmdLine)
216  return TRUE;
217 
218  /* Build the arguments vector */
219  argv = CommandLineToArgvW(lpCmdLine, &argc);
220  if (!argv)
221  return FALSE;
222 
223  /* Parse the command line for options (skip the program name) */
224  for (i = 1; i < argc; ++i)
225  {
226  /* Check for new options */
227  if (argv[i][0] == L'-' || argv[i][0] == L'/')
228  {
229  if (argv[i][1] == L'?' && argv[i][2] == 0)
230  {
231  /* Display help */
232  DisplayUsage();
233  goto Quit;
234  }
235  else
236  if (argv[i][2] == L':')
237  {
238  switch (towupper(argv[i][1]))
239  {
240  case L'L':
241  {
242  LPWSTR lpNewBuffer;
243  LPWSTR lpFileName = argv[i] + 3;
244  SIZE_T cbFileName;
245 
246  /* Check for a quoted file name */
247  if (*lpFileName == L'\"')
248  {
249  /* Skip this quote, and the last one too if any */
250  ++lpFileName;
251  cbFileName = wcslen(lpFileName);
252  if (cbFileName > 0 && lpFileName[cbFileName - 1] == L'\"')
253  lpFileName[cbFileName - 1] = UNICODE_NULL;
254  }
255 
256  /* Skip this one if we do not actually have a file name */
257  if (!*lpFileName)
258  continue;
259 
260  cbFileName = (wcslen(lpFileName) + 1) * sizeof(WCHAR);
261 
262  /* Reallocate the list of user logs to load */
264  {
265  lpNewBuffer = HeapReAlloc(GetProcessHeap(),
268  /* Count the multi-string NULL-terminator */
269  cbUserLogsSize + cbFileName + sizeof(WCHAR));
270  }
271  else
272  {
273  cbUserLogsSize = 0;
274  lpNewBuffer = HeapAlloc(GetProcessHeap(),
276  /* Count the multi-string NULL-terminator */
277  cbUserLogsSize + cbFileName + sizeof(WCHAR));
278  }
279 
280  if (!lpNewBuffer)
281  {
283  goto Quit;
284  }
285 
286  lpszzUserLogsToLoad = lpNewBuffer;
287  lpNewBuffer = (LPWSTR)((ULONG_PTR)lpNewBuffer + cbUserLogsSize);
288  cbUserLogsSize += cbFileName;
289 
290  /* Save the file name */
291  StringCbCopyW(lpNewBuffer, cbFileName, lpFileName);
292 
293  continue;
294  }
295 
296  default:
297  break;
298  }
299  }
300 
301  /* Unknown argument: display help and bail out */
302  DisplayUsage();
303  goto Quit;
304  }
305  else
306  {
307  /*
308  * An argument that does not start with the switch character.
309  * If this is the first argument then this corresponds to the
310  * optional computer name. Otherwise this is a wrong argument.
311  */
312  if (i == 1)
313  {
314  /* Store the computer name */
315  LPWSTR lpTemp = argv[i];
316  SIZE_T cbLength;
317 
318  /* Strip any leading backslashes */
319  while (*lpTemp == L'\\')
320  ++lpTemp;
321 
322  cbLength = (wcslen(lpTemp) + 1) * sizeof(WCHAR);
324  if (lpComputerName)
325  {
326  StringCbCopyW(lpComputerName, cbLength, lpTemp);
327  }
328  /* else, fall back to local computer */
329  }
330  else
331  {
332  /* Invalid syntax: display help and bail out */
333  DisplayUsage();
334  goto Quit;
335  }
336  }
337  }
338 
339  Success = TRUE;
340 
341 Quit:
342  /* In case of failure, free anything we have allocated */
343  if (!Success)
344  {
346  {
347  cbUserLogsSize = 0;
350  }
351  if (lpComputerName)
352  {
355  }
356  }
357 
358  /* Free the arguments vector and exit */
359  LocalFree(argv);
360  return Success;
361 }
362 
363 BOOL
364 LoadSettings(int nDefCmdShow)
365 {
366  HKEY hKeyEventVwr;
367  LONG Result;
368  DWORD dwSize;
369  DWORD dwType;
370  DWORD Value;
371  UNICODE_STRING ValueU;
372  WCHAR buffer[100];
373 
374  /* Load the default values */
375  Settings.bShowDetailsPane = TRUE;
376  Settings.bShowGrid = FALSE;
377  Settings.bSaveSettings = TRUE;
378  Settings.bNewestEventsFirst = TRUE;
379  Settings.nVSplitPos = 250; /* Splitter default positions */
380  Settings.nHSplitPos = 250;
381  ZeroMemory(&Settings.wpPos, sizeof(Settings.wpPos));
382  Settings.wpPos.length = sizeof(Settings.wpPos);
383  Settings.wpPos.rcNormalPosition.left = CW_USEDEFAULT;
384  Settings.wpPos.rcNormalPosition.top = CW_USEDEFAULT;
385  Settings.wpPos.rcNormalPosition.right = CW_USEDEFAULT;
386  Settings.wpPos.rcNormalPosition.bottom = CW_USEDEFAULT;
387 
388  /* Try to create/open the Event Viewer user key */
391  0,
392  NULL,
395  NULL,
396  &hKeyEventVwr,
397  NULL) != ERROR_SUCCESS)
398  {
399  return FALSE;
400  }
401 
402  // Result = RegQueryValueExW(hKeyEventVwr, L"Filter", NULL, &dwType, (LPBYTE)&szFilter, &dwSize); // REG_SZ
403  // Result = RegQueryValueExW(hKeyEventVwr, L"Find", NULL, &dwType, (LPBYTE)&szFind, &dwSize); // REG_SZ
404  // Result = RegQueryValueExW(hKeyEventVwr, L"Module", NULL, &dwType, (LPBYTE)&szModule, &dwSize); // REG_SZ
405 
406  dwSize = sizeof(Value);
407  Result = RegQueryValueExW(hKeyEventVwr, L"DetailsPane", NULL, &dwType, (LPBYTE)&Value, &dwSize);
408  if ((Result == ERROR_SUCCESS) && (dwType == REG_SZ || dwType == REG_DWORD))
409  {
410  if (dwType == REG_SZ)
411  {
412  ValueU.Buffer = (PWSTR)&Value;
413  ValueU.Length = ValueU.MaximumLength = dwSize;
414  RtlUnicodeStringToInteger(&ValueU, 10, &Value);
415  }
416  Settings.bShowDetailsPane = !!Value;
417  }
418 
419  dwSize = sizeof(Value);
420  Result = RegQueryValueExW(hKeyEventVwr, L"ShowGrid", NULL, &dwType, (LPBYTE)&Value, &dwSize);
421  if ((Result == ERROR_SUCCESS) && (dwType == REG_SZ || dwType == REG_DWORD))
422  {
423  if (dwType == REG_SZ)
424  {
425  ValueU.Buffer = (PWSTR)&Value;
426  ValueU.Length = ValueU.MaximumLength = dwSize;
427  RtlUnicodeStringToInteger(&ValueU, 10, &Value);
428  }
429  Settings.bShowGrid = !!Value;
430  }
431 
432  dwSize = sizeof(Value);
433  Result = RegQueryValueExW(hKeyEventVwr, L"SortOrder", NULL, &dwType, (LPBYTE)&Value, &dwSize);
434  if ((Result == ERROR_SUCCESS) && (dwType == REG_SZ || dwType == REG_DWORD))
435  {
436  if (dwType == REG_SZ)
437  {
438  ValueU.Buffer = (PWSTR)&Value;
439  ValueU.Length = ValueU.MaximumLength = dwSize;
440  RtlUnicodeStringToInteger(&ValueU, 10, &Value);
441  }
442  Settings.bNewestEventsFirst = !!Value;
443  }
444 
445  /* Retrieve the splitter positions */
446  dwSize = sizeof(Value);
447  Result = RegQueryValueExW(hKeyEventVwr, L"VSplitPos", NULL, &dwType, (LPBYTE)&Value, &dwSize);
448  if ((Result == ERROR_SUCCESS) && (dwType == REG_SZ || dwType == REG_DWORD))
449  {
450  if (dwType == REG_SZ)
451  {
452  ValueU.Buffer = (PWSTR)&Value;
453  ValueU.Length = ValueU.MaximumLength = dwSize;
454  RtlUnicodeStringToInteger(&ValueU, 10, &Value);
455  }
456  Settings.nVSplitPos = Value;
457  }
458 
459  dwSize = sizeof(Value);
460  Result = RegQueryValueExW(hKeyEventVwr, L"HSplitPos", NULL, &dwType, (LPBYTE)&Value, &dwSize);
461  if ((Result == ERROR_SUCCESS) && (dwType == REG_SZ || dwType == REG_DWORD))
462  {
463  if (dwType == REG_SZ)
464  {
465  ValueU.Buffer = (PWSTR)&Value;
466  ValueU.Length = ValueU.MaximumLength = dwSize;
467  RtlUnicodeStringToInteger(&ValueU, 10, &Value);
468  }
469  Settings.nHSplitPos = Value;
470  }
471 
472  /* Retrieve the geometry of the main window */
473  dwSize = sizeof(buffer);
474  Result = RegQueryValueExW(hKeyEventVwr, L"Window", NULL, &dwType, (LPBYTE)buffer, &dwSize);
475  if ((Result != ERROR_SUCCESS) || (dwType != REG_SZ))
476  buffer[0] = UNICODE_NULL;
477 
478  if (swscanf(buffer, L"%d %d %d %d %d",
479  &Settings.wpPos.rcNormalPosition.left,
480  &Settings.wpPos.rcNormalPosition.top,
481  &Settings.wpPos.rcNormalPosition.right,
482  &Settings.wpPos.rcNormalPosition.bottom,
483  &Settings.wpPos.showCmd) != 5)
484  {
485  /* Parsing failed, use defaults */
486  Settings.wpPos.rcNormalPosition.left = CW_USEDEFAULT;
487  Settings.wpPos.rcNormalPosition.top = CW_USEDEFAULT;
488  Settings.wpPos.rcNormalPosition.right = CW_USEDEFAULT;
489  Settings.wpPos.rcNormalPosition.bottom = CW_USEDEFAULT;
490  Settings.wpPos.showCmd = nDefCmdShow; // SW_SHOWNORMAL;
491  }
492 
493  dwSize = sizeof(Value);
494  Result = RegQueryValueExW(hKeyEventVwr, L"SaveSettings", NULL, &dwType, (LPBYTE)&Value, &dwSize);
495  if ((Result == ERROR_SUCCESS) && (dwType == REG_SZ || dwType == REG_DWORD))
496  {
497  if (dwType == REG_SZ)
498  {
499  ValueU.Buffer = (PWSTR)&Value;
500  ValueU.Length = ValueU.MaximumLength = dwSize;
501  RtlUnicodeStringToInteger(&ValueU, 10, &Value);
502  }
503  Settings.bSaveSettings = !!Value;
504  }
505 
506  RegCloseKey(hKeyEventVwr);
507  return TRUE;
508 }
509 
510 BOOL
512 {
513  HKEY hKeyEventVwr;
514  DWORD dwSize;
515  WCHAR buffer[100];
516 
517  /* Try to create/open the Event Viewer user key */
520  0,
521  NULL,
524  NULL,
525  &hKeyEventVwr,
526  NULL) != ERROR_SUCCESS)
527  {
528  return FALSE;
529  }
530 
531  dwSize = sizeof(Settings.bSaveSettings);
532  RegSetValueExW(hKeyEventVwr, L"SaveSettings", 0, REG_DWORD, (LPBYTE)&Settings.bSaveSettings, dwSize);
533 
534  /* Do not save more settings if we are not asked to do so */
535  if (!Settings.bSaveSettings)
536  goto Quit;
537 
538  dwSize = sizeof(Settings.bShowDetailsPane);
539  RegSetValueExW(hKeyEventVwr, L"DetailsPane", 0, REG_DWORD, (LPBYTE)&Settings.bShowDetailsPane, dwSize);
540 
541  dwSize = sizeof(Settings.bShowGrid);
542  RegSetValueExW(hKeyEventVwr, L"ShowGrid", 0, REG_DWORD, (LPBYTE)&Settings.bShowGrid, dwSize);
543 
544  dwSize = sizeof(Settings.bNewestEventsFirst);
545  RegSetValueExW(hKeyEventVwr, L"SortOrder", 0, REG_DWORD, (LPBYTE)&Settings.bNewestEventsFirst, dwSize);
546 
547  Settings.nVSplitPos = nVSplitPos;
548  dwSize = sizeof(Settings.nVSplitPos);
549  RegSetValueExW(hKeyEventVwr, L"VSplitPos", 0, REG_DWORD, (LPBYTE)&Settings.nVSplitPos, dwSize);
550 
551  Settings.nHSplitPos = nHSplitPos;
552  dwSize = sizeof(Settings.nHSplitPos);
553  RegSetValueExW(hKeyEventVwr, L"HSplitPos", 0, REG_DWORD, (LPBYTE)&Settings.nHSplitPos, dwSize);
554 
555  StringCbPrintfW(buffer, sizeof(buffer),
556  L"%d %d %d %d %d",
557  Settings.wpPos.rcNormalPosition.left,
558  Settings.wpPos.rcNormalPosition.top,
559  Settings.wpPos.rcNormalPosition.right,
560  Settings.wpPos.rcNormalPosition.bottom,
561  Settings.wpPos.showCmd);
562 
563  dwSize = (DWORD)(wcslen(buffer) * sizeof(WCHAR));
564  RegSetValueExW(hKeyEventVwr, L"Window", 0, REG_SZ, (LPBYTE)buffer, dwSize);
565 
566 Quit:
567  RegCloseKey(hKeyEventVwr);
568  return TRUE;
569 }
570 
571 int APIENTRY
573  HINSTANCE hPrevInstance,
574  LPWSTR lpCmdLine,
575  int nCmdShow)
576 {
577  HANDLE hThread;
579  HMODULE hRichEdit;
580  HACCEL hAccelTable;
581  MSG msg;
582 
583  UNREFERENCED_PARAMETER(hPrevInstance);
584  UNREFERENCED_PARAMETER(lpCmdLine);
585 
586  /* Whenever any of the common controls are used in your app,
587  * you must call InitCommonControlsEx() to register the classes
588  * for those controls. */
589  iccx.dwSize = sizeof(iccx);
591  InitCommonControlsEx(&iccx);
592 
593  /* Load the RichEdit DLL to add support for RichEdit controls */
594  hRichEdit = LoadLibraryW(L"riched20.dll");
595  if (!hRichEdit)
596  return -1;
597 
598  msg.wParam = (WPARAM)-1;
599 
600  /* Store the instance handle in the global variable */
601  hInst = hInstance;
602 
603  /* Initialize global strings */
609 
610  /*
611  * Process the command-line arguments. Note that we need the full
612  * command-line, with the program file included, and not just what
613  * WinMain() provides in its lpCmdLine parameter.
614  */
616  goto Quit;
617 
619  goto Quit;
620 
621  /* Load the settings */
622  LoadSettings(nCmdShow);
623 
624  /* Perform application initialization */
625  if (!InitInstance(hInstance))
626  goto Quit;
627 
629 
630  /* Create the Start/Stop enumerator thread */
631  // Manual-reset event
633  if (!hStartStopEnumEvent)
634  goto Cleanup;
635 
636  // Auto-reset event
638  if (!hStartEnumEvent)
639  goto Cleanup;
640 
643  NULL, 0, NULL);
644  if (!hThread)
645  goto Cleanup;
646 
647  /* Retrieve the available event logs on this computer and create filters for them */
651 
652  /* Open the user-specified logs if any are present on the command-line */
654  {
655  LPWSTR lpUserLog;
656  for (lpUserLog = lpszzUserLogsToLoad; *lpUserLog; lpUserLog += wcslen(lpUserLog) + 1)
657  {
658  OpenUserEventLogFile(lpUserLog);
659  }
660 
661  /* Now cleanup the list of user logs */
662  cbUserLogsSize = 0;
665  }
666 
667  /* Main message loop */
668  while (GetMessageW(&msg, NULL, 0, 0))
669  {
670  if (!TranslateAcceleratorW(hwndMainWindow, hAccelTable, &msg))
671  {
674  }
675  }
676 
677  /* Save the settings */
678  SaveSettings();
679 
680  /* Disconnect from computer */
682  {
683  /* We are connected to some other computer, close the old connection */
685  hkMachine = NULL;
686  }
687 
688  /* Stop the enumerator thread */
692 
693  /* Free the filters list and the event logs list */
695  FreeLogList();
696 
697 Cleanup:
698  /* Handle cleanup */
699  if (hStartEnumEvent)
703 
704 Quit:
705  /* Final cleanup */
707  {
708  cbUserLogsSize = 0;
711  }
712  if (lpComputerName)
713  {
716  }
717  FreeLibrary(hRichEdit);
718 
719  return (int)msg.wParam;
720 }
721 
722 
723 /* GENERIC HELPER FUNCTIONS ***************************************************/
724 
725 VOID
727  OUT PSYSTEMTIME pSystemTime)
728 {
729  SYSTEMTIME st1970 = { 1970, 1, 0, 1, 0, 0, 0, 0 };
730  FILETIME ftLocal;
731  union
732  {
733  FILETIME ft;
734  ULONGLONG ll;
735  } u1970, uUCT;
736 
737  uUCT.ft.dwHighDateTime = 0;
738  uUCT.ft.dwLowDateTime = EventTime;
739  SystemTimeToFileTime(&st1970, &u1970.ft);
740  uUCT.ll = uUCT.ll * 10000000 + u1970.ll;
741  FileTimeToLocalFileTime(&uUCT.ft, &ftLocal);
742  FileTimeToSystemTime(&ftLocal, pSystemTime);
743 }
744 
745 /*
746  * This function takes in entry a path to a single DLL, in which
747  * the message string of ID dwMessageId has to be searched.
748  * The other parameters are similar to those of the FormatMessageW API.
749  */
750 LPWSTR
752  IN LPCWSTR lpMessageDll,
753  IN DWORD dwFlags, // If we always use the same flags, just remove this param...
754  IN DWORD dwMessageId,
755  IN DWORD nSize,
756  IN va_list* Arguments OPTIONAL)
757 {
759  DWORD dwLength;
760  LPWSTR lpMsgBuf = NULL;
761 
762  hLibrary = LoadLibraryExW(lpMessageDll, NULL,
763  /* LOAD_LIBRARY_AS_IMAGE_RESOURCE | */ LOAD_LIBRARY_AS_DATAFILE);
764  if (hLibrary == NULL)
765  return NULL;
766 
767  /* Sanitize dwFlags */
770 
771  _SEH2_TRY
772  {
773  /*
774  * Retrieve the message string without appending extra newlines.
775  * Wrap in SEH to protect from invalid string parameters.
776  */
777  _SEH2_TRY
778  {
780  /* FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
781  FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, */
782  hLibrary,
783  dwMessageId,
785  (LPWSTR)&lpMsgBuf,
786  nSize,
787  Arguments);
788  }
790  {
791  dwLength = 0;
792 
793  /*
794  * An exception occurred while calling FormatMessage, this is usually
795  * the sign that a parameter was invalid, either 'lpMsgBuf' was NULL
796  * but we did not pass the flag FORMAT_MESSAGE_ALLOCATE_BUFFER, or the
797  * array pointer 'Arguments' was NULL or did not contain enough elements,
798  * and we did not pass the flag FORMAT_MESSAGE_IGNORE_INSERTS, and the
799  * message string expected too many inserts.
800  * In this last case only, we can call again FormatMessage but ignore
801  * explicitly the inserts. The string that we will return to the user
802  * will not be pre-formatted.
803  */
804  if (((dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) || lpMsgBuf) &&
806  {
807  /* Remove any possible harmful flags and always ignore inserts */
810 
811  /* If this call also throws an exception, we are really dead */
813  hLibrary,
814  dwMessageId,
816  (LPWSTR)&lpMsgBuf,
817  nSize,
818  NULL /* Arguments */);
819  }
820  }
821  _SEH2_END;
822  }
824  {
826  }
827  _SEH2_END;
828 
829  if (dwLength == 0)
830  {
831  ASSERT(lpMsgBuf == NULL);
832  lpMsgBuf = NULL;
833  }
834  else
835  {
836  LPWSTR ptr;
837 
838  ASSERT(lpMsgBuf);
839 
840  /* Trim any trailing whitespace */
841  ptr = lpMsgBuf + dwLength - 1;
842  while (iswspace(*ptr))
843  *ptr-- = UNICODE_NULL;
844  }
845 
846  return lpMsgBuf;
847 }
848 
849 /*
850  * This function takes in entry a comma-separated list of DLLs, in which
851  * the message string of ID dwMessageId has to be searched.
852  * The other parameters are similar to those of the FormatMessageW API.
853  */
854 LPWSTR
856  IN LPCWSTR lpMessageDllList,
857  IN DWORD dwFlags, // If we always use the same flags, just remove this param...
858  IN DWORD dwMessageId,
859  IN DWORD nSize,
860  IN va_list* Arguments OPTIONAL)
861 {
862  BOOL Success = FALSE;
863  SIZE_T cbLength;
864  LPWSTR szMessageDllList;
865  LPWSTR szDll;
866  LPWSTR lpMsgBuf = NULL;
867 
868  /* Allocate a local buffer for the DLL list that can be tokenized */
869  // TODO: Optimize that!! Maybe we can cleverly use lpMessageDllList in read/write mode
870  // and cleverly temporarily replace the ';' by UNICODE_NULL, do our job, then reverse the change.
871  cbLength = (wcslen(lpMessageDllList) + 1) * sizeof(WCHAR);
872  szMessageDllList = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbLength);
873  if (!szMessageDllList)
874  return NULL;
875  RtlCopyMemory(szMessageDllList, lpMessageDllList, cbLength);
876 
877  /* Loop through the list of message DLLs */
878  szDll = wcstok(szMessageDllList, EVENT_DLL_SEPARATOR);
879  while ((szDll != NULL) && !Success)
880  {
881  // Uses LANG_USER_DEFAULT
882  lpMsgBuf = GetMessageStringFromDll(szDll,
883  dwFlags,
884  dwMessageId,
885  nSize,
886  Arguments);
887  if (lpMsgBuf)
888  {
889  /* The ID was found and the message was formatted */
890  Success = TRUE;
891  break;
892  }
893 
894  /*
895  * The DLL could not be loaded, or the message could not be found,
896  * try the next DLL, if any.
897  */
898  szDll = wcstok(NULL, EVENT_DLL_SEPARATOR);
899  }
900 
901  HeapFree(GetProcessHeap(), 0, szMessageDllList);
902 
903  return lpMsgBuf;
904 }
905 
906 
907 typedef struct
908 {
909  LPWSTR pStartingAddress; // Pointer to the beginning of a parameter string in pMessage
910  LPWSTR pEndingAddress; // Pointer to the end of a parameter string in pMessage
911  DWORD pParameterID; // Parameter identifier found in pMessage
912  LPWSTR pParameter; // Actual parameter string
914 
915 DWORD
917  IN LPCWSTR lpMessageDllList,
918  IN BOOL bMessagePreFormatted,
919  IN CONST LPCWSTR pMessage,
920  OUT LPWSTR* pFinalMessage)
921 {
922  /*
923  * This code is heavily adapted from the MSDN example:
924  * https://msdn.microsoft.com/en-us/library/windows/desktop/bb427356.aspx
925  * with bugs removed.
926  */
927 
929  DWORD dwParamCount = 0; // Number of insertion strings found in pMessage
930  size_t cchBuffer = 0; // Size of the buffer in characters
931  size_t cchParams = 0; // Number of characters in all the parameter strings
932  size_t cch = 0;
933  DWORD i = 0;
934  param_strings_format_data* pParamData = NULL; // Array of pointers holding information about each parameter string in pMessage
935  LPWSTR pTempMessage = (LPWSTR)pMessage;
936  LPWSTR pTempFinalMessage = NULL;
937 
938  *pFinalMessage = NULL;
939 
940  /* Determine the number of parameter insertion strings in pMessage */
941  if (bMessagePreFormatted)
942  {
943  while ((pTempMessage = wcschr(pTempMessage, L'%')))
944  {
945  pTempMessage++;
946  if (iswdigit(*pTempMessage))
947  {
948  dwParamCount++;
949  while (iswdigit(*++pTempMessage)) ;
950  }
951  }
952  }
953  else
954  {
955  while ((pTempMessage = wcsstr(pTempMessage, L"%%")))
956  {
957  pTempMessage += 2;
958  if (iswdigit(*pTempMessage))
959  {
960  dwParamCount++;
961  while (iswdigit(*++pTempMessage)) ;
962  }
963  }
964  }
965 
966  /* If there are no parameter insertion strings in pMessage, just return */
967  if (dwParamCount == 0)
968  {
969  // *pFinalMessage = NULL;
970  goto Cleanup;
971  }
972 
973  /* Allocate the array of parameter string format data */
974  pParamData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwParamCount * sizeof(param_strings_format_data));
975  if (!pParamData)
976  {
978  goto Cleanup;
979  }
980 
981  /*
982  * Retrieve each parameter in pMessage and the beginning and end of the
983  * insertion string, as well as the message identifier of the parameter.
984  */
985  pTempMessage = (LPWSTR)pMessage;
986  if (bMessagePreFormatted)
987  {
988  while ((pTempMessage = wcschr(pTempMessage, L'%')) && (i < dwParamCount))
989  {
990  pTempMessage++;
991  if (iswdigit(*pTempMessage))
992  {
993  pParamData[i].pStartingAddress = pTempMessage-1;
994  pParamData[i].pParameterID = (DWORD)_wtol(pTempMessage);
995 
996  while (iswdigit(*++pTempMessage)) ;
997 
998  pParamData[i].pEndingAddress = pTempMessage;
999  i++;
1000  }
1001  }
1002  }
1003  else
1004  {
1005  while ((pTempMessage = wcsstr(pTempMessage, L"%%")) && (i < dwParamCount))
1006  {
1007  pTempMessage += 2;
1008  if (iswdigit(*pTempMessage))
1009  {
1010  pParamData[i].pStartingAddress = pTempMessage-2;
1011  pParamData[i].pParameterID = (DWORD)_wtol(pTempMessage);
1012 
1013  while (iswdigit(*++pTempMessage)) ;
1014 
1015  pParamData[i].pEndingAddress = pTempMessage;
1016  i++;
1017  }
1018  }
1019  }
1020 
1021  /* Retrieve each parameter string */
1022  for (i = 0; i < dwParamCount; i++)
1023  {
1024  // pParamData[i].pParameter = GetMessageString(pParamData[i].pParameterID, 0, NULL);
1025  pParamData[i].pParameter =
1026  GetMessageStringFromDllList(lpMessageDllList,
1029  pParamData[i].pParameterID,
1030  0, NULL);
1031  if (!pParamData[i].pParameter)
1032  {
1033  /* Skip the insertion string */
1034  continue;
1035  }
1036 
1037  cchParams += wcslen(pParamData[i].pParameter);
1038  }
1039 
1040  /*
1041  * Allocate the final message buffer, the size of which is based on the
1042  * length of the original message and the length of each parameter string.
1043  */
1044  cchBuffer = wcslen(pMessage) + cchParams + 1;
1045  *pFinalMessage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cchBuffer * sizeof(WCHAR));
1046  if (!*pFinalMessage)
1047  {
1049  goto Cleanup;
1050  }
1051 
1052  pTempFinalMessage = *pFinalMessage;
1053 
1054  /* Build the final message string */
1055  pTempMessage = (LPWSTR)pMessage;
1056  for (i = 0; i < dwParamCount; i++)
1057  {
1058  /* Append the segment from pMessage */
1059  cch = pParamData[i].pStartingAddress - pTempMessage;
1060  StringCchCopyNW(pTempFinalMessage, cchBuffer, pTempMessage, cch);
1061  pTempMessage = pParamData[i].pEndingAddress;
1062  cchBuffer -= cch;
1063  pTempFinalMessage += cch;
1064 
1065  /* Append the parameter string */
1066  if (pParamData[i].pParameter)
1067  {
1068  StringCchCopyW(pTempFinalMessage, cchBuffer, pParamData[i].pParameter);
1069  cch = wcslen(pParamData[i].pParameter); // pTempFinalMessage
1070  }
1071  else
1072  {
1073  /*
1074  * We failed to retrieve the parameter string before, so just
1075  * place back the original string placeholder.
1076  */
1077  cch = pParamData[i].pEndingAddress /* == pTempMessage */ - pParamData[i].pStartingAddress;
1078  StringCchCopyNW(pTempFinalMessage, cchBuffer, pParamData[i].pStartingAddress, cch);
1079  // cch = wcslen(pTempFinalMessage);
1080  }
1081  cchBuffer -= cch;
1082  pTempFinalMessage += cch;
1083  }
1084 
1085  /* Append the last segment from pMessage */
1086  StringCchCopyW(pTempFinalMessage, cchBuffer, pTempMessage);
1087 
1088 Cleanup:
1089 
1090  // if (Status != ERROR_SUCCESS)
1091  // *pFinalMessage = NULL;
1092 
1093  if (pParamData)
1094  {
1095  for (i = 0; i < dwParamCount; i++)
1096  {
1097  if (pParamData[i].pParameter)
1098  LocalFree(pParamData[i].pParameter);
1099  }
1100 
1101  HeapFree(GetProcessHeap(), 0, pParamData);
1102  }
1103 
1104  return Status;
1105 }
1106 
1107 
1108 /*
1109  * The following functions were adapted from
1110  * shell32!dialogs/filedefext.cpp:``SH_...'' functions.
1111  */
1112 
1113 UINT
1114 FormatInteger(LONGLONG Num, LPWSTR pwszResult, UINT cchResultMax)
1115 {
1116  WCHAR wszNumber[24];
1117  WCHAR wszDecimalSep[8], wszThousandSep[8];
1118  NUMBERFMTW nf;
1119  WCHAR wszGrouping[12];
1120  INT cchGrouping;
1121  INT cchResult;
1122  INT i;
1123 
1124  // Print the number in uniform mode
1125  swprintf(wszNumber, L"%I64u", Num);
1126 
1127  // Get system strings for decimal and thousand separators.
1128  GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, wszDecimalSep, _countof(wszDecimalSep));
1129  GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, wszThousandSep, _countof(wszThousandSep));
1130 
1131  // Initialize format for printing the number in bytes
1132  ZeroMemory(&nf, sizeof(nf));
1133  nf.lpDecimalSep = wszDecimalSep;
1134  nf.lpThousandSep = wszThousandSep;
1135 
1136  // Get system string for groups separator
1137  cchGrouping = GetLocaleInfoW(LOCALE_USER_DEFAULT,
1139  wszGrouping,
1140  _countof(wszGrouping));
1141 
1142  // Convert grouping specs from string to integer
1143  for (i = 0; i < cchGrouping; i++)
1144  {
1145  WCHAR wch = wszGrouping[i];
1146 
1147  if (wch >= L'0' && wch <= L'9')
1148  nf.Grouping = nf.Grouping * 10 + (wch - L'0');
1149  else if (wch != L';')
1150  break;
1151  }
1152 
1153  if ((nf.Grouping % 10) == 0)
1154  nf.Grouping /= 10;
1155  else
1156  nf.Grouping *= 10;
1157 
1158  // Format the number
1160  0,
1161  wszNumber,
1162  &nf,
1163  pwszResult,
1164  cchResultMax);
1165 
1166  if (!cchResult)
1167  return 0;
1168 
1169  // GetNumberFormatW returns number of characters including UNICODE_NULL
1170  return cchResult - 1;
1171 }
1172 
1173 UINT
1174 FormatByteSize(LONGLONG cbSize, LPWSTR pwszResult, UINT cchResultMax)
1175 {
1176  UINT cchWritten, cchRemaining;
1177  LPWSTR pwszEnd;
1178  size_t cchStringRemaining;
1179 
1180  /* Write formated bytes count */
1181  cchWritten = FormatInteger(cbSize, pwszResult, cchResultMax);
1182  if (!cchWritten)
1183  return 0;
1184 
1185  /* Copy " bytes" to buffer */
1186  pwszEnd = pwszResult + cchWritten;
1187  cchRemaining = cchResultMax - cchWritten;
1188  StringCchCopyExW(pwszEnd, cchRemaining, L" ", &pwszEnd, &cchStringRemaining, 0);
1189  cchRemaining = (UINT)cchStringRemaining;
1190  cchWritten = LoadStringW(hInst, IDS_BYTES_FORMAT, pwszEnd, cchRemaining);
1191  cchRemaining -= cchWritten;
1192 
1193  return cchResultMax - cchRemaining;
1194 }
1195 
1196 LPWSTR
1197 FormatFileSizeWithBytes(const PULARGE_INTEGER lpQwSize, LPWSTR pwszResult, UINT cchResultMax)
1198 {
1199  UINT cchWritten, cchRemaining;
1200  LPWSTR pwszEnd;
1201  size_t cchCopyRemaining;
1202 
1203  /* Format bytes in KBs, MBs etc */
1204  if (StrFormatByteSizeW(lpQwSize->QuadPart, pwszResult, cchResultMax) == NULL)
1205  return NULL;
1206 
1207  /* If there is less bytes than 1KB, we have nothing to do */
1208  if (lpQwSize->QuadPart < 1024)
1209  return pwszResult;
1210 
1211  /* Concatenate " (" */
1212  cchWritten = (UINT)wcslen(pwszResult);
1213  pwszEnd = pwszResult + cchWritten;
1214  cchRemaining = cchResultMax - cchWritten;
1215  StringCchCopyExW(pwszEnd, cchRemaining, L" (", &pwszEnd, &cchCopyRemaining, 0);
1216  cchRemaining = (UINT)cchCopyRemaining;
1217 
1218  /* Write formated bytes count */
1219  cchWritten = FormatByteSize(lpQwSize->QuadPart, pwszEnd, cchRemaining);
1220  pwszEnd += cchWritten;
1221  cchRemaining -= cchWritten;
1222 
1223  /* Copy ")" to the buffer */
1224  StringCchCopyW(pwszEnd, cchRemaining, L")");
1225 
1226  return pwszResult;
1227 }
1228 
1229 /* Adapted from shell32!dialogs/filedefext.cpp:``CFileDefExt::GetFileTimeString'' */
1230 BOOL
1231 GetFileTimeString(LPFILETIME lpFileTime, LPWSTR pwszResult, UINT cchResult)
1232 {
1233  FILETIME ft;
1234  SYSTEMTIME st;
1235  int cchWritten;
1236  UINT cchRemaining = cchResult;
1237  size_t cchCopyRemaining;
1238  LPWSTR pwszEnd = pwszResult;
1239 
1240  if (!FileTimeToLocalFileTime(lpFileTime, &ft) || !FileTimeToSystemTime(&ft, &st))
1241  return FALSE;
1242 
1243  cchWritten = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, pwszEnd, cchRemaining);
1244  if (cchWritten)
1245  --cchWritten; // GetDateFormatW returns count with terminating zero
1246  // else
1247  // ERR("GetDateFormatW failed\n");
1248 
1249  cchRemaining -= cchWritten;
1250  pwszEnd += cchWritten;
1251 
1252  StringCchCopyExW(pwszEnd, cchRemaining, L", ", &pwszEnd, &cchCopyRemaining, 0);
1253  cchRemaining = (UINT)cchCopyRemaining;
1254 
1255  cchWritten = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, pwszEnd, cchRemaining);
1256  if (cchWritten)
1257  --cchWritten; // GetTimeFormatW returns count with terminating zero
1258  // else
1259  // ERR("GetTimeFormatW failed\n");
1260 
1261  return TRUE;
1262 }
1263 
1264 
1265 HTREEITEM
1268  IN LPWSTR lpText,
1269  IN INT Image,
1270  IN INT SelectedImage,
1271  IN LPARAM lParam)
1272 {
1273  TV_INSERTSTRUCTW Insert;
1274 
1275  ZeroMemory(&Insert, sizeof(Insert));
1276 
1277  Insert.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
1278  Insert.hInsertAfter = TVI_LAST;
1279  Insert.hParent = hParent;
1280  Insert.item.pszText = lpText;
1281  Insert.item.iImage = Image;
1282  Insert.item.iSelectedImage = SelectedImage;
1283  Insert.item.lParam = lParam;
1284 
1285  Insert.item.mask |= TVIF_STATE;
1286  Insert.item.stateMask = TVIS_OVERLAYMASK;
1287  Insert.item.state = INDEXTOOVERLAYMASK(1);
1288 
1289  return TreeView_InsertItem(hTreeView, &Insert);
1290 }
1291 
1292 
1293 /* LOG HELPER FUNCTIONS *******************************************************/
1294 
1295 PEVENTLOG
1297  IN PCWSTR LogName,
1298  IN BOOL Permanent)
1299 {
1300  PEVENTLOG EventLog;
1301  SIZE_T cchName;
1302 
1303  /* Allocate a new event log entry */
1304  EventLog = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*EventLog));
1305  if (!EventLog)
1306  return NULL;
1307 
1308  /* Allocate the computer name string (optional) and copy it */
1309  if (ComputerName)
1310  {
1311  cchName = wcslen(ComputerName) + 1;
1312  EventLog->ComputerName = HeapAlloc(GetProcessHeap(), 0, cchName * sizeof(WCHAR));
1313  if (EventLog->ComputerName)
1314  StringCchCopyW(EventLog->ComputerName, cchName, ComputerName);
1315  }
1316 
1317  /* Allocate the event log name string and copy it */
1318  cchName = wcslen(LogName) + 1;
1319  EventLog->LogName = HeapAlloc(GetProcessHeap(), 0, cchName * sizeof(WCHAR));
1320  if (!EventLog->LogName)
1321  {
1322  if (EventLog->ComputerName)
1323  HeapFree(GetProcessHeap(), 0, EventLog->ComputerName);
1324  HeapFree(GetProcessHeap(), 0, EventLog);
1325  return NULL;
1326  }
1327  StringCchCopyW(EventLog->LogName, cchName, LogName);
1328 
1329  EventLog->Permanent = Permanent;
1330 
1331  return EventLog;
1332 }
1333 
1334 VOID
1336 {
1337  if (EventLog->LogName)
1338  HeapFree(GetProcessHeap(), 0, EventLog->LogName);
1339 
1340  if (EventLog->ComputerName)
1341  HeapFree(GetProcessHeap(), 0, EventLog->ComputerName);
1342 
1343  if (EventLog->FileName)
1344  HeapFree(GetProcessHeap(), 0, EventLog->FileName);
1345 
1346  HeapFree(GetProcessHeap(), 0, EventLog);
1347 }
1348 
1349 
1350 PWSTR
1352 {
1353  PWSTR pStr;
1354  ULONG Length;
1355 
1356  if (!MultiStr)
1357  return NULL;
1358 
1359  pStr = (PWSTR)MultiStr;
1360  while (*pStr) pStr += (wcslen(pStr) + 1);
1361  Length = MultiStr - pStr + 2;
1362 
1363  pStr = HeapAlloc(GetProcessHeap(), 0, Length * sizeof(WCHAR));
1364  // NOTE: If we failed allocating the string, then fall back into no filter!
1365  if (pStr)
1366  RtlCopyMemory(pStr, MultiStr, Length * sizeof(WCHAR));
1367 
1368  return pStr;
1369 }
1370 
1372 AllocEventLogFilter(// IN PCWSTR FilterName,
1374  IN BOOL Warning,
1375  IN BOOL Error,
1376  IN BOOL AuditSuccess,
1377  IN BOOL AuditFailure,
1378  IN PCWSTR Sources OPTIONAL,
1379  IN PCWSTR Users OPTIONAL,
1380  IN PCWSTR ComputerNames OPTIONAL,
1381  IN ULONG NumOfEventLogs,
1382  IN PEVENTLOG* EventLogs)
1383 {
1384  PEVENTLOGFILTER EventLogFilter;
1385 
1386  /* Allocate a new event log filter entry, big enough to accommodate the list of logs */
1387  EventLogFilter = HeapAlloc(GetProcessHeap(),
1389  FIELD_OFFSET(EVENTLOGFILTER, EventLogs[NumOfEventLogs]));
1390  if (!EventLogFilter)
1391  return NULL;
1392 
1393  EventLogFilter->Information = Information;
1394  EventLogFilter->Warning = Warning;
1395  EventLogFilter->Error = Error;
1396  EventLogFilter->AuditSuccess = AuditSuccess;
1397  EventLogFilter->AuditFailure = AuditFailure;
1398 
1399  /* Allocate and copy the sources, users, and computers multi-strings */
1400  EventLogFilter->Sources = AllocAndCopyMultiStr(Sources);
1401  EventLogFilter->Users = AllocAndCopyMultiStr(Users);
1402  EventLogFilter->ComputerNames = AllocAndCopyMultiStr(ComputerNames);
1403 
1404  /* Copy the list of event logs */
1405  EventLogFilter->NumOfEventLogs = NumOfEventLogs;
1406  RtlCopyMemory(EventLogFilter->EventLogs, EventLogs, NumOfEventLogs * sizeof(PEVENTLOG));
1407 
1408  /* Initialize the filter reference count */
1409  EventLogFilter->ReferenceCount = 1;
1410 
1411  return EventLogFilter;
1412 }
1413 
1414 VOID
1416 {
1417  if (EventLogFilter->Sources)
1418  HeapFree(GetProcessHeap(), 0, EventLogFilter->Sources);
1419 
1420  if (EventLogFilter->Users)
1421  HeapFree(GetProcessHeap(), 0, EventLogFilter->Users);
1422 
1423  if (EventLogFilter->ComputerNames)
1424  HeapFree(GetProcessHeap(), 0, EventLogFilter->ComputerNames);
1425 
1426  HeapFree(GetProcessHeap(), 0, EventLogFilter);
1427 }
1428 
1430 {
1431  ASSERT(EventLogFilter);
1432  return InterlockedIncrement(&EventLogFilter->ReferenceCount);
1433 }
1434 
1436 {
1437  LONG RefCount;
1438 
1439  ASSERT(EventLogFilter);
1440 
1441  /* When the reference count reaches zero, delete the filter */
1442  RefCount = InterlockedDecrement(&EventLogFilter->ReferenceCount);
1443  if (RefCount <= 0)
1444  {
1445  /* Remove the filter from the list */
1447  EventLogFilter_Free(EventLogFilter);
1448  }
1449 
1450  return RefCount;
1451 }
1452 
1453 void
1455 {
1456  WCHAR *c;
1457 
1458  if (s != NULL)
1459  {
1460  c = s + wcslen(s) - 1;
1461  while (c >= s && iswspace(*c))
1462  --c;
1463  *++c = L'\0';
1464  }
1465 }
1466 
1467 DWORD
1469  IN LPCWSTR ComputerName OPTIONAL,
1471  OUT LPWSTR lpFullFileName OPTIONAL,
1472  IN DWORD nSize)
1473 {
1474  SIZE_T dwLength;
1475 
1476  /* Determine the needed size after expansion of any environment strings */
1478  if (dwLength == 0)
1479  {
1480  /* We failed, bail out */
1481  return 0;
1482  }
1483 
1484  /* If the file path is on a remote computer, estimate its length */
1485  // FIXME: Use WNetGetUniversalName instead?
1486  if (ComputerName && *ComputerName)
1487  {
1488  /* Skip any leading backslashes */
1489  while (*ComputerName == L'\\')
1490  ++ComputerName;
1491 
1492  if (*ComputerName)
1493  {
1494  /* Count 2 backslashes plus the computer name and one backslash separator */
1495  dwLength += 2 + wcslen(ComputerName) + 1;
1496  }
1497  }
1498 
1499  /* Check whether we have enough space */
1500  if (dwLength > nSize)
1501  {
1502  /* No, return the needed size in characters (includes NULL-terminator) */
1503  return dwLength;
1504  }
1505 
1506 
1507  /* Now expand the file path */
1508  ASSERT(dwLength <= nSize);
1509 
1510  /* Expand any existing environment strings */
1511  if (ExpandEnvironmentStringsW(lpFileName, lpFullFileName, dwLength) == 0)
1512  {
1513  /* We failed, bail out */
1514  return 0;
1515  }
1516 
1517  /* If the file path is on a remote computer, retrieve the network share form of the file name */
1518  // FIXME: Use WNetGetUniversalName instead?
1519  if (ComputerName && *ComputerName)
1520  {
1521  /* Note that we previously skipped any potential leading backslashes */
1522 
1523  /* Replace ':' by '$' in the drive letter */
1524  if (*lpFullFileName && lpFullFileName[1] == L':')
1525  lpFullFileName[1] = L'$';
1526 
1527  /* Prepend the computer name */
1528  RtlMoveMemory(lpFullFileName + 2 + wcslen(ComputerName) + 1,
1529  lpFullFileName, dwLength * sizeof(WCHAR) - (2 + wcslen(ComputerName) + 1) * sizeof(WCHAR));
1530  lpFullFileName[0] = L'\\';
1531  lpFullFileName[1] = L'\\';
1532  wcsncpy(lpFullFileName + 2, ComputerName, wcslen(ComputerName));
1533  lpFullFileName[2 + wcslen(ComputerName)] = L'\\';
1534  }
1535 
1536  /* Return the number of stored characters (includes NULL-terminator) */
1537  return dwLength;
1538 }
1539 
1540 BOOL
1543  IN LPCWSTR EntryName,
1544  OUT PWCHAR lpModuleName) // TODO: Add IN DWORD BufLen
1545 {
1546  BOOL Success = FALSE;
1547  LONG Result;
1548  DWORD dwType, dwSize;
1550  WCHAR szKeyName[MAX_PATH];
1551  HKEY hLogKey = NULL;
1552  HKEY hSourceKey = NULL;
1553 
1554  StringCbCopyW(szKeyName, sizeof(szKeyName), EVENTLOG_BASE_KEY);
1555  StringCbCatW(szKeyName, sizeof(szKeyName), lpLogName);
1556 
1558  szKeyName,
1559  0,
1560  KEY_READ,
1561  &hLogKey);
1562  if (Result != ERROR_SUCCESS)
1563  return FALSE;
1564 
1565  Result = RegOpenKeyExW(hLogKey,
1566  SourceName,
1567  0,
1569  &hSourceKey);
1570  if (Result != ERROR_SUCCESS)
1571  {
1572  RegCloseKey(hLogKey);
1573  return FALSE;
1574  }
1575 
1576  dwSize = sizeof(szModuleName);
1577  Result = RegQueryValueExW(hSourceKey,
1578  EntryName,
1579  NULL,
1580  &dwType,
1582  &dwSize);
1583  if ((Result != ERROR_SUCCESS) || (dwType != REG_EXPAND_SZ && dwType != REG_SZ))
1584  {
1586  }
1587  else
1588  {
1589  /* NULL-terminate the string and expand it */
1590  szModuleName[dwSize / sizeof(WCHAR) - 1] = UNICODE_NULL;
1592  Success = TRUE;
1593  }
1594 
1595  RegCloseKey(hSourceKey);
1596  RegCloseKey(hLogKey);
1597 
1598  return Success;
1599 }
1600 
1601 BOOL
1604  IN PEVENTLOGRECORD pevlr,
1605  OUT PWCHAR CategoryName) // TODO: Add IN DWORD BufLen
1606 {
1607  BOOL Success = FALSE;
1608  WCHAR szMessageDLL[MAX_PATH];
1609  LPWSTR lpMsgBuf = NULL;
1610 
1612  goto Quit;
1613 
1614  /* Retrieve the message string without appending extra newlines */
1615  lpMsgBuf =
1616  GetMessageStringFromDllList(szMessageDLL,
1619  pevlr->EventCategory,
1621  NULL);
1622  if (lpMsgBuf)
1623  {
1624  /* Trim the string */
1625  TrimNulls(lpMsgBuf);
1626 
1627  /* Copy the category name */
1628  StringCchCopyW(CategoryName, MAX_PATH, lpMsgBuf);
1629 
1630  /* Free the buffer allocated by FormatMessage */
1631  LocalFree(lpMsgBuf);
1632 
1633  /* The ID was found and the message was formatted */
1634  Success = TRUE;
1635  }
1636 
1637 Quit:
1638  if (!Success)
1639  {
1640  if (pevlr->EventCategory != 0)
1641  {
1642  StringCchPrintfW(CategoryName, MAX_PATH, L"(%lu)", pevlr->EventCategory);
1643  Success = TRUE;
1644  }
1645  }
1646 
1647  return Success;
1648 }
1649 
1650 
1651 BOOL // NOTE: Used by evtdetctl.c
1654  IN PEVENTLOGRECORD pevlr,
1655  OUT PWCHAR EventText) // TODO: Add IN DWORD BufLen
1656 {
1657  BOOL Success = FALSE;
1658  DWORD i;
1659  size_t cch;
1660  WCHAR SourceModuleName[1024];
1661  WCHAR ParameterModuleName[1024];
1662  BOOL IsParamModNameCached = FALSE;
1663  LPWSTR lpMsgBuf = NULL;
1664  LPWSTR szStringArray, szMessage;
1665  LPWSTR *szArguments;
1666 
1667  /* Get the event string array */
1668  szStringArray = (LPWSTR)((LPBYTE)pevlr + pevlr->StringOffset);
1669 
1670  /* NOTE: GetEventMessageFileDLL can return a comma-separated list of DLLs */
1671  if (!GetEventMessageFileDLL(KeyName, SourceName, EVENT_MESSAGE_FILE, SourceModuleName))
1672  goto Quit;
1673 
1674  /* Allocate space for insertion strings */
1675  szArguments = HeapAlloc(GetProcessHeap(), 0, pevlr->NumStrings * sizeof(LPVOID));
1676  if (!szArguments)
1677  goto Quit;
1678 
1679  if (!IsParamModNameCached)
1680  {
1681  /* Now that the parameter file list is loaded, no need to reload it at the next run! */
1682  IsParamModNameCached = GetEventMessageFileDLL(KeyName, SourceName, EVENT_PARAMETER_MESSAGE_FILE, ParameterModuleName);
1683  // FIXME: If the string loading failed the first time, no need to retry it just after???
1684  }
1685 
1686  if (IsParamModNameCached)
1687  {
1688  /* Not yet support for reading messages from parameter message DLL */
1689  }
1690 
1691  szMessage = szStringArray;
1692  /*
1693  * HACK:
1694  * We do some hackish preformatting of the cached event strings...
1695  * That's because after we pass the string to FormatMessage
1696  * (via GetMessageStringFromDllList) with the FORMAT_MESSAGE_ARGUMENT_ARRAY
1697  * flag, instead of ignoring the insertion parameters and do the formatting
1698  * by ourselves. Therefore, the resulting string should have the parameter
1699  * string placeholders starting with a single '%' instead of a mix of one
1700  * and two '%'.
1701  */
1702  /* HACK part 1: Compute the full length of the string array */
1703  cch = 0;
1704  for (i = 0; i < pevlr->NumStrings; i++)
1705  {
1706  szMessage += wcslen(szMessage) + 1;
1707  }
1708  cch = szMessage - szStringArray;
1709 
1710  /* HACK part 2: Now do the HACK proper! */
1711  szMessage = szStringArray;
1712  for (i = 0; i < pevlr->NumStrings; i++)
1713  {
1714  lpMsgBuf = szMessage;
1715  while ((lpMsgBuf = wcsstr(lpMsgBuf, L"%%")))
1716  {
1717  if (iswdigit(lpMsgBuf[2]))
1718  {
1719  RtlMoveMemory(lpMsgBuf, lpMsgBuf+1, ((szStringArray + cch) - lpMsgBuf - 1) * sizeof(WCHAR));
1720  }
1721  }
1722 
1723  szArguments[i] = szMessage;
1724  szMessage += wcslen(szMessage) + 1;
1725  }
1726 
1727  /* Retrieve the message string without appending extra newlines */
1728  lpMsgBuf =
1729  GetMessageStringFromDllList(SourceModuleName,
1732  pevlr->EventID,
1733  0,
1734  (va_list*)szArguments);
1735  if (lpMsgBuf)
1736  {
1737  /* Trim the string */
1738  TrimNulls(lpMsgBuf);
1739 
1740  szMessage = NULL;
1741  Success = (ApplyParameterStringsToMessage(ParameterModuleName,
1742  TRUE,
1743  lpMsgBuf,
1744  &szMessage) == ERROR_SUCCESS);
1745  if (Success && szMessage)
1746  {
1747  /* Free the buffer allocated by FormatMessage */
1748  LocalFree(lpMsgBuf);
1749  lpMsgBuf = szMessage;
1750  }
1751 
1752  /* Copy the event text */
1753  StringCchCopyW(EventText, EVENT_MESSAGE_EVENTTEXT_BUFFER, lpMsgBuf);
1754 
1755  /* Free the buffer allocated by FormatMessage */
1756  LocalFree(lpMsgBuf);
1757  }
1758 
1759  HeapFree(GetProcessHeap(), 0, szArguments);
1760 
1761 Quit:
1762  if (!Success)
1763  {
1764  /* Get a read-only pointer to the "event-not-found" string */
1765  lpMsgBuf = HeapAlloc(GetProcessHeap(), 0, EVENT_MESSAGE_EVENTTEXT_BUFFER * sizeof(WCHAR));
1767  StringCchPrintfW(EventText, EVENT_MESSAGE_EVENTTEXT_BUFFER, lpMsgBuf, (pevlr->EventID & 0xFFFF), SourceName);
1768 
1769  /* Append the strings */
1770  szMessage = szStringArray;
1771  for (i = 0; i < pevlr->NumStrings; i++)
1772  {
1773  StringCchCatW(EventText, EVENT_MESSAGE_EVENTTEXT_BUFFER, szMessage);
1775  szMessage += wcslen(szMessage) + 1;
1776  }
1777  }
1778 
1779  return Success;
1780 }
1781 
1782 VOID
1783 GetEventType(IN WORD dwEventType,
1784  OUT PWCHAR eventTypeText) // TODO: Add IN DWORD BufLen
1785 {
1786  switch (dwEventType)
1787  {
1788  case EVENTLOG_ERROR_TYPE:
1790  break;
1791  case EVENTLOG_WARNING_TYPE:
1793  break;
1796  break;
1797  case EVENTLOG_SUCCESS:
1799  break;
1802  break;
1805  break;
1806  default:
1808  break;
1809  }
1810 }
1811 
1812 BOOL
1814  IN OUT PSID *pLastSid,
1815  OUT PWCHAR pszUser) // TODO: Add IN DWORD BufLen
1816 {
1817  PSID pCurrentSid;
1818  PWSTR StringSid;
1819  WCHAR szName[1024];
1820  WCHAR szDomain[1024];
1823  DWORD cchDomain = ARRAYSIZE(szDomain);
1824  BOOL Success = FALSE;
1825 
1826  /* Point to the SID */
1827  pCurrentSid = (PSID)((LPBYTE)pelr + pelr->UserSidOffset);
1828 
1829  if (!IsValidSid(pCurrentSid))
1830  {
1831  *pLastSid = NULL;
1832  return FALSE;
1833  }
1834  else if (*pLastSid && EqualSid(*pLastSid, pCurrentSid))
1835  {
1836  return TRUE;
1837  }
1838 
1839  /* User SID */
1840  if (pelr->UserSidLength > 0)
1841  {
1842  /*
1843  * Try to retrieve the user account name and domain name corresponding
1844  * to the SID. If it cannot be retrieved, try to convert the SID to a
1845  * string-form. It should not be bigger than the user-provided buffer
1846  * 'pszUser', otherwise we return an error.
1847  */
1849  pCurrentSid,
1850  szName,
1851  &cchName,
1852  szDomain,
1853  &cchDomain,
1854  &peUse))
1855  {
1856  StringCchCopyW(pszUser, MAX_PATH, szName);
1857  Success = TRUE;
1858  }
1859  else if (ConvertSidToStringSidW(pCurrentSid, &StringSid))
1860  {
1861  /* Copy the string only if the user-provided buffer is big enough */
1862  if (wcslen(StringSid) + 1 <= MAX_PATH) // + 1 for NULL-terminator
1863  {
1864  StringCchCopyW(pszUser, MAX_PATH, StringSid);
1865  Success = TRUE;
1866  }
1867  else
1868  {
1870  Success = FALSE;
1871  }
1872 
1873  /* Free the allocated buffer */
1874  LocalFree(StringSid);
1875  }
1876  }
1877 
1878  *pLastSid = Success ? pCurrentSid : NULL;
1879 
1880  return Success;
1881 }
1882 
1883 
1885 {
1886  DWORD iIndex;
1887 
1888  if (!g_RecordPtrs)
1889  return;
1890 
1891  for (iIndex = 0; iIndex < g_TotalRecords; iIndex++)
1892  {
1893  if (g_RecordPtrs[iIndex])
1894  HeapFree(GetProcessHeap(), 0, g_RecordPtrs[iIndex]);
1895  }
1897  g_RecordPtrs = NULL;
1898  g_TotalRecords = 0;
1899 }
1900 
1901 BOOL
1903  IN PEVENTLOGRECORD pevlr)
1904 {
1905  if ((pevlr->EventType == EVENTLOG_SUCCESS && !EventLogFilter->Information ) ||
1906  (pevlr->EventType == EVENTLOG_INFORMATION_TYPE && !EventLogFilter->Information ) ||
1907  (pevlr->EventType == EVENTLOG_WARNING_TYPE && !EventLogFilter->Warning ) ||
1908  (pevlr->EventType == EVENTLOG_ERROR_TYPE && !EventLogFilter->Error ) ||
1909  (pevlr->EventType == EVENTLOG_AUDIT_SUCCESS && !EventLogFilter->AuditSuccess) ||
1910  (pevlr->EventType == EVENTLOG_AUDIT_FAILURE && !EventLogFilter->AuditFailure))
1911  {
1912  return FALSE;
1913  }
1914  return TRUE;
1915 }
1916 
1917 BOOL
1918 FilterByString(IN PCWSTR FilterString, // This is a multi-string
1919  IN PWSTR String)
1920 {
1921  PCWSTR pStr;
1922 
1923  /* The filter string is NULL so it does not filter anything */
1924  if (!FilterString)
1925  return TRUE;
1926 
1927  /*
1928  * If the filter string filters for an empty string AND the source string
1929  * is an empty string, we have a match (particular case of the last one).
1930  */
1931  if (!*FilterString && !*String)
1932  return TRUE;
1933 
1934  // if (*FilterString || *String)
1935 
1936  /*
1937  * If the filter string is empty BUT the source string is not empty,
1938  * OR vice-versa, we cannot have a match.
1939  */
1940  if ( (!*FilterString && *String) || (*FilterString && !*String) )
1941  return FALSE;
1942 
1943  /*
1944  * If the filter string filters for at least a non-empty string,
1945  * browse it and search for a string that matches the source string.
1946  */
1947  // else if (*FilterString && *String)
1948  {
1949  pStr = FilterString;
1950  while (*pStr)
1951  {
1952  if (wcsicmp(pStr, String) == 0)
1953  {
1954  /* We have a match, break the loop */
1955  break;
1956  }
1957 
1958  pStr += (wcslen(pStr) + 1);
1959  }
1960  if (!*pStr) // && *String
1961  {
1962  /* We do not have a match */
1963  return FALSE;
1964  }
1965  }
1966 
1967  /* We have a match */
1968  return TRUE;
1969 }
1970 
1971 /*
1972  * The events enumerator thread.
1973  */
1974 static DWORD WINAPI
1976 {
1977  PEVENTLOGFILTER EventLogFilter = (PEVENTLOGFILTER)lpParameter;
1978  PEVENTLOG EventLog;
1979 
1980  ULONG LogIndex;
1981  HANDLE hEventLog;
1982  PEVENTLOGRECORD pEvlr = NULL;
1983  PBYTE pEvlrEnd;
1984  PBYTE pEvlrBuffer;
1985  DWORD dwWanted, dwRead, dwNeeded, dwStatus = ERROR_SUCCESS;
1986  DWORD dwTotalRecords = 0, dwCurrentRecord = 0;
1987  DWORD dwFlags, dwMaxLength;
1988  size_t cchRemaining;
1989  LPWSTR lpszSourceName;
1990  LPWSTR lpszComputerName;
1991  BOOL bResult = TRUE; /* Read succeeded */
1993  PSID pLastSid = NULL;
1994 
1995  UINT uStep = 0, uStepAt = 0, uPos = 0;
1996 
1997  WCHAR szWindowTitle[MAX_PATH];
1998  WCHAR szStatusText[MAX_PATH];
1999  WCHAR szLocalDate[MAX_PATH];
2000  WCHAR szLocalTime[MAX_PATH];
2001  WCHAR szEventID[MAX_PATH];
2002  WCHAR szEventTypeText[MAX_LOADSTRING];
2003  WCHAR szCategoryID[MAX_PATH];
2004  WCHAR szUsername[MAX_PATH];
2005  WCHAR szNoUsername[MAX_PATH];
2006  WCHAR szCategory[MAX_PATH];
2007  WCHAR szNoCategory[MAX_PATH];
2008  PWCHAR lpTitleTemplateEnd;
2009 
2010  SYSTEMTIME time;
2011  LVITEMW lviEventItem;
2012 
2013  /* Save the current event log filter globally */
2014  EventLogFilter_AddRef(EventLogFilter);
2015  ActiveFilter = EventLogFilter;
2016 
2017 
2019  EventLog = EventLogFilter->EventLogs[0];
2020 
2021  // FIXME: Use something else instead of EventLog->LogName !!
2022 
2023  /*
2024  * Use a different formatting, whether the event log filter holds
2025  * only one log, or many logs (the latter case is WIP TODO!)
2026  */
2027  if (EventLogFilter->NumOfEventLogs <= 1)
2028  {
2029  StringCchPrintfExW(szWindowTitle,
2030  ARRAYSIZE(szWindowTitle),
2031  &lpTitleTemplateEnd,
2032  &cchRemaining,
2033  0,
2034  szTitleTemplate, szTitle, EventLog->LogName); /* i = number of characters written */
2035  dwMaxLength = (DWORD)cchRemaining;
2036  if (!EventLog->ComputerName)
2037  GetComputerNameW(lpTitleTemplateEnd, &dwMaxLength);
2038  else
2039  StringCchCopyW(lpTitleTemplateEnd, dwMaxLength, EventLog->ComputerName);
2040 
2041  StringCbPrintfW(szStatusText,
2042  sizeof(szStatusText),
2044  EventLog->LogName,
2045  0,
2046  0);
2047  }
2048  else
2049  {
2050  // TODO: Use a different title & implement filtering for multi-log filters !!
2051  // (EventLogFilter->NumOfEventLogs > 1)
2053  L"Many-logs filtering is not implemented yet!!",
2054  L"Event Log",
2056  }
2057 
2058  /* Set the window title */
2059  SetWindowTextW(hwndMainWindow, szWindowTitle);
2060 
2061  /* Update the status bar */
2062  StatusBar_SetText(hwndStatus, 0, szStatusText);
2063 
2064 
2065  /* Disable list view redraw */
2067 
2068  /* Clear the list view and free the cached records */
2070  FreeRecords();
2071 
2076 
2077  /* Do a loop over the logs enumerated in the filter */
2078  // FIXME: For now we only support 1 event log per filter!
2079  LogIndex = 0;
2080  // for (LogIndex = 0; LogIndex < EventLogFilter->NumOfEventLogs; ++LogIndex)
2081  {
2082 
2083  EventLog = EventLogFilter->EventLogs[LogIndex];
2084 
2085  /* Open the event log */
2086  if (EventLog->Permanent)
2087  hEventLog = OpenEventLogW(EventLog->ComputerName, EventLog->LogName);
2088  else
2089  hEventLog = OpenBackupEventLogW(EventLog->ComputerName, EventLog->LogName); // FileName
2090 
2091  if (hEventLog == NULL)
2092  {
2094  goto Cleanup;
2095  }
2096 
2097  // GetOldestEventLogRecord(hEventLog, &dwThisRecord);
2098 
2099  /* Get the total number of event log records */
2100  GetNumberOfEventLogRecords(hEventLog, &dwTotalRecords);
2101 
2102  if (dwTotalRecords > 0)
2103  {
2106  }
2107  else
2108  {
2111  }
2112 
2113  /* Set up the event records cache */
2114  g_RecordPtrs = HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, dwTotalRecords * sizeof(*g_RecordPtrs));
2115  if (!g_RecordPtrs)
2116  {
2117  // ShowWin32Error(GetLastError());
2118  goto Quit;
2119  }
2120  g_TotalRecords = dwTotalRecords;
2121 
2123  goto Quit;
2124 
2125  LoadStringW(hInst, IDS_NOT_AVAILABLE, szNoUsername, ARRAYSIZE(szNoUsername));
2126  LoadStringW(hInst, IDS_NONE, szNoCategory, ARRAYSIZE(szNoCategory));
2127 
2129  uStepAt = (dwTotalRecords / 100) + 1;
2130 
2132 
2133  /* 0x7ffff is the maximum buffer size ReadEventLog will accept */
2134  dwWanted = 0x7ffff;
2135  pEvlr = HeapAlloc(hProcessHeap, 0, dwWanted);
2136 
2137  if (!pEvlr)
2138  goto Quit;
2139 
2140  while (dwStatus == ERROR_SUCCESS)
2141  {
2142  bResult = ReadEventLogW(hEventLog, dwFlags, 0, pEvlr, dwWanted, &dwRead, &dwNeeded);
2143  dwStatus = GetLastError();
2144 
2145  if (!bResult && dwStatus == ERROR_INSUFFICIENT_BUFFER)
2146  {
2147  pEvlr = HeapReAlloc(hProcessHeap, 0, pEvlr, dwNeeded);
2148  dwWanted = dwNeeded;
2149 
2150  if (!pEvlr)
2151  break;
2152 
2153  bResult = ReadEventLogW(hEventLog, dwFlags, 0, pEvlr, dwNeeded, &dwRead, &dwNeeded);
2154 
2155  if (!bResult)
2156  break;
2157  }
2158  else if (!bResult)
2159  {
2160  /* Exit on other errors (ERROR_HANDLE_EOF) */
2161  break;
2162  }
2163 
2164  pEvlrBuffer = (LPBYTE)pEvlr;
2165  pEvlrEnd = pEvlrBuffer + dwRead;
2166 
2167  while (pEvlrBuffer < pEvlrEnd)
2168  {
2169  PEVENTLOGRECORD pEvlrTmp = (PEVENTLOGRECORD)pEvlrBuffer;
2170  PWSTR lpszUsername, lpszCategoryName;
2171  g_RecordPtrs[dwCurrentRecord] = NULL;
2172 
2173  // ProgressBar_StepIt(hwndStatusProgress);
2174  uStep++;
2175  if (uStep % uStepAt == 0)
2176  {
2177  ++uPos;
2179  }
2180 
2182  goto Quit;
2183 
2184  /* Filter by event type */
2185  if (!FilterByType(EventLogFilter, pEvlrTmp))
2186  goto SkipEvent;
2187 
2188  /* Get the event source name and filter it */
2189  lpszSourceName = (LPWSTR)(pEvlrBuffer + sizeof(EVENTLOGRECORD));
2190  if (!FilterByString(EventLogFilter->Sources, lpszSourceName))
2191  goto SkipEvent;
2192 
2193  /* Get the computer name and filter it */
2194  lpszComputerName = (LPWSTR)(pEvlrBuffer + sizeof(EVENTLOGRECORD) + (wcslen(lpszSourceName) + 1) * sizeof(WCHAR));
2195  if (!FilterByString(EventLogFilter->ComputerNames, lpszComputerName))
2196  goto SkipEvent;
2197 
2198  /* Compute the event time */
2199  EventTimeToSystemTime(pEvlrTmp->TimeWritten, &time);
2200  GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time, NULL, szLocalDate, ARRAYSIZE(szLocalDate));
2201  GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &time, NULL, szLocalTime, ARRAYSIZE(szLocalTime));
2202 
2203  /* Get the username that generated the event, and filter it */
2204  lpszUsername = GetEventUserName(pEvlrTmp, &pLastSid, szUsername) ? szUsername : szNoUsername;
2205 
2206  if (!FilterByString(EventLogFilter->Users, lpszUsername))
2207  goto SkipEvent;
2208 
2209  // TODO: Filter by event ID and category
2210  GetEventType(pEvlrTmp->EventType, szEventTypeText);
2211 
2212  lpszCategoryName = GetEventCategory(EventLog->LogName, lpszSourceName, pEvlrTmp, szCategory) ? szCategory : szNoCategory;
2213 
2214  StringCbPrintfW(szEventID, sizeof(szEventID), L"%u", (pEvlrTmp->EventID & 0xFFFF));
2215  StringCbPrintfW(szCategoryID, sizeof(szCategoryID), L"%u", pEvlrTmp->EventCategory);
2216 
2217  g_RecordPtrs[dwCurrentRecord] = HeapAlloc(hProcessHeap, 0, pEvlrTmp->Length);
2218  RtlCopyMemory(g_RecordPtrs[dwCurrentRecord], pEvlrTmp, pEvlrTmp->Length);
2219 
2220  lviEventItem.mask = LVIF_IMAGE | LVIF_TEXT | LVIF_PARAM;
2221  lviEventItem.iItem = 0;
2222  lviEventItem.iSubItem = 0;
2223  lviEventItem.lParam = (LPARAM)g_RecordPtrs[dwCurrentRecord];
2224  lviEventItem.pszText = szEventTypeText;
2225 
2226  switch (pEvlrTmp->EventType)
2227  {
2228  case EVENTLOG_SUCCESS:
2230  lviEventItem.iImage = 0;
2231  break;
2232 
2233  case EVENTLOG_WARNING_TYPE:
2234  lviEventItem.iImage = 1;
2235  break;
2236 
2237  case EVENTLOG_ERROR_TYPE:
2238  lviEventItem.iImage = 2;
2239  break;
2240 
2242  lviEventItem.iImage = 3;
2243  break;
2244 
2246  lviEventItem.iImage = 4;
2247  break;
2248  }
2249 
2250  lviEventItem.iItem = ListView_InsertItem(hwndListView, &lviEventItem);
2251 
2252  ListView_SetItemText(hwndListView, lviEventItem.iItem, 1, szLocalDate);
2253  ListView_SetItemText(hwndListView, lviEventItem.iItem, 2, szLocalTime);
2254  ListView_SetItemText(hwndListView, lviEventItem.iItem, 3, lpszSourceName);
2255  ListView_SetItemText(hwndListView, lviEventItem.iItem, 4, lpszCategoryName);
2256  ListView_SetItemText(hwndListView, lviEventItem.iItem, 5, szEventID);
2257  ListView_SetItemText(hwndListView, lviEventItem.iItem, 6, lpszUsername);
2258  ListView_SetItemText(hwndListView, lviEventItem.iItem, 7, lpszComputerName);
2259 
2260 SkipEvent:
2261  pEvlrBuffer += pEvlrTmp->Length;
2262  dwCurrentRecord++;
2263  }
2264  }
2265 
2266 Quit:
2267 
2268  if (pEvlr)
2269  HeapFree(hProcessHeap, 0, pEvlr);
2270 
2271  /* Close the event log */
2272  CloseEventLog(hEventLog);
2273 
2274  } // end-for (LogIndex)
2275 
2276  /* All events loaded */
2277 
2278 Cleanup:
2279 
2282 
2283  // FIXME: Use something else instead of EventLog->LogName !!
2284 
2285  /*
2286  * Use a different formatting, whether the event log filter holds
2287  * only one log, or many logs (the latter case is WIP TODO!)
2288  */
2289  if (EventLogFilter->NumOfEventLogs <= 1)
2290  {
2291  StringCbPrintfW(szStatusText,
2292  sizeof(szStatusText),
2294  EventLog->LogName,
2295  dwTotalRecords,
2297  }
2298  else
2299  {
2300  // TODO: Use a different title & implement filtering for multi-log filters !!
2301  // (EventLogFilter->NumOfEventLogs > 1)
2302  }
2303 
2304  /* Update the status bar */
2305  StatusBar_SetText(hwndStatus, 0, szStatusText);
2306 
2307  /* Resume list view redraw */
2309 
2310  EventLogFilter_Release(EventLogFilter);
2311 
2314 
2315  return 0;
2316 }
2317 
2318 /*
2319  * The purpose of this thread is to serialize the creation of the events
2320  * enumeration thread, since the Event Log Viewer currently only supports
2321  * one view, one event list, one enumeration.
2322  */
2323 static DWORD WINAPI
2325 {
2326  HANDLE WaitHandles[2];
2327  DWORD WaitResult;
2328 
2329  WaitHandles[0] = hStartStopEnumEvent; // End-of-application event
2330  WaitHandles[1] = hStartEnumEvent; // Command event
2331 
2332  while (TRUE)
2333  {
2334  WaitResult = WaitForMultipleObjects(ARRAYSIZE(WaitHandles),
2335  WaitHandles,
2336  FALSE, // WaitAny
2337  INFINITE);
2338  switch (WaitResult)
2339  {
2340  case WAIT_OBJECT_0 + 0:
2341  {
2342  /* End-of-application event signaled, quit this thread */
2343 
2344  /* Stop the previous enumeration */
2345  if (hEnumEventsThread)
2346  {
2347  if (hStopEnumEvent)
2348  {
2351  // NOTE: The following is done by the enumeration thread just before terminating.
2352  // hStopEnumEvent = NULL;
2353  }
2354 
2357  }
2358 
2359  /* Clear the list view and free the cached records */
2361  FreeRecords();
2362 
2363  /* Reset the active filter */
2364  ActiveFilter = NULL;
2365 
2366  return 0;
2367  }
2368 
2369  case WAIT_OBJECT_0 + 1:
2370  {
2371  /* Restart a new enumeration if needed */
2372  PEVENTLOGFILTER EventLogFilter;
2373 
2374  /* Stop the previous enumeration */
2375  if (hEnumEventsThread)
2376  {
2377  if (hStopEnumEvent)
2378  {
2381  // NOTE: The following is done by the enumeration thread just before terminating.
2382  // hStopEnumEvent = NULL;
2383  }
2384 
2387  }
2388 
2389  /* Clear the list view and free the cached records */
2391  FreeRecords();
2392 
2393  /* Reset the active filter */
2394  ActiveFilter = NULL;
2395 
2396  EventLogFilter = InterlockedExchangePointer((PVOID*)&EnumFilter, NULL);
2397  if (!EventLogFilter)
2398  break;
2399 
2400  // Manual-reset event
2402  if (!hStopEnumEvent)
2403  break;
2404 
2406  0,
2408  (LPVOID)EventLogFilter,
2410  NULL);
2411  if (!hEnumEventsThread)
2412  {
2414  hStopEnumEvent = NULL;
2415  break;
2416  }
2417  // CloseHandle(hEnumEventsThread);
2419 
2420  break;
2421  }
2422 
2423  default:
2424  {
2425  /* Unknown command, must never go there! */
2426  return GetLastError();
2427  }
2428  }
2429  }
2430 
2431  return 0;
2432 }
2433 
2434 VOID
2436 {
2437  /* Signal the enumerator thread we want to enumerate events */
2438  InterlockedExchangePointer((PVOID*)&EnumFilter, EventLogFilter);
2440  return;
2441 }
2442 
2443 
2446 {
2447  TVITEMEXW tvItemEx;
2448  HTREEITEM hti;
2449 
2450  if (phti)
2451  *phti = NULL;
2452 
2453  /* Get index of selected item */
2455  if (hti == NULL)
2456  return NULL; // No filter
2457 
2458  tvItemEx.mask = TVIF_PARAM;
2459  tvItemEx.hItem = hti;
2460 
2461  TreeView_GetItem(hwndTreeView, &tvItemEx);
2462 
2463  if (phti)
2464  *phti = tvItemEx.hItem;
2465 
2466  return (PEVENTLOGFILTER)tvItemEx.lParam;
2467 }
2468 
2469 
2470 VOID
2472 {
2473  WIN32_FIND_DATAW FindData;
2474  HANDLE hFind;
2475  PEVENTLOG EventLog;
2476  PEVENTLOGFILTER EventLogFilter;
2477  SIZE_T cchFileName;
2478  HTREEITEM hItem = NULL;
2479 
2480  /* Check whether the file actually exists */
2481  hFind = FindFirstFileW(lpszFileName, &FindData);
2482  if (hFind == INVALID_HANDLE_VALUE)
2483  {
2485  return;
2486  }
2487  FindClose(hFind);
2488 
2489  /* Allocate a new event log entry */
2490  EventLog = AllocEventLog(NULL, lpszFileName, FALSE);
2491  if (EventLog == NULL)
2492  {
2494  return;
2495  }
2496 
2497  /* Allocate a new event log filter entry for this event log */
2498  EventLogFilter = AllocEventLogFilter(// LogName,
2499  TRUE, TRUE, TRUE, TRUE, TRUE,
2500  NULL, NULL, NULL,
2501  1, &EventLog);
2502  if (EventLogFilter == NULL)
2503  {
2505  EventLog_Free(EventLog);
2506  return;
2507  }
2508 
2509  /* Add the event log and the filter into their lists */
2510  InsertTailList(&EventLogList, &EventLog->ListEntry);
2511  InsertTailList(&EventLogFilterList, &EventLogFilter->ListEntry);
2512 
2513  /* Retrieve and cache the event log file */
2514  cchFileName = wcslen(lpszFileName) + 1;
2515  EventLog->FileName = HeapAlloc(GetProcessHeap(), 0, cchFileName * sizeof(WCHAR));
2516  if (EventLog->FileName)
2517  StringCchCopyW(EventLog->FileName, cchFileName, lpszFileName);
2518 
2520  (LPWSTR)lpszFileName,
2521  2, 3, (LPARAM)EventLogFilter);
2522 
2523  /* Select the event log */
2524  if (hItem)
2525  {
2526  // TreeView_Expand(hwndTreeView, htiUserLogs, TVE_EXPAND);
2529  }
2532 }
2533 
2534 VOID
2536 {
2537  WCHAR szFileName[MAX_PATH];
2538 
2539  ZeroMemory(szFileName, sizeof(szFileName));
2540 
2541  sfn.lpstrFile = szFileName;
2542  sfn.nMaxFile = ARRAYSIZE(szFileName);
2543 
2544  if (!GetOpenFileNameW(&sfn))
2545  return;
2547 
2549 }
2550 
2551 VOID
2553 {
2554  PEVENTLOG EventLog;
2555  HANDLE hEventLog;
2556  WCHAR szFileName[MAX_PATH];
2557 
2558  /* Bail out if there is no available filter */
2559  if (!EventLogFilter)
2560  return;
2561 
2562  ZeroMemory(szFileName, sizeof(szFileName));
2563 
2564  sfn.lpstrFile = szFileName;
2565  sfn.nMaxFile = ARRAYSIZE(szFileName);
2566 
2567  if (!GetSaveFileNameW(&sfn))
2568  return;
2569 
2570  EventLogFilter_AddRef(EventLogFilter);
2571 
2572  EventLog = EventLogFilter->EventLogs[0];
2573  hEventLog = OpenEventLogW(EventLog->ComputerName, EventLog->LogName);
2574 
2575  EventLogFilter_Release(EventLogFilter);
2576 
2577  if (!hEventLog)
2578  {
2580  return;
2581  }
2582 
2583  if (!BackupEventLogW(hEventLog, szFileName))
2585 
2586  CloseEventLog(hEventLog);
2587 }
2588 
2589 VOID
2591 {
2592  /* Bail out if there is no available filter */
2593  if (!EventLogFilter)
2594  return;
2595 
2596  if (InterlockedCompareExchangePointer((PVOID*)&ActiveFilter, NULL, NULL) == EventLogFilter)
2597  {
2598  /* Signal the enumerator thread we want to stop enumerating events */
2599  // EnumEvents(NULL);
2602  }
2603 
2604  /*
2605  * The deletion of the item automatically triggers a TVN_SELCHANGED
2606  * notification, that will reset the ActiveFilter (in case the item
2607  * selected is a filter). Otherwise we reset it there.
2608  */
2610 
2611  /* Remove the filter from the list */
2612  RemoveEntryList(&EventLogFilter->ListEntry);
2613  EventLogFilter_Release(EventLogFilter);
2614 
2615  // /* Select the default event log */
2616  // // TreeView_Expand(hwndTreeView, htiUserLogs, TVE_EXPAND);
2617  // TreeView_SelectItem(hwndTreeView, hItem);
2618  // TreeView_EnsureVisible(hwndTreeView, hItem);
2621 }
2622 
2623 
2624 BOOL
2626 {
2627  BOOL Success;
2628  PEVENTLOG EventLog;
2629  HANDLE hEventLog;
2630  WCHAR szFileName[MAX_PATH];
2631  WCHAR szMessage[MAX_LOADSTRING];
2632 
2633  /* Bail out if there is no available filter */
2634  if (!EventLogFilter)
2635  return FALSE;
2636 
2637  ZeroMemory(szFileName, sizeof(szFileName));
2638  ZeroMemory(szMessage, sizeof(szMessage));
2639 
2640  LoadStringW(hInst, IDS_CLEAREVENTS_MSG, szMessage, ARRAYSIZE(szMessage));
2641 
2642  sfn.lpstrFile = szFileName;
2643  sfn.nMaxFile = ARRAYSIZE(szFileName);
2644 
2646  {
2647  case IDCANCEL:
2648  return FALSE;
2649 
2650  case IDNO:
2651  sfn.lpstrFile = NULL;
2652  break;
2653 
2654  case IDYES:
2655  if (!GetSaveFileNameW(&sfn))
2656  return FALSE;
2657  break;
2658  }
2659 
2660  EventLogFilter_AddRef(EventLogFilter);
2661 
2662  EventLog = EventLogFilter->EventLogs[0];
2663  hEventLog = OpenEventLogW(EventLog->ComputerName, EventLog->LogName);
2664 
2665  EventLogFilter_Release(EventLogFilter);
2666 
2667  if (!hEventLog)
2668  {
2670  return FALSE;
2671  }
2672 
2673  Success = ClearEventLogW(hEventLog, sfn.lpstrFile);
2674  if (!Success)
2676 
2677  CloseEventLog(hEventLog);
2678  return Success;
2679 }
2680 
2681 
2682 VOID
2683 Refresh(IN PEVENTLOGFILTER EventLogFilter)
2684 {
2685  /* Bail out if there is no available filter */
2686  if (!EventLogFilter)
2687  return;
2688 
2689  /* Reenumerate the events through the filter */
2690  EnumEvents(EventLogFilter);
2691 }
2692 
2693 
2694 ATOM
2696 {
2697  WNDCLASSEXW wcex;
2698 
2699  wcex.cbSize = sizeof(wcex);
2700  wcex.style = 0;
2701  wcex.lpfnWndProc = WndProc;
2702  wcex.cbClsExtra = 0;
2703  wcex.cbWndExtra = 0;
2704  wcex.hInstance = hInstance;
2707  wcex.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); // COLOR_WINDOW + 1
2712  IMAGE_ICON,
2713  16,
2714  16,
2715  LR_SHARED);
2716 
2717  return RegisterClassExW(&wcex);
2718 }
2719 
2720 
2721 BOOL
2723  OUT PWCHAR lpModuleName, // TODO: Add IN DWORD BufLen
2724  OUT PDWORD pdwMessageID)
2725 {
2726  BOOL Success = FALSE;
2727  LONG Result;
2728  HKEY hLogKey;
2729  WCHAR *KeyPath;
2730  SIZE_T cbKeyPath;
2731  DWORD dwType, cbData;
2732  DWORD dwMessageID = 0;
2734 
2735  /* Use a default value for the message ID */
2736  *pdwMessageID = 0;
2737 
2738  cbKeyPath = (wcslen(EVENTLOG_BASE_KEY) + wcslen(lpLogName) + 1) * sizeof(WCHAR);
2739  KeyPath = HeapAlloc(GetProcessHeap(), 0, cbKeyPath);
2740  if (!KeyPath)
2741  {
2743  return FALSE;
2744  }
2745 
2746  StringCbCopyW(KeyPath, cbKeyPath, EVENTLOG_BASE_KEY);
2747  StringCbCatW(KeyPath, cbKeyPath, lpLogName);
2748 
2749  Result = RegOpenKeyExW(hkMachine, KeyPath, 0, KEY_QUERY_VALUE, &hLogKey);
2750  HeapFree(GetProcessHeap(), 0, KeyPath);
2751  if (Result != ERROR_SUCCESS)
2752  {
2754  return FALSE;
2755  }
2756 
2757  cbData = sizeof(szModuleName);
2758  Result = RegQueryValueExW(hLogKey,
2759  L"DisplayNameFile",
2760  NULL,
2761  &dwType,
2763  &cbData);
2764  if ((Result != ERROR_SUCCESS) || (dwType != REG_EXPAND_SZ && dwType != REG_SZ))
2765  {
2767  }
2768  else
2769  {
2770  /* NULL-terminate the string and expand it */
2771  szModuleName[cbData / sizeof(WCHAR) - 1] = UNICODE_NULL;
2773  Success = TRUE;
2774  }
2775 
2776  /*
2777  * If we have a 'DisplayNameFile', query for 'DisplayNameID';
2778  * otherwise it's not really useful. 'DisplayNameID' is optional.
2779  */
2780  if (Success)
2781  {
2782  cbData = sizeof(dwMessageID);
2783  Result = RegQueryValueExW(hLogKey,
2784  L"DisplayNameID",
2785  NULL,
2786  &dwType,
2787  (LPBYTE)&dwMessageID,
2788  &cbData);
2789  if ((Result != ERROR_SUCCESS) || (dwType != REG_DWORD))
2790  dwMessageID = 0;
2791 
2792  *pdwMessageID = dwMessageID;
2793  }
2794 
2795  RegCloseKey(hLogKey);
2796 
2797  return Success;
2798 }
2799 
2800 
2801 VOID
2803 {
2804  LONG Result;
2805  HKEY hEventLogKey, hLogKey;
2806  DWORD dwNumLogs = 0;
2807  DWORD dwIndex, dwMaxKeyLength;
2808  DWORD dwType;
2809  PEVENTLOG EventLog;
2810  PEVENTLOGFILTER EventLogFilter;
2811  LPWSTR LogName = NULL;
2813  DWORD lpcName;
2814  DWORD dwMessageID;
2816  HTREEITEM hRootNode = NULL, hItem = NULL, hItemDefault = NULL;
2817 
2819  {
2820  /* We are connected to some other computer, close the old connection */
2822  hkMachine = NULL;
2823  }
2824  if (!lpComputerName || !*lpComputerName)
2825  {
2826  /* Use the local computer registry */
2828  }
2829  else
2830  {
2831  /* Connect to the remote computer registry */
2833  if (Result != ERROR_SUCCESS)
2834  {
2835  /* Connection failed, display a message and bail out */
2836  hkMachine = NULL;
2838  return;
2839  }
2840  }
2841 
2842  /* Open the EventLog key */
2843  Result = RegOpenKeyExW(hkMachine, EVENTLOG_BASE_KEY, 0, KEY_READ, &hEventLogKey);
2844  if (Result != ERROR_SUCCESS)
2845  {
2846  return;
2847  }
2848 
2849  /* Retrieve the number of event logs enumerated as registry keys */
2850  Result = RegQueryInfoKeyW(hEventLogKey, NULL, NULL, NULL, &dwNumLogs, &dwMaxKeyLength,
2851  NULL, NULL, NULL, NULL, NULL, NULL);
2852  if (Result != ERROR_SUCCESS)
2853  {
2854  goto Quit;
2855  }
2856  if (!dwNumLogs)
2857  goto Quit;
2858 
2859  /* Take the NULL terminator into account */
2860  ++dwMaxKeyLength;
2861 
2862  /* Allocate the temporary buffer */
2863  LogName = HeapAlloc(GetProcessHeap(), 0, dwMaxKeyLength * sizeof(WCHAR));
2864  if (!LogName)
2865  goto Quit;
2866 
2867  /* Enumerate and retrieve each event log name */
2868  for (dwIndex = 0; dwIndex < dwNumLogs; dwIndex++)
2869  {
2870  lpcName = dwMaxKeyLength;
2871  Result = RegEnumKeyExW(hEventLogKey, dwIndex, LogName, &lpcName, NULL, NULL, NULL, NULL);
2872  if (Result != ERROR_SUCCESS)
2873  continue;
2874 
2875  /* Take the NULL terminator into account */
2876  ++lpcName;
2877 
2878  /* Allocate a new event log entry */
2879  EventLog = AllocEventLog(lpComputerName, LogName, TRUE);
2880  if (EventLog == NULL)
2881  continue;
2882 
2883  /* Allocate a new event log filter entry for this event log */
2884  EventLogFilter = AllocEventLogFilter(// LogName,
2885  TRUE, TRUE, TRUE, TRUE, TRUE,
2886  NULL, NULL, NULL,
2887  1, &EventLog);
2888  if (EventLogFilter == NULL)
2889  {
2890  EventLog_Free(EventLog);
2891  continue;
2892  }
2893 
2894  /* Add the event log and the filter into their lists */
2895  InsertTailList(&EventLogList, &EventLog->ListEntry);
2896  InsertTailList(&EventLogFilterList, &EventLogFilter->ListEntry);
2897 
2898  EventLog->FileName = NULL;
2899 
2900  /* Retrieve and cache the event log file */
2901  Result = RegOpenKeyExW(hEventLogKey,
2902  LogName,
2903  0,
2905  &hLogKey);
2906  if (Result == ERROR_SUCCESS)
2907  {
2908  lpcName = 0;
2909  Result = RegQueryValueExW(hLogKey,
2910  L"File",
2911  NULL,
2912  &dwType,
2913  NULL,
2914  &lpcName);
2915  if ((Result != ERROR_SUCCESS) || (dwType != REG_EXPAND_SZ && dwType != REG_SZ))
2916  {
2917  // Windows' EventLog uses some kind of default value, we do not.
2918  EventLog->FileName = NULL;
2919  }
2920  else
2921  {
2922  lpcName = ROUND_DOWN(lpcName, sizeof(WCHAR));
2923  EventLog->FileName = HeapAlloc(GetProcessHeap(), 0, lpcName);
2924  if (EventLog->FileName)
2925  {
2926  Result = RegQueryValueExW(hLogKey,
2927  L"File",
2928  NULL,
2929  &dwType,
2930  (LPBYTE)EventLog->FileName,
2931  &lpcName);
2932  if (Result != ERROR_SUCCESS)
2933  {
2934  HeapFree(GetProcessHeap(), 0, EventLog->FileName);
2935  EventLog->FileName = NULL;
2936  }
2937  else
2938  {
2939  EventLog->FileName[lpcName / sizeof(WCHAR) - 1] = UNICODE_NULL;
2940  }
2941  }
2942  }
2943 
2944  RegCloseKey(hLogKey);
2945  }
2946 
2947  /* Get the display name for the event log */
2948  lpDisplayName = NULL;
2949 
2951  if (GetDisplayNameFileAndID(LogName, szModuleName, &dwMessageID))
2952  {
2953  /* Retrieve the message string without appending extra newlines */
2954  lpDisplayName =
2958  dwMessageID,
2959  0,
2960  NULL);
2961  }
2962 
2963  /*
2964  * Select the correct tree root node, whether the log is a System
2965  * or an Application log. Default to Application log otherwise.
2966  */
2967  hRootNode = htiAppLogs;
2968  for (lpcName = 0; lpcName < ARRAYSIZE(SystemLogs); ++lpcName)
2969  {
2970  /* Check whether the log name is part of the system logs */
2971  if (wcsicmp(LogName, SystemLogs[lpcName]) == 0)
2972  {
2973  hRootNode = htiSystemLogs;
2974  break;
2975  }
2976  }
2977 
2978  hItem = TreeViewAddItem(hwndTreeView, hRootNode,
2979  (lpDisplayName ? lpDisplayName : LogName),
2980  2, 3, (LPARAM)EventLogFilter);
2981 
2982  /* Try to get the default event log: "Application" */
2983  if ((hItemDefault == NULL) && (wcsicmp(LogName, SystemLogs[0]) == 0))
2984  {
2985  hItemDefault = hItem;
2986  }
2987 
2988  /* Free the buffer allocated by FormatMessage */
2989  if (lpDisplayName)
2991  }
2992 
2993  HeapFree(GetProcessHeap(), 0, LogName);
2994 
2995 Quit:
2996  RegCloseKey(hEventLogKey);
2997 
2998  /* Select the default event log */
2999  if (hItemDefault)
3000  {
3001  // TreeView_Expand(hwndTreeView, hRootNode, TVE_EXPAND);
3002  TreeView_SelectItem(hwndTreeView, hItemDefault);
3003  TreeView_EnsureVisible(hwndTreeView, hItemDefault);
3004  }
3007 
3008  return;
3009 }
3010 
3011 VOID
3013 {
3015  PEVENTLOG EventLog;
3016 
3017  while (!IsListEmpty(&EventLogList))
3018  {
3020  EventLog = (PEVENTLOG)CONTAINING_RECORD(Entry, EVENTLOG, ListEntry);
3021  EventLog_Free(EventLog);
3022  }
3023 
3024  return;
3025 }
3026 
3027 VOID
3029 {
3031  PEVENTLOGFILTER EventLogFilter;
3032 
3033  while (!IsListEmpty(&EventLogFilterList))
3034  {
3036  EventLogFilter = (PEVENTLOGFILTER)CONTAINING_RECORD(Entry, EVENTLOGFILTER, ListEntry);
3037  EventLogFilter_Free(EventLogFilter);
3038  }
3039 
3040  ActiveFilter = NULL;
3041 
3042  return;
3043 }
3044 
3045 BOOL
3047 {
3048  RECT rcClient, rs;
3049  LONG StatusHeight;
3050  HIMAGELIST hSmall;
3051  LVCOLUMNW lvc = {0};
3052  WCHAR szTemp[256];
3053 
3054  /* Create the main window */
3055  rs = Settings.wpPos.rcNormalPosition;
3057  szTitle,
3059  rs.left, rs.top,
3060  (rs.right != CW_USEDEFAULT && rs.left != CW_USEDEFAULT) ? rs.right - rs.left : CW_USEDEFAULT,
3061  (rs.bottom != CW_USEDEFAULT && rs.top != CW_USEDEFAULT) ? rs.bottom - rs.top : CW_USEDEFAULT,
3062  NULL,
3063  NULL,
3064  hInstance,
3065  NULL);
3066  if (!hwndMainWindow)
3067  return FALSE;
3068 
3069  /* Create the status bar */
3070  hwndStatus = CreateWindowExW(0, // no extended styles
3071  STATUSCLASSNAMEW, // status bar
3072  L"", // no text
3073  WS_CHILD | WS_VISIBLE | CCS_BOTTOM | SBARS_SIZEGRIP, // styles
3074  0, 0, 0, 0, // x, y, cx, cy
3075  hwndMainWindow, // parent window
3076  (HMENU)100, // window ID
3077  hInstance, // instance
3078  NULL); // window data
3079 
3080  GetClientRect(hwndMainWindow, &rcClient);
3081  GetWindowRect(hwndStatus, &rs);
3082  StatusHeight = rs.bottom - rs.top;
3083 
3084  /* Create a progress bar in the status bar (hidden by default) */
3086  hwndStatusProgress = CreateWindowExW(0, // no extended styles
3087  PROGRESS_CLASSW, // status bar
3088  NULL, // no text
3089  WS_CHILD | PBS_SMOOTH, // styles
3090  rs.left, rs.top, // x, y
3091  rs.right - rs.left, rs.bottom - rs.top, // cx, cy
3092  hwndStatus, // parent window
3093  NULL, // window ID
3094  hInstance, // instance
3095  NULL); // window data
3096  /* Remove its static edge */
3100 
3101  /* Initialize the splitter default positions */
3102  nVSplitPos = Settings.nVSplitPos;
3103  nHSplitPos = Settings.nHSplitPos;
3104 
3105  /* Create the TreeView */
3107  WC_TREEVIEWW,
3108  NULL,
3109  // WS_CHILD | WS_VISIBLE | TVS_HASLINES | TVS_SHOWSELALWAYS,
3111  0, 0,
3112  nVSplitPos - SPLIT_WIDTH/2,
3113  (rcClient.bottom - rcClient.top) - StatusHeight,
3115  NULL,
3116  hInstance,
3117  NULL);
3118 
3119  /* Create the ImageList */
3122  ILC_COLOR32 | ILC_MASK, // ILC_COLOR24
3123  1, 1);
3124 
3125  /* Add event type icons to the ImageList: closed/opened folder, event log (normal/viewed) */
3130 
3131  /* Assign the ImageList to the Tree View */
3133 
3134  /* Add the event logs nodes */
3135  // "System Logs"
3138  // "Application Logs"
3139  LoadStringW(hInstance, IDS_EVENTLOG_APP, szTemp, ARRAYSIZE(szTemp));
3140  htiAppLogs = TreeViewAddItem(hwndTreeView, NULL, szTemp, 0, 1, (LPARAM)NULL);
3141  // "User Logs"
3142  LoadStringW(hInstance, IDS_EVENTLOG_USER, szTemp, ARRAYSIZE(szTemp));
3144 
3145  /* Create the Event details pane (optional) */
3147  if (hwndEventDetails)
3148  {
3152  nVSplitPos + SPLIT_WIDTH/2,
3153  nHSplitPos + SPLIT_WIDTH/2,
3154  (rcClient.right - rcClient.left) - nVSplitPos - SPLIT_WIDTH/2,
3155  (rcClient.bottom - rcClient.top) - nHSplitPos - SPLIT_WIDTH/2 - StatusHeight,
3157  }
3158 
3159  /* Create the ListView */
3161  WC_LISTVIEWW,
3162  NULL,
3164  nVSplitPos + SPLIT_WIDTH/2,
3165  0,
3166  (rcClient.right - rcClient.left) - nVSplitPos - SPLIT_WIDTH/2,
3167  hwndEventDetails && Settings.bShowDetailsPane
3168  ? nHSplitPos - SPLIT_WIDTH/2
3169  : (rcClient.bottom - rcClient.top) - StatusHeight,
3171  NULL,
3172  hInstance,
3173  NULL);
3174 
3175  /* Add the extended ListView styles */
3177 
3178  /* Create the ImageList */
3181  ILC_COLOR32 | ILC_MASK, // ILC_COLOR24
3182  1, 1);
3183 
3184  /* Add event type icons to the ImageList */
3190 
3191  /* Assign the ImageList to the List View */
3193 
3194  /* Now set up the listview with its columns */
3195  lvc.mask = LVCF_TEXT | LVCF_WIDTH;
3196  lvc.cx = 90;
3199  szTemp,
3200  ARRAYSIZE(szTemp));
3201  lvc.pszText = szTemp;
3203 
3204  lvc.cx = 70;
3207  szTemp,
3208  ARRAYSIZE(szTemp));
3209  lvc.pszText = szTemp;
3211 
3212  lvc.cx = 70;
3215  szTemp,
3216  ARRAYSIZE(szTemp));
3217  lvc.pszText = szTemp;
3219 
3220  lvc.cx = 150;
3223  szTemp,
3224  ARRAYSIZE(szTemp));
3225  lvc.pszText = szTemp;
3227 
3228  lvc.cx = 100;
3231  szTemp,
3232  ARRAYSIZE(szTemp));
3233  lvc.pszText = szTemp;
3235 
3236  lvc.cx = 60;
3239  szTemp,
3240  ARRAYSIZE(szTemp));
3241  lvc.pszText = szTemp;
3243 
3244  lvc.cx = 120;
3247  szTemp,
3248  ARRAYSIZE(szTemp));
3249  lvc.pszText = szTemp;
3251 
3252  lvc.cx = 100;
3255  szTemp,
3256  ARRAYSIZE(szTemp));
3257  lvc.pszText = szTemp;
3259 
3260  /* Initialize the save Dialog */
3261  ZeroMemory(&sfn, sizeof(sfn));
3263 
3265 
3266  sfn.lStructSize = sizeof(sfn);
3272  sfn.lpstrDefExt = NULL;
3273 
3274  ShowWindow(hwndMainWindow, Settings.wpPos.showCmd);
3276 
3277  return TRUE;
3278 }
3279 
3281 {
3282  RECT rs;
3283  LONG StatusHeight;
3284  LONG_PTR dwExStyle;
3285  HDWP hdwp;
3286 
3287  /* Resize the status bar -- now done in WM_SIZE */
3288  // SendMessageW(hwndStatus, WM_SIZE, 0, 0);
3289  GetWindowRect(hwndStatus, &rs);
3290  StatusHeight = rs.bottom - rs.top;
3291 
3292  /*
3293  * Move the progress bar -- Take into account for extra size due to the static edge
3294  * (AdjustWindowRectEx() does not seem to work for the progress bar).
3295  */
3300  rs.left, rs.top, rs.right - rs.left, rs.bottom - rs.top,
3303 
3304  /*
3305  * TODO: Adjust the splitter positions:
3306  * - Vertical splitter (1) : fixed position from the left window side.
3307  * - Horizontal splitter (2): fixed position from the bottom window side.
3308  */
3310  nHSplitPos = min(max(nHSplitPos, SPLIT_WIDTH/2), cy - SPLIT_WIDTH/2 - StatusHeight); // FIXME!
3311 
3312  hdwp = BeginDeferWindowPos(3);
3313 
3314  if (hdwp)
3315  hdwp = DeferWindowPos(hdwp,
3316  hwndTreeView,
3317  HWND_TOP,
3318  0, 0,
3319  nVSplitPos - SPLIT_WIDTH/2,
3320  cy - StatusHeight,
3322 
3323  if (hdwp)
3324  hdwp = DeferWindowPos(hdwp,
3325  hwndListView,
3326  HWND_TOP,
3327  nVSplitPos + SPLIT_WIDTH/2, 0,
3328  cx - nVSplitPos - SPLIT_WIDTH/2,
3329  hwndEventDetails && Settings.bShowDetailsPane
3330  ? nHSplitPos - SPLIT_WIDTH/2
3331  : cy - StatusHeight,
3333 
3334  if (hwndEventDetails && Settings.bShowDetailsPane && hdwp)
3335  hdwp = DeferWindowPos(hdwp,
3337  HWND_TOP,
3338  nVSplitPos + SPLIT_WIDTH/2,
3339  nHSplitPos + SPLIT_WIDTH/2,
3340  cx - nVSplitPos - SPLIT_WIDTH/2,
3341  cy - nHSplitPos - SPLIT_WIDTH/2 - StatusHeight,
3343 
3344  if (hdwp)
3345  EndDeferWindowPos(hdwp);
3346 }
3347 
3348 
3351 {
3352  RECT rect;
3353 
3354  switch (uMsg)
3355  {
3356  case WM_CREATE:
3357  hMainMenu = GetMenu(hWnd);
3358  break;
3359 
3360  case WM_DESTROY:
3361  {
3363  PostQuitMessage(0);
3364  break;
3365  }
3366 
3367  case WM_NOTIFY:
3368  {
3370 
3371  if (hdr->hwndFrom == hwndListView)
3372  {
3373  switch (hdr->code)
3374  {
3375  case LVN_ITEMCHANGED:
3376  {
3378 
3379  if ( (pnmv->uChanged & LVIF_STATE) && /* The state has changed */
3380  (pnmv->uNewState & LVIS_SELECTED) /* The item has been (de)selected */ )
3381  {
3382  if (hwndEventDetails)
3384  }
3385  break;
3386  }
3387 
3388  case NM_DBLCLK:
3389  case NM_RETURN:
3391  break;
3392  }
3393  }
3394  else if (hdr->hwndFrom == hwndTreeView)
3395  {
3396  switch (hdr->code)
3397  {
3398  case TVN_BEGINLABELEDIT:
3399  {
3400  HTREEITEM hItem = ((LPNMTVDISPINFO)lParam)->item.hItem;
3401 
3402  /* Disable label editing for root nodes */
3403  return ((hItem == htiSystemLogs) ||
3404  (hItem == htiAppLogs) ||
3405  (hItem == htiUserLogs));
3406  }
3407 
3408  case TVN_ENDLABELEDIT:
3409  {
3410  TVITEMW item = ((LPNMTVDISPINFO)lParam)->item;
3411  HTREEITEM hItem = item.hItem;
3412 
3413  /* Disable label editing for root nodes */
3414  if ((hItem == htiSystemLogs) ||
3415  (hItem == htiAppLogs) ||
3416  (hItem == htiUserLogs))
3417  {
3418  return FALSE;
3419  }
3420 
3421  if (item.pszText)
3422  {
3423  LPWSTR pszText = item.pszText;
3424 
3425  /* Trim leading whitespace */
3426  while (*pszText && iswspace(*pszText))
3427  ++pszText;
3428 
3429  if (!*pszText)
3430  return FALSE;
3431 
3432  return TRUE;
3433  }
3434  else
3435  {
3436  return FALSE;
3437  }
3438  }
3439 
3440  case TVN_SELCHANGED:
3441  {
3442  PEVENTLOGFILTER EventLogFilter =
3443  (PEVENTLOGFILTER)((LPNMTREEVIEW)lParam)->itemNew.lParam;
3444 
3445  // FIXME: It might be nice to reference here the filter,
3446  // so that we don't have to reference/dereference it many times
3447  // in the other functions???
3448 
3449  // FIXME: This is a hack!!
3450  if (hwndEventDetails && EventLogFilter)
3451  {
3452  SendMessageW(hwndEventDetails, EVT_SETFILTER, 0, (LPARAM)EventLogFilter);
3453  }
3454 
3455  if (EventLogFilter)
3456  {
3457  /*
3458  * If we have selected a filter, enable the menu commands;
3459  * they will possibly be updated after events enumeration.
3460  */
3466  }
3467  else
3468  {
3474  }
3475 
3476  /*
3477  * The enumeration thread that is triggered by EnumEvents
3478  * will set a new value for the 'ActiveFilter'.
3479  */
3480  if (EventLogFilter)
3481  EnumEvents(EventLogFilter);
3482 
3483  break;
3484  }
3485  }
3486  }
3487  break;
3488  }
3489 
3490  case WM_COMMAND:
3491  {
3492  /* Parse the menu selections */
3493  switch (LOWORD(wParam))
3494  {
3495  case IDM_OPEN_EVENTLOG:
3496  OpenUserEventLog();
3497  break;
3498 
3499  case IDM_SAVE_EVENTLOG:
3501  break;
3502 
3503  case IDM_CLOSE_EVENTLOG:
3504  {
3505  HTREEITEM hti;
3506  PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(&hti);
3507  CloseUserEventLog(EventLogFilter, hti);
3508  break;
3509  }
3510 
3511  case IDM_CLEAR_EVENTS:
3512  {
3513  PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(NULL);
3514  if (EventLogFilter && ClearEvents(EventLogFilter))
3515  Refresh(EventLogFilter);
3516  break;
3517  }
3518 
3519  case IDM_RENAME_EVENTLOG:
3520  if (GetFocus() == hwndTreeView)
3522  break;
3523 
3524  case IDM_EVENTLOG_SETTINGS:
3525  {
3526  PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(NULL);
3527  // TODO: Check the returned value?
3528  if (EventLogFilter)
3529  EventLogProperties(hInst, hWnd, EventLogFilter);
3530  break;
3531  }
3532 
3533  case IDM_LIST_NEWEST:
3534  {
3536  if (!Settings.bNewestEventsFirst)
3537  {
3538  Settings.bNewestEventsFirst = TRUE;
3540  }
3541  break;
3542  }
3543 
3544  case IDM_LIST_OLDEST:
3545  {
3547  if (Settings.bNewestEventsFirst)
3548  {
3549  Settings.bNewestEventsFirst = FALSE;
3551  }
3552  break;
3553  }
3554 
3555  case IDM_EVENT_DETAILS:
3556  {
3557  // LPNMITEMACTIVATE lpnmitem = (LPNMITEMACTIVATE)lParam;
3558  PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(NULL);
3559  if (/*lpnmitem->iItem != -1 &&*/ EventLogFilter)
3560  {
3561  EventLogFilter_AddRef(EventLogFilter);
3564  hWnd,
3565  EventDetails,
3566  (LPARAM)EventLogFilter);
3567  EventLogFilter_Release(EventLogFilter);
3568  }
3569  break;
3570  }
3571 
3572  case IDM_REFRESH:
3574  break;
3575 
3577  {
3578  Settings.bShowDetailsPane = !Settings.bShowDetailsPane;
3580  MF_BYCOMMAND | (Settings.bShowDetailsPane ? MF_CHECKED : MF_UNCHECKED));
3581 
3582  GetClientRect(hWnd, &rect);
3583  if (Settings.bShowDetailsPane)
3584  {
3585  ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
3587  }
3588  else
3589  {
3591  ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
3592  }
3593 
3594  break;
3595  }
3596 
3597  case IDM_LIST_GRID_LINES:
3598  {
3599  Settings.bShowGrid = !Settings.bShowGrid;
3601  MF_BYCOMMAND | (Settings.bShowGrid ? MF_CHECKED : MF_UNCHECKED));
3602 
3604  break;
3605  }
3606 
3607  case IDM_SAVE_SETTINGS:
3608  {
3609  Settings.bSaveSettings = !Settings.bSaveSettings;
3611  MF_BYCOMMAND | (Settings.bSaveSettings ? MF_CHECKED : MF_UNCHECKED));
3612  break;
3613  }
3614 
3615  case IDM_ABOUT:
3616  {
3617  HICON hIcon;
3618  WCHAR szCopyright[MAX_LOADSTRING];
3619 
3621  LoadStringW(hInst, IDS_COPYRIGHT, szCopyright, ARRAYSIZE(szCopyright));
3622  ShellAboutW(hWnd, szTitle, szCopyright, hIcon);
3624  break;
3625  }
3626 
3627  case IDM_HELP:
3629  L"Help not implemented yet!",
3630  L"Event Log",
3632  break;
3633 
3634  case IDM_EXIT:
3636  break;
3637 
3638  default:
3639  return DefWindowProcW(hWnd, uMsg, wParam, lParam);
3640  }
3641  break;
3642  }
3643 
3644  case WM_INITMENU:
3645  {
3646  if ((HMENU)wParam != hMainMenu)
3647  break;
3648 
3650  Settings.bNewestEventsFirst ? IDM_LIST_NEWEST : IDM_LIST_OLDEST,
3651  MF_BYCOMMAND);
3652 
3653  if (!hwndEventDetails)
3654  {
3657  }
3659  MF_BYCOMMAND | (Settings.bShowDetailsPane ? MF_CHECKED : MF_UNCHECKED));
3660 
3662  MF_BYCOMMAND | (Settings.bShowGrid ? MF_CHECKED : MF_UNCHECKED));
3663 
3665  MF_BYCOMMAND | (Settings.bSaveSettings ? MF_CHECKED : MF_UNCHECKED));
3666 
3667  break;
3668  }
3669 
3670 #if 0
3671  case WM_INITMENUPOPUP:
3672  lParam = lParam;
3673  break;
3674 
3675  case WM_CONTEXTMENU:
3676  lParam = lParam;
3677  break;
3678 #endif
3679 
3680  case WM_SETCURSOR:
3681  {
3682  POINT pt;
3683 
3684  if (LOWORD(lParam) != HTCLIENT)
3685  goto Default;
3686 
3687  GetCursorPos(&pt);
3688  ScreenToClient(hWnd, &pt);
3689 
3690  /* Set the cursor for the vertical splitter */
3691  if (pt.x >= nVSplitPos - SPLIT_WIDTH/2 && pt.x < nVSplitPos + SPLIT_WIDTH/2 + 1)
3692  {
3693  RECT rs;
3694  GetClientRect(hWnd, &rect);
3695  GetWindowRect(hwndStatus, &rs);
3696  if (pt.y >= rect.top && pt.y < rect.bottom - (rs.bottom - rs.top))
3697  {
3699  return TRUE;
3700  }
3701  }
3702  else
3703  /* Set the cursor for the horizontal splitter, if the Event details pane is displayed */
3704  if (hwndEventDetails && Settings.bShowDetailsPane &&
3705  (pt.y >= nHSplitPos - SPLIT_WIDTH/2 && pt.y < nHSplitPos + SPLIT_WIDTH/2 + 1))
3706  {
3707  // RECT rs;
3708  GetClientRect(hWnd, &rect);
3709  // GetWindowRect(hwndStatus, &rs);
3710  if (pt.x >= nVSplitPos + SPLIT_WIDTH/2 + 1 /* rect.left + (rs.bottom - rs.top) */ &&
3711  pt.x < rect.right)
3712  {
3714  return TRUE;
3715  }
3716  }
3717  goto Default;
3718  }
3719 
3720  case WM_LBUTTONDOWN:
3721  {
3722  INT x = GET_X_LPARAM(lParam);
3723  INT y = GET_Y_LPARAM(lParam);
3724 
3725  /* Reset the splitter state */
3726  bSplit = 0;
3727 
3728  /* Capture the cursor for the vertical splitter */
3729  if (x >= nVSplitPos - SPLIT_WIDTH/2 && x < nVSplitPos + SPLIT_WIDTH/2 + 1)
3730  {
3731  bSplit = 1;
3732  SetCapture(hWnd);
3733  }
3734  else
3735  /* Capture the cursor for the horizontal splitter, if the Event details pane is displayed */
3736  if (hwndEventDetails && Settings.bShowDetailsPane &&
3737  (y >= nHSplitPos - SPLIT_WIDTH/2 && y < nHSplitPos + SPLIT_WIDTH/2 + 1))
3738  {
3739  bSplit = 2;
3740  SetCapture(hWnd);
3741  }
3742  break;
3743  }
3744 
3745  case WM_LBUTTONUP:
3746  case WM_RBUTTONDOWN:
3747  {
3748  if (GetCapture() != hWnd)
3749  break;
3750 
3751  /* Adjust the correct splitter position */
3752  if (bSplit == 1)
3754  else if (bSplit == 2)
3756 
3757  /* If we are splitting, resize the windows */
3758  if (bSplit != 0)
3759  {
3760  GetClientRect(hWnd, &rect);
3761  ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
3762  }
3763 
3764  /* Reset the splitter state */
3765  bSplit = 0;
3766 
3767  ReleaseCapture();
3768  break;
3769  }
3770 
3771  case WM_MOUSEMOVE:
3772  {
3773  if (GetCapture() != hWnd)
3774  break;
3775 
3776  /* Move the correct splitter */
3777  if (bSplit == 1)
3778  {
3779  INT x = GET_X_LPARAM(lParam);
3780 
3781  GetClientRect(hWnd, &rect);
3782 
3783  x = min(max(x, SPLIT_WIDTH/2), rect.right - rect.left - SPLIT_WIDTH/2);
3784  if (nVSplitPos != x)
3785  {
3786  nVSplitPos = x;
3787  ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
3788  }
3789  }
3790  else if (bSplit == 2)
3791  {
3792  RECT rs;
3793  INT y = GET_Y_LPARAM(lParam);
3794 
3795  GetClientRect(hWnd, &rect);
3796  GetWindowRect(hwndStatus, &rs);
3797 
3798  y = min(max(y, SPLIT_WIDTH/2), rect.bottom - rect.top - SPLIT_WIDTH/2 - (rs.bottom - rs.top));
3799  if (nHSplitPos != y)
3800  {
3801  nHSplitPos = y;
3802  ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
3803  }
3804  }
3805  break;
3806  }
3807 
3808  case WM_SIZE:
3809  {
3810  if (wParam != SIZE_MINIMIZED)
3811  {
3814  break;
3815  }
3816  /* Fall through the default case */
3817  }
3818 
3819  default: Default:
3820  return DefWindowProcW(hWnd, uMsg, wParam, lParam);
3821  }
3822 
3823  return 0;
3824 }
3825 
3826 
3827 static
3828 VOID
3830 {
3831  LPWSTR lpLogName = EventLog->LogName;
3832 
3833  DWORD Result, dwType;
3834  DWORD dwMaxSize = 0, dwRetention = 0;
3835  BOOL Success;
3836  WIN32_FIND_DATAW FileInfo; // WIN32_FILE_ATTRIBUTE_DATA
3838  WCHAR wszBuf[MAX_PATH];
3839  WCHAR szTemp[MAX_LOADSTRING];
3840  LPWSTR FileName;
3841 
3842  HKEY hLogKey;
3843  WCHAR *KeyPath;
3844  DWORD cbData;
3845  SIZE_T cbKeyPath;
3846 
3847  if (EventLog->Permanent)
3848  {
3849 
3850  cbKeyPath = (wcslen(EVENTLOG_BASE_KEY) + wcslen(lpLogName) + 1) * sizeof(WCHAR);
3851  KeyPath = HeapAlloc(GetProcessHeap(), 0, cbKeyPath);
3852  if (!KeyPath)
3853  {
3855  goto Quit;
3856  }
3857 
3858  StringCbCopyW(KeyPath, cbKeyPath, EVENTLOG_BASE_KEY);
3859  StringCbCatW(KeyPath, cbKeyPath, lpLogName);
3860 
3861  Result = RegOpenKeyExW(hkMachine, KeyPath, 0, KEY_QUERY_VALUE, &hLogKey);
3862  HeapFree(GetProcessHeap(), 0, KeyPath);
3863  if (Result != ERROR_SUCCESS)
3864  {
3866  goto Quit;
3867  }
3868 
3869 
3870  cbData = sizeof(dwMaxSize);
3871  Result = RegQueryValueExW(hLogKey,
3872  L"MaxSize",
3873  NULL,
3874  &dwType,
3875  (LPBYTE)&dwMaxSize,
3876  &cbData);
3877  if ((Result != ERROR_SUCCESS) || (dwType != REG_DWORD))
3878  {
3879  // dwMaxSize = 512 * 1024; /* 512 kBytes */
3880  /* Workstation: 512 KB; Server: 16384 KB */
3881  dwMaxSize = 16384 * 1024;
3882  }
3883  /* Convert in KB */
3884  dwMaxSize /= 1024;
3885 
3886  cbData = sizeof(dwRetention);
3887  Result = RegQueryValueExW(hLogKey,
3888  L"Retention",
3889  NULL,
3890  &dwType,
3891  (LPBYTE)&dwRetention,
3892  &cbData);
3893  if ((Result != ERROR_SUCCESS) || (dwType != REG_DWORD))
3894  {
3895  /* By default, it is 604800 (secs) == 7 days. On Server, the retention is zeroed out. */
3896  dwRetention = 0;
3897  }
3898  /* Convert in days, rounded up */
3899  if (dwRetention != INFINITE)
3900  dwRetention = (dwRetention + 24*3600 - 1) / (24*3600);
3901 
3902  RegCloseKey(hLogKey);
3903 
3904  }
3905 
3906 Quit:
3907 
3908  SetDlgItemTextW(hDlg, IDC_DISPLAYNAME, lpLogName); // FIXME!
3909  SetDlgItemTextW(hDlg, IDC_LOGNAME, lpLogName);
3910 
3911  FileName = EventLog->FileName;
3912  if (FileName && *FileName)
3913  {
3914  /* Expand the file name. If the log file is on a remote computer, retrieve the network share form of the file name. */
3915  GetExpandedFilePathName(EventLog->ComputerName, FileName, wszBuf, ARRAYSIZE(wszBuf));
3916  FileName = wszBuf;
3917  }
3918  else
3919  {
3920  FileName = L"";
3921  }
3923 
3924  if (FileName && *FileName)
3925  {
3926  /*
3927  * The general problem here (and in the shell as well) is that
3928  * GetFileAttributesEx fails for files that are opened without
3929  * shared access. To retrieve file information for those we need
3930  * to use something else: FindFirstFile, on the full file name.
3931  */
3935  if (!Success)
3936  {
3938  Success = (hFind != INVALID_HANDLE_VALUE);
3939  if (Success)
3940  FindClose(hFind);
3941  }
3942  }
3943  else
3944  {
3945  Success = FALSE;
3946  }
3947 
3948  /* Starting there, FileName becomes invalid because we are reusing wszBuf */
3949 
3950  if (Success)
3951  {
3952  FileSize.u.LowPart = FileInfo.nFileSizeLow;
3953  FileSize.u.HighPart = FileInfo.nFileSizeHigh;
3954  if (FormatFileSizeWithBytes(&FileSize, wszBuf, ARRAYSIZE(wszBuf)))
3955  SetDlgItemTextW(hDlg, IDC_SIZE_LABEL, wszBuf);
3956 
3957  LoadStringW(hInst, IDS_NOT_AVAILABLE, szTemp, ARRAYSIZE(szTemp));
3958 
3959  if (GetFileTimeString(&FileInfo.ftCreationTime, wszBuf, ARRAYSIZE(wszBuf)))
3960  SetDlgItemTextW(hDlg, IDC_CREATED_LABEL, wszBuf);
3961  else
3962  SetDlgItemTextW(hDlg, IDC_CREATED_LABEL, szTemp);
3963 
3964  if (GetFileTimeString(&FileInfo.ftLastWriteTime, wszBuf, ARRAYSIZE(wszBuf)))
3965  SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, wszBuf);
3966  else
3967  SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, szTemp);
3968 
3969  if (GetFileTimeString(&FileInfo.ftLastAccessTime, wszBuf, ARRAYSIZE(wszBuf)))
3970  SetDlgItemTextW(hDlg, IDC_ACCESSED_LABEL, wszBuf);
3971  else
3972  SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, szTemp);
3973  }
3974  else
3975  {
3976  LoadStringW(hInst, IDS_NOT_AVAILABLE, szTemp, ARRAYSIZE(szTemp));
3977 
3978  SetDlgItemTextW(hDlg, IDC_SIZE_LABEL, szTemp);
3979  SetDlgItemTextW(hDlg, IDC_CREATED_LABEL, szTemp);
3980  SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, szTemp);
3981  SetDlgItemTextW(hDlg, IDC_ACCESSED_LABEL, szTemp);
3982  }
3983 
3984  if (EventLog->Permanent)
3985  {
3988 
3989  SetDlgItemInt(hDlg, IDC_EDIT_MAXLOGSIZE, dwMaxSize, FALSE);
3990  SetDlgItemInt(hDlg, IDC_EDIT_EVENTS_AGE, (dwRetention == 0) ? 7 : dwRetention, FALSE);
3991 
3992  if (dwRetention == 0)
3993  {
3997  }
3998  else if (dwRetention == INFINITE)
3999  {
4003  }
4004  else
4005  {
4009  }
4010  }
4011  else
4012  {
4013  // TODO: Hide the unused controls! Or, just use another type of property sheet!
4014  }
4015 }
4016 
4017 static
4018 VOID
4020 {
4021  LPWSTR lpLogName = EventLog->LogName;
4022 
4023  LONG Result;
4024  DWORD dwMaxSize = 0, dwRetention = 0;
4025  HKEY hLogKey;
4026  WCHAR *KeyPath;
4027  SIZE_T cbKeyPath;
4028 
4029  if (!EventLog->Permanent)
4030  return;
4031 
4032  cbKeyPath = (wcslen(EVENTLOG_BASE_KEY) + wcslen(lpLogName) + 1) * sizeof(WCHAR);
4033  KeyPath = HeapAlloc(GetProcessHeap(), 0, cbKeyPath);
4034  if (!KeyPath)
4035  {
4037  return;
4038  }
4039 
4040  StringCbCopyW(KeyPath, cbKeyPath, EVENTLOG_BASE_KEY);
4041  StringCbCatW(KeyPath, cbKeyPath, lpLogName);
4042 
4043  Result = RegOpenKeyExW(hkMachine, KeyPath, 0, KEY_SET_VALUE, &hLogKey);
4044  HeapFree(GetProcessHeap(), 0, KeyPath);
4045  if (Result != ERROR_SUCCESS)
4046  {
4048  return;
4049  }
4050 
4051  dwMaxSize = GetDlgItemInt(hDlg, IDC_EDIT_MAXLOGSIZE, NULL, FALSE) * 1024;
4052  RegSetValueExW(hLogKey,
4053  L"MaxSize",
4054  0,
4055  REG_DWORD,
4056  (LPBYTE)&dwMaxSize,
4057  sizeof(dwMaxSize));
4058 
4060  dwRetention = 0;
4061  else if (IsDlgButtonChecked(hDlg, IDC_NO_OVERWRITE) == BST_CHECKED)
4062  dwRetention = INFINITE;
4063  else // if (IsDlgButtonChecked(hDlg, IDC_OVERWRITE_OLDER_THAN) == BST_CHECKED)
4064  dwRetention = GetDlgItemInt(hDlg, IDC_EDIT_EVENTS_AGE, NULL, FALSE) * (24*3600);
4065 
4066  RegSetValueExW(hLogKey,
4067  L"Retention",
4068  0,
4069  REG_DWORD,
4070  (LPBYTE)&dwRetention,
4071  sizeof(dwRetention));
4072 
4073  RegCloseKey(hLogKey);
4074 }
4075 
4076 /* Message handler for EventLog Properties dialog */
4079 {
4080  PEVENTLOG EventLog;
4082 
4083  EventLog = (PEVENTLOG)GetWindowLongPtrW(hDlg, DWLP_USER);
4084 
4085  switch (uMsg)
4086  {
4087  case WM_INITDIALOG:
4088  {
4089  EventLog = (PEVENTLOG)((LPPROPSHEETPAGE)lParam)->lParam;
4090  SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)EventLog);
4091 
4092  InitPropertiesDlg(hDlg, EventLog);
4093 
4094  PropSheet_UnChanged(GetParent(hDlg), hDlg);
4095  return (INT_PTR)TRUE;
4096  }
4097 
4098  case WM_DESTROY:
4099  return (INT_PTR)TRUE;
4100 
4101  case WM_NOTIFY:
4102  switch (((LPNMHDR)lParam)->code)
4103  {
4104  case PSN_APPLY:
4105  PropSheet_UnChanged(GetParent(hDlg), hDlg);
4106  SavePropertiesDlg(hDlg, EventLog);
4107  return (INT_PTR)TRUE;
4108  }
4109  break;
4110 
4111  case WM_COMMAND:
4112  switch (LOWORD(wParam))
4113  {
4114  case IDOK:
4115  case IDCANCEL:
4116  EndDialog(hDlg, LOWORD(wParam));
4117  return (INT_PTR)TRUE;
4118 
4119  case ID_CLEARLOG:
4120  {
4121  PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(NULL);
4122  if (EventLogFilter && ClearEvents(EventLogFilter))
4123  {
4124  Refresh(EventLogFilter);
4125  InitPropertiesDlg(hDlg, EventLog);
4126  }
4127  return (INT_PTR)TRUE;
4128  }
4129 
4130  case IDC_EDIT_EVENTS_AGE:
4131  case IDC_EDIT_MAXLOGSIZE:
4132  if (HIWORD(wParam) == EN_CHANGE)
4133  {
4134  PropSheet_Changed(GetParent(hDlg), hDlg);
4135  }
4136  return (INT_PTR)TRUE;
4137 
4139  {
4143  PropSheet_Changed(GetParent(hDlg), hDlg);
4144  return (INT_PTR)TRUE;
4145  }
4146 
4148  {
4152  PropSheet_Changed(GetParent(hDlg), hDlg);
4153  return (INT_PTR)TRUE;
4154  }
4155 
4156  case IDC_NO_OVERWRITE:
4157  {
4161  PropSheet_Changed(GetParent(hDlg), hDlg);
4162  return (INT_PTR)TRUE;
4163  }
4164 
4165  case IDC_RESTOREDEFAULTS:
4166  {
4168 
4170  {
4172  /* Workstation: 512 KB; Server: 16384 KB */
4173  SetDlgItemInt(hDlg, IDC_EDIT_MAXLOGSIZE, 5120, FALSE);
4177  PropSheet_Changed(GetParent(hDlg), hDlg);
4178  }
4179  return (INT_PTR)TRUE;
4180  }
4181 
4182  case IDHELP:
4183  MessageBoxW(hDlg,
4184  L"Help not implemented yet!",
4185  L"Event Log",
4187  return (INT_PTR)TRUE;
4188 
4189  default:
4190  break;
4191  }
4192  break;
4193  }
4194 
4195  return (INT_PTR)FALSE;
4196 }
4197 
4198 INT_PTR
4200 {
4201  INT_PTR ret = 0;
4202  PROPSHEETHEADERW psh;
4203  PROPSHEETPAGEW psp[1]; // 2
4204 
4205  /*
4206  * Bail out if there is no available filter, or if the filter
4207  * contains more than one log.
4208  */
4209  if (!EventLogFilter)
4210  return 0;
4211 
4212  EventLogFilter_AddRef(EventLogFilter);
4213 
4214  if (EventLogFilter->NumOfEventLogs > 1 ||
4215  EventLogFilter->EventLogs[0] == NULL)
4216  {
4217  goto Quit;
4218  }
4219 
4220  /* Header */
4221  psh.dwSize = sizeof(psh);
4222  psh.dwFlags = PSH_PROPSHEETPAGE /*| PSH_USEICONID */ | PSH_PROPTITLE | PSH_HASHELP /*| PSH_NOCONTEXTHELP */ /*| PSH_USECALLBACK */;
4223  psh.hInstance = hInstance;
4224  psh.hwndParent = hWndParent;
4225  // psh.pszIcon = MAKEINTRESOURCEW(IDI_APPICON); // Disabled because it only sets the small icon; the big icon is a stretched version of the small one.
4226  psh.pszCaption = EventLogFilter->EventLogs[0]->LogName;
4227  psh.nStartPage = 0;
4228  psh.ppsp = psp;
4229  psh.nPages = ARRAYSIZE(psp);
4230  // psh.pfnCallback = PropSheetCallback;
4231 
4232  /* Log properties page */
4233  psp[0].dwSize = sizeof(psp[0]);
4234  psp[0].dwFlags = PSP_HASHELP;
4235  psp[0].hInstance = hInstance;
4237  psp[0].pfnDlgProc = EventLogPropProc;
4238  psp[0].lParam = (LPARAM)EventLogFilter->EventLogs[0];
4239 
4240 #if 0
4241  /* TODO: Log sources page */
4242  psp[1].dwSize = sizeof(psp[1]);
4243  psp[1].dwFlags = PSP_HASHELP;
4244  psp[1].hInstance = hInstance;
4246  psp[1].pfnDlgProc = GeneralPageWndProc;
4247  psp[1].lParam = (LPARAM)EventLogFilter->EventLogs[0];
4248 #endif
4249 
4250  /* Create the property sheet */
4251  ret = PropertySheetW(&psh);
4252 
4253 Quit:
4254  EventLogFilter_Release(EventLogFilter);
4255  return ret;
4256 }
4257 
4258 /* Message handler for Event Details dialog */
4259 static HWND hWndDetailsCtrl = NULL; // May go into the DWLP_USER
4260 static HWND hWndGrip = NULL;
4261 static INT cxMin, cyMin; // In window coordinates
4262 static INT cxOld, cyOld; // In client coordinates
4263 
4266 {
4267  switch (uMsg)
4268  {
4269  case WM_INITDIALOG:
4270  {
4271  LONG_PTR dwStyle;
4272  INT sbVXSize, sbHYSize;
4273  RECT rcWnd, rect;
4274 
4276  if (!hWndDetailsCtrl)
4277  {
4278  EndDialog(hDlg, 0);
4279  return (INT_PTR)TRUE;
4280  }
4281 
4282  /* Create a size grip if the dialog has a sizing border */
4283  GetClientRect(hDlg, &rcWnd);
4284  dwStyle = GetWindowLongPtrW(hDlg, GWL_STYLE);
4285  sbVXSize = GetSystemMetrics(SM_CXVSCROLL);
4286  sbHYSize = GetSystemMetrics(SM_CYHSCROLL);
4287  if (dwStyle & WS_THICKFRAME /* == WS_SIZEBOX */)
4288  {
4290  NULL,
4292  rcWnd.right - sbVXSize,
4293  rcWnd.bottom - sbHYSize,
4294  sbVXSize, sbHYSize,
4295  hDlg,
4296  NULL,
4297  hInst,
4298  NULL);
4299  }
4300 
4301  // SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)hWndDetailsCtrl);
4302 
4303  /*
4304  * Compute the minimum window size (in window coordinates) by
4305  * adding the widths/heights of the "Help" and "Close" buttons,
4306  * together with the margins, and add some minimal spacing
4307  * between the buttons.
4308  */
4309  GetWindowRect(hDlg, &rcWnd);
4310  cxMin = cyMin = 0;
4311 
4312  GetWindowRect(GetDlgItem(hDlg, IDHELP), &rect);
4313  cxMin += (rect.right - rect.left) + (rect.left - rcWnd.left); // == (rect.right - rcWnd.left);
4314  cyMin += (rect.bottom - rect.top) + (rcWnd.bottom - rect.bottom); // == (rcWnd.bottom - rect.top);
4315 
4316  GetWindowRect(GetDlgItem(hDlg, IDOK), &rect);
4317  cxMin += (rect.right - rect.left) + (rcWnd.right - rect.right); // == (rcWnd.right - rect.left);
4318  cyMin += (rect.bottom - rect.top) + (rcWnd.bottom - rect.bottom); // == (rcWnd.bottom - rect.top);
4319 
4320  /*
4321  * Convert the window rect from window to client coordinates
4322  * in order to retrieve the sizes of the left and top margins,
4323  * and add some extra space.
4324  */
4325  MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rcWnd, sizeof(RECT)/sizeof(POINT));
4326 
4327  cxMin += -2*rcWnd.left; // Minimal spacing between the buttons == 2 * left margin
4328  cyMin += -rcWnd.top + 12; // Add some space on top
4329 
4330  GetClientRect(hDlg, &rcWnd);
4331  cxOld = rcWnd.right - rcWnd.left;
4332  cyOld = rcWnd.bottom - rcWnd.top;
4333 
4334  /* Show event info on dialog control */
4336 
4337  // SetWindowPos(hWndDetailsCtrl, NULL,
4338  // 0, 0,
4339  // (rcWnd.right - rcWnd.left),
4340  // (rcWnd.bottom - rcWnd.top),
4341  // SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
4342 
4343  /*
4344  * Hide the placeholder static control and show the event details
4345  * control instead. Note that the placeholder is here so far just
4346  * to get the dimensions right in the dialog resource editor.
4347  * I plan to remove it and use a custom control with a suitable
4348  * window class for it, that would create the event details control
4349  * instead.
4350  */
4353  return (INT_PTR)TRUE;
4354  }
4355 
4356  case WM_DESTROY:
4360  return (INT_PTR)TRUE;
4361 
4362  case WM_COMMAND:
4363  switch (LOWORD(wParam))
4364  {
4365  case IDOK:
4366  case IDCANCEL:
4367  EndDialog(hDlg, LOWORD(wParam));
4368  return (INT_PTR)TRUE;
4369 
4370  case IDHELP:
4371  MessageBoxW(hDlg,
4372  L"Help not implemented yet!",
4373  L"Event Log",
4375  return (INT_PTR)TRUE;
4376 
4377  default:
4378  break;
4379  }
4380  break;
4381 
4382  case WM_SETCURSOR:
4383  if (((HWND)wParam == hWndGrip) && (LOWORD(lParam) == HTCLIENT))
4384  {
4387  return (INT_PTR)TRUE;
4388  }
4389  break;
4390 
4391  case WM_SIZING:
4392  {
4393  /* Forbid resizing the dialog smaller than its minimal size */
4394  PRECT dragRect = (PRECT)lParam;
4395 
4396  if ((wParam == WMSZ_LEFT) || (wParam == WMSZ_TOPLEFT) || (wParam == WMSZ_BOTTOMLEFT))
4397  {
4398  if (dragRect->right - dragRect->left < cxMin)
4399  dragRect->left = dragRect->right - cxMin;
4400  }
4401 
4402  if ((wParam == WMSZ_RIGHT) || (wParam == WMSZ_TOPRIGHT) || (wParam == WMSZ_BOTTOMRIGHT))
4403  {
4404  if (dragRect->right - dragRect->left < cxMin)
4405  dragRect->right = dragRect->left + cxMin;
4406  }
4407 
4408  if ((wParam == WMSZ_TOP) || (wParam == WMSZ_TOPLEFT) || (wParam == WMSZ_TOPRIGHT))
4409  {
4410  if (dragRect->bottom - dragRect->top < cyMin)
4411  dragRect->top = dragRect->bottom - cyMin;
4412  }
4413 
4415  {
4416  if (dragRect->bottom - dragRect->top < cyMin)
4417  dragRect->bottom = dragRect->top + cyMin;
4418  }
4419 
4421  return (INT_PTR)TRUE;
4422  }
4423 
4424  case WM_SIZE:
4425  {
4426  INT cx = LOWORD(lParam);
4427  INT cy = HIWORD(lParam);
4428 
4429  HDWP hdwp;
4430  HWND hItemWnd;
4431  RECT rect;
4432 
4433  hdwp = BeginDeferWindowPos(4);
4434 
4435  /* Resize the event details control window */
4436 
4437  hItemWnd = hWndDetailsCtrl;
4438  GetWindowRect(hItemWnd, &rect);
4439  MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
4440 
4441  if (hdwp)
4442  hdwp = DeferWindowPos(hdwp,
4443  hItemWnd,
4444  HWND_TOP,
4445  0, 0,
4446  (rect.right - rect.left) + (cx - cxOld),
4447  (rect.bottom - rect.top) + (cy - cyOld),
4449 
4450  /* Move the buttons */
4451 
4452  hItemWnd = GetDlgItem(hDlg, IDHELP);
4453  GetWindowRect(hItemWnd, &rect);
4454  MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
4455 
4456  if (hdwp)
4457  hdwp = DeferWindowPos(hdwp,
4458  hItemWnd,
4459  HWND_TOP,
4460  rect.left,
4461  rect.top + (cy - cyOld),
4462  0, 0,
4464 
4465  hItemWnd = GetDlgItem(hDlg, IDOK);
4466  GetWindowRect(hItemWnd, &rect);
4467  MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
4468 
4469  if (hdwp)
4470  hdwp = DeferWindowPos(hdwp,
4471  hItemWnd,
4472  HWND_TOP,
4473  rect.left + (cx - cxOld),
4474  rect.top + (cy - cyOld),
4475  0, 0,
4477 
4478  /* Move the size grip */
4479  if (hWndGrip && hdwp)
4480  {
4482  MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
4483 
4484  hdwp = DeferWindowPos(hdwp,
4485  hWndGrip,
4486  HWND_TOP,
4487  rect.left + (cx - cxOld),
4488  rect.top + (cy - cyOld),
4489  0, 0,
4491  }
4492 
4493  if (hdwp)
4494  EndDeferWindowPos(hdwp);
4495 
4496  /* Hide the size grip if we are in maximized mode */
4497  if (hWndGrip)
4499 
4500  cxOld = cx;
4501  cyOld = cy;
4502 
4504  return (INT_PTR)TRUE;
4505  }
4506  }
4507 
4508  return (INT_PTR)FALSE;
4509 }
#define IDC_SIZEWE
Definition: winuser.h:689
#define WS_CLIPSIBLINGS
Definition: pedump.c:618
LPWSTR lpThousandSep
Definition: winnls.h:643
#define STATUSCLASSNAMEW
Definition: commctrl.h:1932
_In_opt_ ULONG _Out_ PULONG Value
Definition: rtlfuncs.h:2373
HANDLE WINAPI OpenEventLogW(IN LPCWSTR lpUNCServerName, IN LPCWSTR lpSourceName)
Definition: eventlog.c:985
#define LOCALE_SGROUPING
Definition: winnls.h:44
HINSTANCE hInstance
Definition: commdlg.h:362
_In_ LPCSTR _Out_writes_bytes_to_opt_ cbSid PSID _Inout_ LPDWORD _Out_writes_to_opt_ cchReferencedDomainName LPSTR _Inout_ LPDWORD _Out_ PSID_NAME_USE peUse
Definition: winbase.h:2707
HWND hwndParent
Definition: prsht.h:295
#define WS_THICKFRAME
Definition: pedump.c:630
LPWSTR WINAPI StrFormatByteSizeW(LONGLONG llBytes, LPWSTR lpszDest, UINT cchMax)
Definition: string.c:2376
_Must_inspect_result_ _Out_ PNDIS_STATUS _In_ NDIS_HANDLE _In_ ULONG _Out_ PNDIS_STRING KeyName
Definition: ndis.h:4711
void TrimNulls(LPWSTR s)
Definition: eventvwr.c:1454
static int argc
Definition: ServiceArgs.c:12
const uint16_t * PCWSTR
Definition: typedefs.h:56
#define WMSZ_BOTTOMRIGHT
Definition: winuser.h:2446
#define IN
Definition: typedefs.h:39
DWORD Flags
Definition: commdlg.h:373
INT_PTR EventLogProperties(HINSTANCE, HWND, PEVENTLOGFILTER)
Definition: eventvwr.c:4199
#define max(a, b)
Definition: svc.c:63
BOOL AuditSuccess
Definition: eventvwr.h:117
STRSAFEAPI StringCchCopyNW(STRSAFE_LPWSTR pszDest, size_t cchDest, STRSAFE_LPCWSTR pszSrc, size_t cchToCopy)
Definition: strsafe.h:236
#define MAX_LOADSTRING
Definition: eventvwr.c:60
BOOL WINAPI TranslateMessage(_In_ const MSG *)
static HICON
Definition: imagelist.c:84
BOOL bNewestEventsFirst
Definition: eventvwr.c:127
int iImage
Definition: commctrl.h:2363
#define IDM_EVENT_DETAILS
Definition: resource.h:75
HDWP WINAPI BeginDeferWindowPos(_In_ int)
#define SM_CYHSCROLL
Definition: winuser.h:952
#define TRUE
Definition: types.h:120
NTSYSAPI VOID NTAPI RtlCopyMemory(VOID UNALIGNED *Destination, CONST VOID UNALIGNED *Source, ULONG Length)
#define WC_TREEVIEWW
Definition: commctrl.h:3239
BOOL bShowDetailsPane
Definition: eventvwr.c:124
#define IDOK
Definition: winuser.h:824
#define IDS_EVENTLOG_ERROR_TYPE
Definition: resource.h:102
#define CloseHandle
Definition: