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