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