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 return FALSE; 2742 2743 StringCbCopyW(KeyPath, cbKeyPath, EVENTLOG_BASE_KEY); 2744 StringCbCatW(KeyPath, cbKeyPath, lpLogName); 2745 2746 Result = RegOpenKeyExW(hkMachine, KeyPath, 0, KEY_QUERY_VALUE, &hLogKey); 2747 HeapFree(GetProcessHeap(), 0, KeyPath); 2748 if (Result != ERROR_SUCCESS) 2749 return FALSE; 2750 2751 cbData = sizeof(szModuleName); 2752 Result = RegQueryValueExW(hLogKey, 2753 L"DisplayNameFile", 2754 NULL, 2755 &dwType, 2756 (LPBYTE)szModuleName, 2757 &cbData); 2758 if ((Result != ERROR_SUCCESS) || (dwType != REG_EXPAND_SZ && dwType != REG_SZ)) 2759 { 2760 szModuleName[0] = UNICODE_NULL; 2761 } 2762 else 2763 { 2764 /* NULL-terminate the string and expand it */ 2765 szModuleName[cbData / sizeof(WCHAR) - 1] = UNICODE_NULL; 2766 GetExpandedFilePathName(lpComputerName, szModuleName, lpModuleName, ARRAYSIZE(szModuleName)); 2767 Success = TRUE; 2768 } 2769 2770 /* 2771 * If we have a 'DisplayNameFile', query for 'DisplayNameID'; 2772 * otherwise it's not really useful. 'DisplayNameID' is optional. 2773 */ 2774 if (Success) 2775 { 2776 cbData = sizeof(dwMessageID); 2777 Result = RegQueryValueExW(hLogKey, 2778 L"DisplayNameID", 2779 NULL, 2780 &dwType, 2781 (LPBYTE)&dwMessageID, 2782 &cbData); 2783 if ((Result != ERROR_SUCCESS) || (dwType != REG_DWORD)) 2784 dwMessageID = 0; 2785 2786 *pdwMessageID = dwMessageID; 2787 } 2788 2789 RegCloseKey(hLogKey); 2790 2791 return Success; 2792 } 2793 2794 2795 VOID 2796 BuildLogListAndFilterList(IN LPCWSTR lpComputerName) 2797 { 2798 LONG Result; 2799 HKEY hEventLogKey, hLogKey; 2800 DWORD dwNumLogs = 0; 2801 DWORD dwIndex, dwMaxKeyLength; 2802 DWORD dwType; 2803 PEVENTLOG EventLog; 2804 PEVENTLOGFILTER EventLogFilter; 2805 LPWSTR LogName = NULL; 2806 WCHAR szModuleName[MAX_PATH]; 2807 DWORD lpcName; 2808 DWORD dwMessageID; 2809 LPWSTR lpDisplayName; 2810 HTREEITEM hRootNode = NULL, hItem = NULL, hItemDefault = NULL; 2811 2812 if (hkMachine && hkMachine != HKEY_LOCAL_MACHINE) 2813 { 2814 /* We are connected to some other computer, close the old connection */ 2815 RegCloseKey(hkMachine); 2816 hkMachine = NULL; 2817 } 2818 if (!lpComputerName || !*lpComputerName) 2819 { 2820 /* Use the local computer registry */ 2821 hkMachine = HKEY_LOCAL_MACHINE; 2822 } 2823 else 2824 { 2825 /* Connect to the remote computer registry */ 2826 Result = RegConnectRegistry(lpComputerName, HKEY_LOCAL_MACHINE, &hkMachine); 2827 if (Result != ERROR_SUCCESS) 2828 { 2829 /* Connection failed, display a message and bail out */ 2830 hkMachine = NULL; 2831 ShowWin32Error(GetLastError()); 2832 return; 2833 } 2834 } 2835 2836 /* Open the EventLog key */ 2837 Result = RegOpenKeyExW(hkMachine, EVENTLOG_BASE_KEY, 0, KEY_READ, &hEventLogKey); 2838 if (Result != ERROR_SUCCESS) 2839 { 2840 return; 2841 } 2842 2843 /* Retrieve the number of event logs enumerated as registry keys */ 2844 Result = RegQueryInfoKeyW(hEventLogKey, NULL, NULL, NULL, &dwNumLogs, &dwMaxKeyLength, 2845 NULL, NULL, NULL, NULL, NULL, NULL); 2846 if (Result != ERROR_SUCCESS) 2847 { 2848 goto Quit; 2849 } 2850 if (!dwNumLogs) 2851 goto Quit; 2852 2853 /* Take the NULL terminator into account */ 2854 ++dwMaxKeyLength; 2855 2856 /* Allocate the temporary buffer */ 2857 LogName = HeapAlloc(GetProcessHeap(), 0, dwMaxKeyLength * sizeof(WCHAR)); 2858 if (!LogName) 2859 goto Quit; 2860 2861 /* Enumerate and retrieve each event log name */ 2862 for (dwIndex = 0; dwIndex < dwNumLogs; dwIndex++) 2863 { 2864 lpcName = dwMaxKeyLength; 2865 Result = RegEnumKeyExW(hEventLogKey, dwIndex, LogName, &lpcName, NULL, NULL, NULL, NULL); 2866 if (Result != ERROR_SUCCESS) 2867 continue; 2868 2869 /* Take the NULL terminator into account */ 2870 ++lpcName; 2871 2872 /* Allocate a new event log entry */ 2873 EventLog = AllocEventLog(lpComputerName, LogName, TRUE); 2874 if (EventLog == NULL) 2875 continue; 2876 2877 /* Allocate a new event log filter entry for this event log */ 2878 EventLogFilter = AllocEventLogFilter(// LogName, 2879 TRUE, TRUE, TRUE, TRUE, TRUE, 2880 NULL, NULL, NULL, 2881 1, &EventLog); 2882 if (EventLogFilter == NULL) 2883 { 2884 EventLog_Free(EventLog); 2885 continue; 2886 } 2887 2888 /* Add the event log and the filter into their lists */ 2889 InsertTailList(&EventLogList, &EventLog->ListEntry); 2890 InsertTailList(&EventLogFilterList, &EventLogFilter->ListEntry); 2891 2892 EventLog->FileName = NULL; 2893 2894 /* Retrieve and cache the event log file */ 2895 Result = RegOpenKeyExW(hEventLogKey, 2896 LogName, 2897 0, 2898 KEY_QUERY_VALUE, 2899 &hLogKey); 2900 if (Result == ERROR_SUCCESS) 2901 { 2902 lpcName = 0; 2903 Result = RegQueryValueExW(hLogKey, 2904 L"File", 2905 NULL, 2906 &dwType, 2907 NULL, 2908 &lpcName); 2909 if ((Result != ERROR_SUCCESS) || (dwType != REG_EXPAND_SZ && dwType != REG_SZ)) 2910 { 2911 // Windows' EventLog uses some kind of default value, we do not. 2912 EventLog->FileName = NULL; 2913 } 2914 else 2915 { 2916 lpcName = ROUND_DOWN(lpcName, sizeof(WCHAR)); 2917 EventLog->FileName = HeapAlloc(GetProcessHeap(), 0, lpcName); 2918 if (EventLog->FileName) 2919 { 2920 Result = RegQueryValueExW(hLogKey, 2921 L"File", 2922 NULL, 2923 &dwType, 2924 (LPBYTE)EventLog->FileName, 2925 &lpcName); 2926 if (Result != ERROR_SUCCESS) 2927 { 2928 HeapFree(GetProcessHeap(), 0, EventLog->FileName); 2929 EventLog->FileName = NULL; 2930 } 2931 else 2932 { 2933 EventLog->FileName[lpcName / sizeof(WCHAR) - 1] = UNICODE_NULL; 2934 } 2935 } 2936 } 2937 2938 RegCloseKey(hLogKey); 2939 } 2940 2941 /* Get the display name for the event log */ 2942 lpDisplayName = NULL; 2943 2944 ZeroMemory(szModuleName, sizeof(szModuleName)); 2945 if (GetDisplayNameFileAndID(LogName, szModuleName, &dwMessageID)) 2946 { 2947 /* Retrieve the message string without appending extra newlines */ 2948 lpDisplayName = 2949 GetMessageStringFromDll(szModuleName, 2950 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE | 2951 FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, 2952 dwMessageID, 2953 0, 2954 NULL); 2955 } 2956 2957 /* 2958 * Select the correct tree root node, whether the log is a System 2959 * or an Application log. Default to Application log otherwise. 2960 */ 2961 hRootNode = htiAppLogs; 2962 for (lpcName = 0; lpcName < ARRAYSIZE(SystemLogs); ++lpcName) 2963 { 2964 /* Check whether the log name is part of the system logs */ 2965 if (wcsicmp(LogName, SystemLogs[lpcName]) == 0) 2966 { 2967 hRootNode = htiSystemLogs; 2968 break; 2969 } 2970 } 2971 2972 hItem = TreeViewAddItem(hwndTreeView, hRootNode, 2973 (lpDisplayName ? lpDisplayName : LogName), 2974 2, 3, (LPARAM)EventLogFilter); 2975 2976 /* Try to get the default event log: "Application" */ 2977 if ((hItemDefault == NULL) && (wcsicmp(LogName, SystemLogs[0]) == 0)) 2978 { 2979 hItemDefault = hItem; 2980 } 2981 2982 /* Free the buffer allocated by FormatMessage */ 2983 if (lpDisplayName) 2984 LocalFree(lpDisplayName); 2985 } 2986 2987 HeapFree(GetProcessHeap(), 0, LogName); 2988 2989 Quit: 2990 RegCloseKey(hEventLogKey); 2991 2992 /* Select the default event log */ 2993 if (hItemDefault) 2994 { 2995 // TreeView_Expand(hwndTreeView, hRootNode, TVE_EXPAND); 2996 TreeView_SelectItem(hwndTreeView, hItemDefault); 2997 TreeView_EnsureVisible(hwndTreeView, hItemDefault); 2998 } 2999 InvalidateRect(hwndTreeView, NULL, FALSE); 3000 SetFocus(hwndTreeView); 3001 3002 return; 3003 } 3004 3005 VOID 3006 FreeLogList(VOID) 3007 { 3008 PLIST_ENTRY Entry; 3009 PEVENTLOG EventLog; 3010 3011 while (!IsListEmpty(&EventLogList)) 3012 { 3013 Entry = RemoveHeadList(&EventLogList); 3014 EventLog = (PEVENTLOG)CONTAINING_RECORD(Entry, EVENTLOG, ListEntry); 3015 EventLog_Free(EventLog); 3016 } 3017 3018 return; 3019 } 3020 3021 VOID 3022 FreeLogFilterList(VOID) 3023 { 3024 PLIST_ENTRY Entry; 3025 PEVENTLOGFILTER EventLogFilter; 3026 3027 while (!IsListEmpty(&EventLogFilterList)) 3028 { 3029 Entry = RemoveHeadList(&EventLogFilterList); 3030 EventLogFilter = (PEVENTLOGFILTER)CONTAINING_RECORD(Entry, EVENTLOGFILTER, ListEntry); 3031 EventLogFilter_Free(EventLogFilter); 3032 } 3033 3034 ActiveFilter = NULL; 3035 3036 return; 3037 } 3038 3039 BOOL 3040 InitInstance(HINSTANCE hInstance) 3041 { 3042 RECT rcClient, rs; 3043 LONG StatusHeight; 3044 HIMAGELIST hSmall; 3045 LVCOLUMNW lvc = {0}; 3046 WCHAR szTemp[256]; 3047 3048 /* Create the main window */ 3049 rs = Settings.wpPos.rcNormalPosition; 3050 hwndMainWindow = CreateWindowW(EVENTVWR_WNDCLASS, 3051 szTitle, 3052 WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, 3053 rs.left, rs.top, 3054 (rs.right != CW_USEDEFAULT && rs.left != CW_USEDEFAULT) ? rs.right - rs.left : CW_USEDEFAULT, 3055 (rs.bottom != CW_USEDEFAULT && rs.top != CW_USEDEFAULT) ? rs.bottom - rs.top : CW_USEDEFAULT, 3056 NULL, 3057 NULL, 3058 hInstance, 3059 NULL); 3060 if (!hwndMainWindow) 3061 return FALSE; 3062 3063 /* Create the status bar */ 3064 hwndStatus = CreateWindowExW(0, // no extended styles 3065 STATUSCLASSNAMEW, // status bar 3066 L"", // no text 3067 WS_CHILD | WS_VISIBLE | CCS_BOTTOM | SBARS_SIZEGRIP, // styles 3068 0, 0, 0, 0, // x, y, cx, cy 3069 hwndMainWindow, // parent window 3070 (HMENU)100, // window ID 3071 hInstance, // instance 3072 NULL); // window data 3073 3074 GetClientRect(hwndMainWindow, &rcClient); 3075 GetWindowRect(hwndStatus, &rs); 3076 StatusHeight = rs.bottom - rs.top; 3077 3078 /* Create a progress bar in the status bar (hidden by default) */ 3079 StatusBar_GetItemRect(hwndStatus, 0, &rs); 3080 hwndStatusProgress = CreateWindowExW(0, // no extended styles 3081 PROGRESS_CLASSW, // status bar 3082 NULL, // no text 3083 WS_CHILD | PBS_SMOOTH, // styles 3084 rs.left, rs.top, // x, y 3085 rs.right - rs.left, rs.bottom - rs.top, // cx, cy 3086 hwndStatus, // parent window 3087 NULL, // window ID 3088 hInstance, // instance 3089 NULL); // window data 3090 /* Remove its static edge */ 3091 SetWindowLongPtrW(hwndStatusProgress, GWL_EXSTYLE, 3092 GetWindowLongPtrW(hwndStatusProgress, GWL_EXSTYLE) & ~WS_EX_STATICEDGE); 3093 ProgressBar_SetStep(hwndStatusProgress, 1); 3094 3095 /* Initialize the splitter default positions */ 3096 nVSplitPos = Settings.nVSplitPos; 3097 nHSplitPos = Settings.nHSplitPos; 3098 3099 /* Create the TreeView */ 3100 hwndTreeView = CreateWindowExW(WS_EX_CLIENTEDGE, 3101 WC_TREEVIEWW, 3102 NULL, 3103 // WS_CHILD | WS_VISIBLE | TVS_HASLINES | TVS_SHOWSELALWAYS, 3104 WS_CHILD | WS_VISIBLE | /* WS_TABSTOP | */ TVS_HASLINES | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_EDITLABELS | TVS_SHOWSELALWAYS, 3105 0, 0, 3106 nVSplitPos - SPLIT_WIDTH/2, 3107 (rcClient.bottom - rcClient.top) - StatusHeight, 3108 hwndMainWindow, 3109 NULL, 3110 hInstance, 3111 NULL); 3112 3113 /* Create the ImageList */ 3114 hSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON), 3115 GetSystemMetrics(SM_CYSMICON), 3116 ILC_COLOR32 | ILC_MASK, // ILC_COLOR24 3117 1, 1); 3118 3119 /* Add event type icons to the ImageList: closed/opened folder, event log (normal/viewed) */ 3120 ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_CLOSED_CATEGORY))); 3121 ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_OPENED_CATEGORY))); 3122 ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_EVENTLOG))); 3123 ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_EVENTVWR))); 3124 3125 /* Assign the ImageList to the Tree View */ 3126 TreeView_SetImageList(hwndTreeView, hSmall, TVSIL_NORMAL); 3127 3128 /* Add the event logs nodes */ 3129 // "System Logs" 3130 LoadStringW(hInstance, IDS_EVENTLOG_SYSTEM, szTemp, ARRAYSIZE(szTemp)); 3131 htiSystemLogs = TreeViewAddItem(hwndTreeView, NULL, szTemp, 0, 1, (LPARAM)NULL); 3132 // "Application Logs" 3133 LoadStringW(hInstance, IDS_EVENTLOG_APP, szTemp, ARRAYSIZE(szTemp)); 3134 htiAppLogs = TreeViewAddItem(hwndTreeView, NULL, szTemp, 0, 1, (LPARAM)NULL); 3135 // "User Logs" 3136 LoadStringW(hInstance, IDS_EVENTLOG_USER, szTemp, ARRAYSIZE(szTemp)); 3137 htiUserLogs = TreeViewAddItem(hwndTreeView, NULL, szTemp, 0, 1, (LPARAM)NULL); 3138 3139 /* Create the Event details pane (optional) */ 3140 hwndEventDetails = CreateEventDetailsCtrl(hInst, hwndMainWindow, (LPARAM)NULL); 3141 if (hwndEventDetails) 3142 { 3143 SetWindowLongPtrW(hwndEventDetails, GWL_EXSTYLE, 3144 GetWindowLongPtrW(hwndEventDetails, GWL_EXSTYLE) | WS_EX_CLIENTEDGE); 3145 SetWindowPos(hwndEventDetails, NULL, 3146 nVSplitPos + SPLIT_WIDTH/2, 3147 nHSplitPos + SPLIT_WIDTH/2, 3148 (rcClient.right - rcClient.left) - nVSplitPos - SPLIT_WIDTH/2, 3149 (rcClient.bottom - rcClient.top) - nHSplitPos - SPLIT_WIDTH/2 - StatusHeight, 3150 SWP_NOZORDER | SWP_NOACTIVATE | (Settings.bShowDetailsPane ? SWP_SHOWWINDOW : SWP_HIDEWINDOW)); 3151 } 3152 3153 /* Create the ListView */ 3154 hwndListView = CreateWindowExW(WS_EX_CLIENTEDGE, 3155 WC_LISTVIEWW, 3156 NULL, 3157 WS_CHILD | WS_VISIBLE | LVS_SHOWSELALWAYS | LVS_REPORT, 3158 nVSplitPos + SPLIT_WIDTH/2, 3159 0, 3160 (rcClient.right - rcClient.left) - nVSplitPos - SPLIT_WIDTH/2, 3161 hwndEventDetails && Settings.bShowDetailsPane 3162 ? nHSplitPos - SPLIT_WIDTH/2 3163 : (rcClient.bottom - rcClient.top) - StatusHeight, 3164 hwndMainWindow, 3165 NULL, 3166 hInstance, 3167 NULL); 3168 3169 /* Add the extended ListView styles */ 3170 ListView_SetExtendedListViewStyle(hwndListView, LVS_EX_HEADERDRAGDROP | LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP | (Settings.bShowGrid ? LVS_EX_GRIDLINES : 0)); 3171 3172 /* Create the ImageList */ 3173 hSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON), 3174 GetSystemMetrics(SM_CYSMICON), 3175 ILC_COLOR32 | ILC_MASK, // ILC_COLOR24 3176 1, 1); 3177 3178 /* Add event type icons to the ImageList */ 3179 ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_INFORMATIONICON))); 3180 ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_WARNINGICON))); 3181 ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_ERRORICON))); 3182 ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_AUDITSUCCESSICON))); 3183 ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_AUDITFAILUREICON))); 3184 3185 /* Assign the ImageList to the List View */ 3186 ListView_SetImageList(hwndListView, hSmall, LVSIL_SMALL); 3187 3188 /* Now set up the listview with its columns */ 3189 lvc.mask = LVCF_TEXT | LVCF_WIDTH; 3190 lvc.cx = 90; 3191 LoadStringW(hInstance, 3192 IDS_COLUMNTYPE, 3193 szTemp, 3194 ARRAYSIZE(szTemp)); 3195 lvc.pszText = szTemp; 3196 ListView_InsertColumn(hwndListView, 0, &lvc); 3197 3198 lvc.cx = 70; 3199 LoadStringW(hInstance, 3200 IDS_COLUMNDATE, 3201 szTemp, 3202 ARRAYSIZE(szTemp)); 3203 lvc.pszText = szTemp; 3204 ListView_InsertColumn(hwndListView, 1, &lvc); 3205 3206 lvc.cx = 70; 3207 LoadStringW(hInstance, 3208 IDS_COLUMNTIME, 3209 szTemp, 3210 ARRAYSIZE(szTemp)); 3211 lvc.pszText = szTemp; 3212 ListView_InsertColumn(hwndListView, 2, &lvc); 3213 3214 lvc.cx = 150; 3215 LoadStringW(hInstance, 3216 IDS_COLUMNSOURCE, 3217 szTemp, 3218 ARRAYSIZE(szTemp)); 3219 lvc.pszText = szTemp; 3220 ListView_InsertColumn(hwndListView, 3, &lvc); 3221 3222 lvc.cx = 100; 3223 LoadStringW(hInstance, 3224 IDS_COLUMNCATEGORY, 3225 szTemp, 3226 ARRAYSIZE(szTemp)); 3227 lvc.pszText = szTemp; 3228 ListView_InsertColumn(hwndListView, 4, &lvc); 3229 3230 lvc.cx = 60; 3231 LoadStringW(hInstance, 3232 IDS_COLUMNEVENT, 3233 szTemp, 3234 ARRAYSIZE(szTemp)); 3235 lvc.pszText = szTemp; 3236 ListView_InsertColumn(hwndListView, 5, &lvc); 3237 3238 lvc.cx = 120; 3239 LoadStringW(hInstance, 3240 IDS_COLUMNUSER, 3241 szTemp, 3242 ARRAYSIZE(szTemp)); 3243 lvc.pszText = szTemp; 3244 ListView_InsertColumn(hwndListView, 6, &lvc); 3245 3246 lvc.cx = 100; 3247 LoadStringW(hInstance, 3248 IDS_COLUMNCOMPUTER, 3249 szTemp, 3250 ARRAYSIZE(szTemp)); 3251 lvc.pszText = szTemp; 3252 ListView_InsertColumn(hwndListView, 7, &lvc); 3253 3254 /* Initialize the save Dialog */ 3255 ZeroMemory(&sfn, sizeof(sfn)); 3256 ZeroMemory(szSaveFilter, sizeof(szSaveFilter)); 3257 3258 LoadStringW(hInst, IDS_SAVE_FILTER, szSaveFilter, ARRAYSIZE(szSaveFilter)); 3259 3260 sfn.lStructSize = sizeof(sfn); 3261 sfn.hwndOwner = hwndMainWindow; 3262 sfn.hInstance = hInstance; 3263 sfn.lpstrFilter = szSaveFilter; 3264 sfn.lpstrInitialDir = NULL; 3265 sfn.Flags = OFN_HIDEREADONLY | OFN_SHAREAWARE; 3266 sfn.lpstrDefExt = NULL; 3267 3268 ShowWindow(hwndMainWindow, Settings.wpPos.showCmd); 3269 UpdateWindow(hwndMainWindow); 3270 3271 return TRUE; 3272 } 3273 3274 VOID ResizeWnd(INT cx, INT cy) 3275 { 3276 RECT rs; 3277 LONG StatusHeight; 3278 LONG_PTR dwExStyle; 3279 HDWP hdwp; 3280 3281 /* Resize the status bar -- now done in WM_SIZE */ 3282 // SendMessageW(hwndStatus, WM_SIZE, 0, 0); 3283 GetWindowRect(hwndStatus, &rs); 3284 StatusHeight = rs.bottom - rs.top; 3285 3286 /* 3287 * Move the progress bar -- Take into account for extra size due to the static edge 3288 * (AdjustWindowRectEx() does not seem to work for the progress bar). 3289 */ 3290 StatusBar_GetItemRect(hwndStatus, 0, &rs); 3291 dwExStyle = GetWindowLongPtrW(hwndStatusProgress, GWL_EXSTYLE); 3292 SetWindowLongPtrW(hwndStatusProgress, GWL_EXSTYLE, dwExStyle | WS_EX_STATICEDGE); 3293 MoveWindow(hwndStatusProgress, 3294 rs.left, rs.top, rs.right - rs.left, rs.bottom - rs.top, 3295 IsWindowVisible(hwndStatusProgress) ? TRUE : FALSE); 3296 SetWindowLongPtrW(hwndStatusProgress, GWL_EXSTYLE, dwExStyle); 3297 3298 /* 3299 * TODO: Adjust the splitter positions: 3300 * - Vertical splitter (1) : fixed position from the left window side. 3301 * - Horizontal splitter (2): fixed position from the bottom window side. 3302 */ 3303 nVSplitPos = min(max(nVSplitPos, SPLIT_WIDTH/2), cx - SPLIT_WIDTH/2); // OK 3304 nHSplitPos = min(max(nHSplitPos, SPLIT_WIDTH/2), cy - SPLIT_WIDTH/2 - StatusHeight); // FIXME! 3305 3306 hdwp = BeginDeferWindowPos(3); 3307 3308 if (hdwp) 3309 hdwp = DeferWindowPos(hdwp, 3310 hwndTreeView, 3311 HWND_TOP, 3312 0, 0, 3313 nVSplitPos - SPLIT_WIDTH/2, 3314 cy - StatusHeight, 3315 SWP_NOZORDER | SWP_NOACTIVATE); 3316 3317 if (hdwp) 3318 hdwp = DeferWindowPos(hdwp, 3319 hwndListView, 3320 HWND_TOP, 3321 nVSplitPos + SPLIT_WIDTH/2, 0, 3322 cx - nVSplitPos - SPLIT_WIDTH/2, 3323 hwndEventDetails && Settings.bShowDetailsPane 3324 ? nHSplitPos - SPLIT_WIDTH/2 3325 : cy - StatusHeight, 3326 SWP_NOZORDER | SWP_NOACTIVATE); 3327 3328 if (hwndEventDetails && Settings.bShowDetailsPane && hdwp) 3329 hdwp = DeferWindowPos(hdwp, 3330 hwndEventDetails, 3331 HWND_TOP, 3332 nVSplitPos + SPLIT_WIDTH/2, 3333 nHSplitPos + SPLIT_WIDTH/2, 3334 cx - nVSplitPos - SPLIT_WIDTH/2, 3335 cy - nHSplitPos - SPLIT_WIDTH/2 - StatusHeight, 3336 SWP_NOZORDER | SWP_NOACTIVATE); 3337 3338 if (hdwp) 3339 EndDeferWindowPos(hdwp); 3340 } 3341 3342 3343 LRESULT CALLBACK 3344 WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 3345 { 3346 RECT rect; 3347 3348 switch (uMsg) 3349 { 3350 case WM_CREATE: 3351 hMainMenu = GetMenu(hWnd); 3352 break; 3353 3354 case WM_DESTROY: 3355 { 3356 GetWindowPlacement(hwndMainWindow, &Settings.wpPos); 3357 PostQuitMessage(0); 3358 break; 3359 } 3360 3361 case WM_NOTIFY: 3362 { 3363 LPNMHDR hdr = (LPNMHDR)lParam; 3364 3365 if (hdr->hwndFrom == hwndListView) 3366 { 3367 switch (hdr->code) 3368 { 3369 case LVN_ITEMCHANGED: 3370 { 3371 LPNMLISTVIEW pnmv = (LPNMLISTVIEW)lParam; 3372 3373 if ( (pnmv->uChanged & LVIF_STATE) && /* The state has changed */ 3374 (pnmv->uNewState & LVIS_SELECTED) /* The item has been (de)selected */ ) 3375 { 3376 if (hwndEventDetails) 3377 SendMessageW(hwndEventDetails, EVT_DISPLAY, 0, 0); 3378 } 3379 break; 3380 } 3381 3382 case NM_DBLCLK: 3383 case NM_RETURN: 3384 SendMessageW(hWnd, WM_COMMAND, IDM_EVENT_DETAILS, 0); 3385 break; 3386 } 3387 } 3388 else if (hdr->hwndFrom == hwndTreeView) 3389 { 3390 switch (hdr->code) 3391 { 3392 case TVN_BEGINLABELEDIT: 3393 { 3394 HTREEITEM hItem = ((LPNMTVDISPINFO)lParam)->item.hItem; 3395 3396 /* Disable label editing for root nodes */ 3397 return ((hItem == htiSystemLogs) || 3398 (hItem == htiAppLogs) || 3399 (hItem == htiUserLogs)); 3400 } 3401 3402 case TVN_ENDLABELEDIT: 3403 { 3404 TVITEMW item = ((LPNMTVDISPINFO)lParam)->item; 3405 HTREEITEM hItem = item.hItem; 3406 3407 /* Disable label editing for root nodes */ 3408 if ((hItem == htiSystemLogs) || 3409 (hItem == htiAppLogs) || 3410 (hItem == htiUserLogs)) 3411 { 3412 return FALSE; 3413 } 3414 3415 if (item.pszText) 3416 { 3417 LPWSTR pszText = item.pszText; 3418 3419 /* Trim leading whitespace */ 3420 while (*pszText && iswspace(*pszText)) 3421 ++pszText; 3422 3423 if (!*pszText) 3424 return FALSE; 3425 3426 return TRUE; 3427 } 3428 else 3429 { 3430 return FALSE; 3431 } 3432 } 3433 3434 case TVN_SELCHANGED: 3435 { 3436 PEVENTLOGFILTER EventLogFilter = 3437 (PEVENTLOGFILTER)((LPNMTREEVIEW)lParam)->itemNew.lParam; 3438 3439 // FIXME: It might be nice to reference here the filter, 3440 // so that we don't have to reference/dereference it many times 3441 // in the other functions??? 3442 3443 // FIXME: This is a hack!! 3444 if (hwndEventDetails && EventLogFilter) 3445 { 3446 SendMessageW(hwndEventDetails, EVT_SETFILTER, 0, (LPARAM)EventLogFilter); 3447 } 3448 3449 if (EventLogFilter) 3450 { 3451 /* 3452 * If we have selected a filter, enable the menu commands; 3453 * they will possibly be updated after events enumeration. 3454 */ 3455 EnableMenuItem(hMainMenu, IDM_SAVE_EVENTLOG, MF_BYCOMMAND | MF_ENABLED); 3456 EnableMenuItem(hMainMenu, IDM_CLOSE_EVENTLOG, MF_BYCOMMAND | MF_ENABLED); 3457 EnableMenuItem(hMainMenu, IDM_CLEAR_EVENTS, MF_BYCOMMAND | MF_ENABLED); 3458 EnableMenuItem(hMainMenu, IDM_RENAME_EVENTLOG, MF_BYCOMMAND | MF_ENABLED); 3459 EnableMenuItem(hMainMenu, IDM_EVENTLOG_SETTINGS, MF_BYCOMMAND | MF_ENABLED); 3460 } 3461 else 3462 { 3463 EnableMenuItem(hMainMenu, IDM_SAVE_EVENTLOG, MF_BYCOMMAND | MF_GRAYED); 3464 EnableMenuItem(hMainMenu, IDM_CLOSE_EVENTLOG, MF_BYCOMMAND | MF_GRAYED); 3465 EnableMenuItem(hMainMenu, IDM_CLEAR_EVENTS, MF_BYCOMMAND | MF_GRAYED); 3466 EnableMenuItem(hMainMenu, IDM_RENAME_EVENTLOG, MF_BYCOMMAND | MF_GRAYED); 3467 EnableMenuItem(hMainMenu, IDM_EVENTLOG_SETTINGS, MF_BYCOMMAND | MF_GRAYED); 3468 } 3469 3470 /* 3471 * The enumeration thread that is triggered by EnumEvents 3472 * will set a new value for the 'ActiveFilter'. 3473 */ 3474 if (EventLogFilter) 3475 EnumEvents(EventLogFilter); 3476 3477 break; 3478 } 3479 } 3480 } 3481 break; 3482 } 3483 3484 case WM_COMMAND: 3485 { 3486 /* Parse the menu selections */ 3487 switch (LOWORD(wParam)) 3488 { 3489 case IDM_OPEN_EVENTLOG: 3490 OpenUserEventLog(); 3491 break; 3492 3493 case IDM_SAVE_EVENTLOG: 3494 SaveEventLog(GetSelectedFilter(NULL)); 3495 break; 3496 3497 case IDM_CLOSE_EVENTLOG: 3498 { 3499 HTREEITEM hti; 3500 PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(&hti); 3501 CloseUserEventLog(EventLogFilter, hti); 3502 break; 3503 } 3504 3505 case IDM_CLEAR_EVENTS: 3506 { 3507 PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(NULL); 3508 if (EventLogFilter && ClearEvents(EventLogFilter)) 3509 Refresh(EventLogFilter); 3510 break; 3511 } 3512 3513 case IDM_RENAME_EVENTLOG: 3514 if (GetFocus() == hwndTreeView) 3515 TreeView_EditLabel(hwndTreeView, TreeView_GetSelection(hwndTreeView)); 3516 break; 3517 3518 case IDM_EVENTLOG_SETTINGS: 3519 { 3520 PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(NULL); 3521 // TODO: Check the returned value? 3522 if (EventLogFilter) 3523 EventLogProperties(hInst, hWnd, EventLogFilter); 3524 break; 3525 } 3526 3527 case IDM_LIST_NEWEST: 3528 { 3529 CheckMenuRadioItem(hMainMenu, IDM_LIST_NEWEST, IDM_LIST_OLDEST, IDM_LIST_NEWEST, MF_BYCOMMAND); 3530 if (!Settings.bNewestEventsFirst) 3531 { 3532 Settings.bNewestEventsFirst = TRUE; 3533 Refresh(GetSelectedFilter(NULL)); 3534 } 3535 break; 3536 } 3537 3538 case IDM_LIST_OLDEST: 3539 { 3540 CheckMenuRadioItem(hMainMenu, IDM_LIST_NEWEST, IDM_LIST_OLDEST, IDM_LIST_OLDEST, MF_BYCOMMAND); 3541 if (Settings.bNewestEventsFirst) 3542 { 3543 Settings.bNewestEventsFirst = FALSE; 3544 Refresh(GetSelectedFilter(NULL)); 3545 } 3546 break; 3547 } 3548 3549 case IDM_EVENT_DETAILS: 3550 { 3551 // LPNMITEMACTIVATE lpnmitem = (LPNMITEMACTIVATE)lParam; 3552 PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(NULL); 3553 if (/*lpnmitem->iItem != -1 &&*/ EventLogFilter) 3554 { 3555 EventLogFilter_AddRef(EventLogFilter); 3556 DialogBoxParamW(hInst, 3557 MAKEINTRESOURCEW(IDD_EVENTDETAILS_DLG), 3558 hWnd, 3559 EventDetails, 3560 (LPARAM)EventLogFilter); 3561 EventLogFilter_Release(EventLogFilter); 3562 } 3563 break; 3564 } 3565 3566 case IDM_REFRESH: 3567 Refresh(GetSelectedFilter(NULL)); 3568 break; 3569 3570 case IDM_EVENT_DETAILS_VIEW: 3571 { 3572 Settings.bShowDetailsPane = !Settings.bShowDetailsPane; 3573 CheckMenuItem(hMainMenu, IDM_EVENT_DETAILS_VIEW, 3574 MF_BYCOMMAND | (Settings.bShowDetailsPane ? MF_CHECKED : MF_UNCHECKED)); 3575 3576 GetClientRect(hWnd, &rect); 3577 if (Settings.bShowDetailsPane) 3578 { 3579 ResizeWnd(rect.right - rect.left, rect.bottom - rect.top); 3580 ShowWindow(hwndEventDetails, SW_SHOW); 3581 } 3582 else 3583 { 3584 ShowWindow(hwndEventDetails, SW_HIDE); 3585 ResizeWnd(rect.right - rect.left, rect.bottom - rect.top); 3586 } 3587 3588 break; 3589 } 3590 3591 case IDM_LIST_GRID_LINES: 3592 { 3593 Settings.bShowGrid = !Settings.bShowGrid; 3594 CheckMenuItem(hMainMenu, IDM_LIST_GRID_LINES, 3595 MF_BYCOMMAND | (Settings.bShowGrid ? MF_CHECKED : MF_UNCHECKED)); 3596 3597 ListView_SetExtendedListViewStyleEx(hwndListView, LVS_EX_GRIDLINES, (Settings.bShowGrid ? LVS_EX_GRIDLINES : 0)); 3598 break; 3599 } 3600 3601 case IDM_SAVE_SETTINGS: 3602 { 3603 Settings.bSaveSettings = !Settings.bSaveSettings; 3604 CheckMenuItem(hMainMenu, IDM_SAVE_SETTINGS, 3605 MF_BYCOMMAND | (Settings.bSaveSettings ? MF_CHECKED : MF_UNCHECKED)); 3606 break; 3607 } 3608 3609 case IDM_ABOUT: 3610 { 3611 HICON hIcon; 3612 WCHAR szCopyright[MAX_LOADSTRING]; 3613 3614 hIcon = LoadIconW(hInst, MAKEINTRESOURCEW(IDI_EVENTVWR)); 3615 LoadStringW(hInst, IDS_COPYRIGHT, szCopyright, ARRAYSIZE(szCopyright)); 3616 ShellAboutW(hWnd, szTitle, szCopyright, hIcon); 3617 DeleteObject(hIcon); 3618 break; 3619 } 3620 3621 case IDM_HELP: 3622 MessageBoxW(hwndMainWindow, 3623 L"Help not implemented yet!", 3624 L"Event Log", 3625 MB_OK | MB_ICONINFORMATION); 3626 break; 3627 3628 case IDM_EXIT: 3629 DestroyWindow(hWnd); 3630 break; 3631 3632 default: 3633 return DefWindowProcW(hWnd, uMsg, wParam, lParam); 3634 } 3635 break; 3636 } 3637 3638 case WM_INITMENU: 3639 { 3640 if ((HMENU)wParam != hMainMenu) 3641 break; 3642 3643 CheckMenuRadioItem(hMainMenu, IDM_LIST_NEWEST, IDM_LIST_OLDEST, 3644 Settings.bNewestEventsFirst ? IDM_LIST_NEWEST : IDM_LIST_OLDEST, 3645 MF_BYCOMMAND); 3646 3647 if (!hwndEventDetails) 3648 { 3649 EnableMenuItem(hMainMenu, IDM_EVENT_DETAILS_VIEW, 3650 MF_BYCOMMAND | MF_GRAYED); 3651 } 3652 CheckMenuItem(hMainMenu, IDM_EVENT_DETAILS_VIEW, 3653 MF_BYCOMMAND | (Settings.bShowDetailsPane ? MF_CHECKED : MF_UNCHECKED)); 3654 3655 CheckMenuItem(hMainMenu, IDM_LIST_GRID_LINES, 3656 MF_BYCOMMAND | (Settings.bShowGrid ? MF_CHECKED : MF_UNCHECKED)); 3657 3658 CheckMenuItem(hMainMenu, IDM_SAVE_SETTINGS, 3659 MF_BYCOMMAND | (Settings.bSaveSettings ? MF_CHECKED : MF_UNCHECKED)); 3660 3661 break; 3662 } 3663 3664 #if 0 3665 case WM_INITMENUPOPUP: 3666 lParam = lParam; 3667 break; 3668 3669 case WM_CONTEXTMENU: 3670 lParam = lParam; 3671 break; 3672 #endif 3673 3674 case WM_SETCURSOR: 3675 { 3676 POINT pt; 3677 3678 if (LOWORD(lParam) != HTCLIENT) 3679 goto Default; 3680 3681 GetCursorPos(&pt); 3682 ScreenToClient(hWnd, &pt); 3683 3684 /* Set the cursor for the vertical splitter */ 3685 if (pt.x >= nVSplitPos - SPLIT_WIDTH/2 && pt.x < nVSplitPos + SPLIT_WIDTH/2 + 1) 3686 { 3687 RECT rs; 3688 GetClientRect(hWnd, &rect); 3689 GetWindowRect(hwndStatus, &rs); 3690 if (pt.y >= rect.top && pt.y < rect.bottom - (rs.bottom - rs.top)) 3691 { 3692 SetCursor(LoadCursorW(NULL, IDC_SIZEWE)); 3693 return TRUE; 3694 } 3695 } 3696 else 3697 /* Set the cursor for the horizontal splitter, if the Event details pane is displayed */ 3698 if (hwndEventDetails && Settings.bShowDetailsPane && 3699 (pt.y >= nHSplitPos - SPLIT_WIDTH/2 && pt.y < nHSplitPos + SPLIT_WIDTH/2 + 1)) 3700 { 3701 // RECT rs; 3702 GetClientRect(hWnd, &rect); 3703 // GetWindowRect(hwndStatus, &rs); 3704 if (pt.x >= nVSplitPos + SPLIT_WIDTH/2 + 1 /* rect.left + (rs.bottom - rs.top) */ && 3705 pt.x < rect.right) 3706 { 3707 SetCursor(LoadCursorW(NULL, IDC_SIZENS)); 3708 return TRUE; 3709 } 3710 } 3711 goto Default; 3712 } 3713 3714 case WM_LBUTTONDOWN: 3715 { 3716 INT x = GET_X_LPARAM(lParam); 3717 INT y = GET_Y_LPARAM(lParam); 3718 3719 /* Reset the splitter state */ 3720 bSplit = 0; 3721 3722 /* Capture the cursor for the vertical splitter */ 3723 if (x >= nVSplitPos - SPLIT_WIDTH/2 && x < nVSplitPos + SPLIT_WIDTH/2 + 1) 3724 { 3725 bSplit = 1; 3726 SetCapture(hWnd); 3727 } 3728 else 3729 /* Capture the cursor for the horizontal splitter, if the Event details pane is displayed */ 3730 if (hwndEventDetails && Settings.bShowDetailsPane && 3731 (y >= nHSplitPos - SPLIT_WIDTH/2 && y < nHSplitPos + SPLIT_WIDTH/2 + 1)) 3732 { 3733 bSplit = 2; 3734 SetCapture(hWnd); 3735 } 3736 break; 3737 } 3738 3739 case WM_LBUTTONUP: 3740 case WM_RBUTTONDOWN: 3741 { 3742 if (GetCapture() != hWnd) 3743 break; 3744 3745 /* Adjust the correct splitter position */ 3746 if (bSplit == 1) 3747 nVSplitPos = GET_X_LPARAM(lParam); 3748 else if (bSplit == 2) 3749 nHSplitPos = GET_Y_LPARAM(lParam); 3750 3751 /* If we are splitting, resize the windows */ 3752 if (bSplit != 0) 3753 { 3754 GetClientRect(hWnd, &rect); 3755 ResizeWnd(rect.right - rect.left, rect.bottom - rect.top); 3756 } 3757 3758 /* Reset the splitter state */ 3759 bSplit = 0; 3760 3761 ReleaseCapture(); 3762 break; 3763 } 3764 3765 case WM_MOUSEMOVE: 3766 { 3767 if (GetCapture() != hWnd) 3768 break; 3769 3770 /* Move the correct splitter */ 3771 if (bSplit == 1) 3772 { 3773 INT x = GET_X_LPARAM(lParam); 3774 3775 GetClientRect(hWnd, &rect); 3776 3777 x = min(max(x, SPLIT_WIDTH/2), rect.right - rect.left - SPLIT_WIDTH/2); 3778 if (nVSplitPos != x) 3779 { 3780 nVSplitPos = x; 3781 ResizeWnd(rect.right - rect.left, rect.bottom - rect.top); 3782 } 3783 } 3784 else if (bSplit == 2) 3785 { 3786 RECT rs; 3787 INT y = GET_Y_LPARAM(lParam); 3788 3789 GetClientRect(hWnd, &rect); 3790 GetWindowRect(hwndStatus, &rs); 3791 3792 y = min(max(y, SPLIT_WIDTH/2), rect.bottom - rect.top - SPLIT_WIDTH/2 - (rs.bottom - rs.top)); 3793 if (nHSplitPos != y) 3794 { 3795 nHSplitPos = y; 3796 ResizeWnd(rect.right - rect.left, rect.bottom - rect.top); 3797 } 3798 } 3799 break; 3800 } 3801 3802 case WM_SIZE: 3803 { 3804 if (wParam != SIZE_MINIMIZED) 3805 { 3806 SendMessageW(hwndStatus, WM_SIZE, 0, 0); 3807 ResizeWnd(LOWORD(lParam), HIWORD(lParam)); 3808 break; 3809 } 3810 /* Fall through the default case */ 3811 } 3812 3813 default: Default: 3814 return DefWindowProcW(hWnd, uMsg, wParam, lParam); 3815 } 3816 3817 return 0; 3818 } 3819 3820 3821 static 3822 VOID 3823 InitPropertiesDlg(HWND hDlg, PEVENTLOG EventLog) 3824 { 3825 LPWSTR lpLogName = EventLog->LogName; 3826 3827 DWORD Result, dwType; 3828 DWORD dwMaxSize = 0, dwRetention = 0; 3829 BOOL Success; 3830 WIN32_FIND_DATAW FileInfo; // WIN32_FILE_ATTRIBUTE_DATA 3831 ULARGE_INTEGER FileSize; 3832 WCHAR wszBuf[MAX_PATH]; 3833 WCHAR szTemp[MAX_LOADSTRING]; 3834 LPWSTR FileName; 3835 3836 HKEY hLogKey; 3837 WCHAR *KeyPath; 3838 DWORD cbData; 3839 SIZE_T cbKeyPath; 3840 3841 if (EventLog->Permanent) 3842 { 3843 3844 cbKeyPath = (wcslen(EVENTLOG_BASE_KEY) + wcslen(lpLogName) + 1) * sizeof(WCHAR); 3845 KeyPath = HeapAlloc(GetProcessHeap(), 0, cbKeyPath); 3846 if (!KeyPath) 3847 { 3848 goto Quit; 3849 } 3850 3851 StringCbCopyW(KeyPath, cbKeyPath, EVENTLOG_BASE_KEY); 3852 StringCbCatW(KeyPath, cbKeyPath, lpLogName); 3853 3854 if (RegOpenKeyExW(hkMachine, KeyPath, 0, KEY_QUERY_VALUE, &hLogKey) != ERROR_SUCCESS) 3855 { 3856 HeapFree(GetProcessHeap(), 0, KeyPath); 3857 goto Quit; 3858 } 3859 HeapFree(GetProcessHeap(), 0, KeyPath); 3860 3861 3862 cbData = sizeof(dwMaxSize); 3863 Result = RegQueryValueExW(hLogKey, 3864 L"MaxSize", 3865 NULL, 3866 &dwType, 3867 (LPBYTE)&dwMaxSize, 3868 &cbData); 3869 if ((Result != ERROR_SUCCESS) || (dwType != REG_DWORD)) 3870 { 3871 // dwMaxSize = 512 * 1024; /* 512 kBytes */ 3872 dwMaxSize = 0; 3873 } 3874 /* Convert in KB */ 3875 dwMaxSize /= 1024; 3876 3877 cbData = sizeof(dwRetention); 3878 Result = RegQueryValueExW(hLogKey, 3879 L"Retention", 3880 NULL, 3881 &dwType, 3882 (LPBYTE)&dwRetention, 3883 &cbData); 3884 if ((Result != ERROR_SUCCESS) || (dwType != REG_DWORD)) 3885 { 3886 /* On Windows 2003 it is 604800 (secs) == 7 days */ 3887 dwRetention = 0; 3888 } 3889 /* Convert in days, rounded up */ // ROUND_UP 3890 // dwRetention = ROUND_UP(dwRetention, 24*3600) / (24*3600); 3891 dwRetention = (dwRetention + 24*3600 - 1) / (24*3600); 3892 3893 3894 RegCloseKey(hLogKey); 3895 3896 } 3897 3898 3899 Quit: 3900 3901 SetDlgItemTextW(hDlg, IDC_DISPLAYNAME, lpLogName); // FIXME! 3902 SetDlgItemTextW(hDlg, IDC_LOGNAME, lpLogName); 3903 3904 FileName = EventLog->FileName; 3905 if (FileName && *FileName) 3906 { 3907 /* Expand the file name. If the log file is on a remote computer, retrieve the network share form of the file name. */ 3908 GetExpandedFilePathName(EventLog->ComputerName, FileName, wszBuf, ARRAYSIZE(wszBuf)); 3909 FileName = wszBuf; 3910 } 3911 else 3912 { 3913 FileName = L""; 3914 } 3915 SetDlgItemTextW(hDlg, IDC_LOGFILE, FileName); 3916 3917 if (FileName && *FileName) 3918 { 3919 /* 3920 * The general problem here (and in the shell as well) is that 3921 * GetFileAttributesEx fails for files that are opened without 3922 * shared access. To retrieve file information for those we need 3923 * to use something else: FindFirstFile, on the full file name. 3924 */ 3925 Success = GetFileAttributesExW(FileName, 3926 GetFileExInfoStandard, 3927 (LPWIN32_FILE_ATTRIBUTE_DATA)&FileInfo); 3928 if (!Success) 3929 { 3930 HANDLE hFind = FindFirstFileW(FileName, &FileInfo); 3931 Success = (hFind != INVALID_HANDLE_VALUE); 3932 if (Success) 3933 FindClose(hFind); 3934 } 3935 } 3936 else 3937 { 3938 Success = FALSE; 3939 } 3940 3941 /* Starting there, FileName becomes invalid because we are reusing wszBuf */ 3942 3943 if (Success) 3944 { 3945 FileSize.u.LowPart = FileInfo.nFileSizeLow; 3946 FileSize.u.HighPart = FileInfo.nFileSizeHigh; 3947 if (FormatFileSizeWithBytes(&FileSize, wszBuf, ARRAYSIZE(wszBuf))) 3948 SetDlgItemTextW(hDlg, IDC_SIZE_LABEL, wszBuf); 3949 3950 LoadStringW(hInst, IDS_NOT_AVAILABLE, szTemp, ARRAYSIZE(szTemp)); 3951 3952 if (GetFileTimeString(&FileInfo.ftCreationTime, wszBuf, ARRAYSIZE(wszBuf))) 3953 SetDlgItemTextW(hDlg, IDC_CREATED_LABEL, wszBuf); 3954 else 3955 SetDlgItemTextW(hDlg, IDC_CREATED_LABEL, szTemp); 3956 3957 if (GetFileTimeString(&FileInfo.ftLastWriteTime, wszBuf, ARRAYSIZE(wszBuf))) 3958 SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, wszBuf); 3959 else 3960 SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, szTemp); 3961 3962 if (GetFileTimeString(&FileInfo.ftLastAccessTime, wszBuf, ARRAYSIZE(wszBuf))) 3963 SetDlgItemTextW(hDlg, IDC_ACCESSED_LABEL, wszBuf); 3964 else 3965 SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, szTemp); 3966 } 3967 else 3968 { 3969 LoadStringW(hInst, IDS_NOT_AVAILABLE, szTemp, ARRAYSIZE(szTemp)); 3970 3971 SetDlgItemTextW(hDlg, IDC_SIZE_LABEL, szTemp); 3972 SetDlgItemTextW(hDlg, IDC_CREATED_LABEL, szTemp); 3973 SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, szTemp); 3974 SetDlgItemTextW(hDlg, IDC_ACCESSED_LABEL, szTemp); 3975 } 3976 3977 if (EventLog->Permanent) 3978 { 3979 SendDlgItemMessageW(hDlg, IDC_UPDOWN_MAXLOGSIZE, UDM_SETRANGE32, (WPARAM)1, (LPARAM)0x3FFFC0); 3980 SendDlgItemMessageW(hDlg, IDC_UPDOWN_EVENTS_AGE, UDM_SETRANGE, 0, (LPARAM)MAKELONG(365, 1)); 3981 3982 SetDlgItemInt(hDlg, IDC_EDIT_MAXLOGSIZE, dwMaxSize, FALSE); 3983 SetDlgItemInt(hDlg, IDC_EDIT_EVENTS_AGE, dwRetention, FALSE); 3984 3985 if (dwRetention == 0) 3986 { 3987 CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_OVERWRITE_AS_NEEDED); 3988 EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, FALSE); 3989 EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, FALSE); 3990 } 3991 else if (dwRetention == INFINITE) 3992 { 3993 CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_NO_OVERWRITE); 3994 EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, FALSE); 3995 EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, FALSE); 3996 } 3997 else 3998 { 3999 CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_OVERWRITE_OLDER_THAN); 4000 EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, TRUE); 4001 EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, TRUE); 4002 } 4003 } 4004 else 4005 { 4006 // TODO: Hide the unused controls! Or, just use another type of property sheet! 4007 } 4008 } 4009 4010 /* Message handler for EventLog Properties dialog */ 4011 INT_PTR CALLBACK 4012 EventLogPropProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) 4013 { 4014 PEVENTLOG EventLog; 4015 4016 EventLog = (PEVENTLOG)GetWindowLongPtrW(hDlg, DWLP_USER); 4017 4018 switch (uMsg) 4019 { 4020 case WM_INITDIALOG: 4021 { 4022 EventLog = (PEVENTLOG)((LPPROPSHEETPAGE)lParam)->lParam; 4023 SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)EventLog); 4024 4025 InitPropertiesDlg(hDlg, EventLog); 4026 4027 PropSheet_UnChanged(GetParent(hDlg), hDlg); 4028 return (INT_PTR)TRUE; 4029 } 4030 4031 case WM_DESTROY: 4032 return (INT_PTR)TRUE; 4033 4034 case WM_COMMAND: 4035 switch (LOWORD(wParam)) 4036 { 4037 case IDOK: 4038 case IDCANCEL: 4039 EndDialog(hDlg, LOWORD(wParam)); 4040 return (INT_PTR)TRUE; 4041 4042 case IDC_OVERWRITE_AS_NEEDED: 4043 { 4044 CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_OVERWRITE_AS_NEEDED); 4045 EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, FALSE); 4046 EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, FALSE); 4047 break; 4048 } 4049 4050 case IDC_OVERWRITE_OLDER_THAN: 4051 { 4052 CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_OVERWRITE_OLDER_THAN); 4053 EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, TRUE); 4054 EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, TRUE); 4055 break; 4056 } 4057 4058 case IDC_NO_OVERWRITE: 4059 { 4060 CheckRadioButton(hDlg, IDC_OVERWRITE_AS_NEEDED, IDC_NO_OVERWRITE, IDC_NO_OVERWRITE); 4061 EnableDlgItem(hDlg, IDC_EDIT_EVENTS_AGE, FALSE); 4062 EnableDlgItem(hDlg, IDC_UPDOWN_EVENTS_AGE, FALSE); 4063 break; 4064 } 4065 4066 case IDHELP: 4067 MessageBoxW(hDlg, 4068 L"Help not implemented yet!", 4069 L"Event Log", 4070 MB_OK | MB_ICONINFORMATION); 4071 return (INT_PTR)TRUE; 4072 4073 default: 4074 break; 4075 } 4076 break; 4077 } 4078 4079 return (INT_PTR)FALSE; 4080 } 4081 4082 INT_PTR 4083 EventLogProperties(HINSTANCE hInstance, HWND hWndParent, PEVENTLOGFILTER EventLogFilter) 4084 { 4085 INT_PTR ret = 0; 4086 PROPSHEETHEADERW psh; 4087 PROPSHEETPAGEW psp[1]; // 2 4088 4089 /* 4090 * Bail out if there is no available filter, or if the filter 4091 * contains more than one log. 4092 */ 4093 if (!EventLogFilter) 4094 return 0; 4095 4096 EventLogFilter_AddRef(EventLogFilter); 4097 4098 if (EventLogFilter->NumOfEventLogs > 1 || 4099 EventLogFilter->EventLogs[0] == NULL) 4100 { 4101 goto Quit; 4102 } 4103 4104 /* Header */ 4105 psh.dwSize = sizeof(psh); 4106 psh.dwFlags = PSH_PROPSHEETPAGE /*| PSH_USEICONID */ | PSH_PROPTITLE | PSH_HASHELP /*| PSH_NOCONTEXTHELP */ /*| PSH_USECALLBACK */; 4107 psh.hInstance = hInstance; 4108 psh.hwndParent = hWndParent; 4109 // psh.pszIcon = MAKEINTRESOURCEW(IDI_APPICON); // Disabled because it only sets the small icon; the big icon is a stretched version of the small one. 4110 psh.pszCaption = EventLogFilter->EventLogs[0]->LogName; 4111 psh.nStartPage = 0; 4112 psh.ppsp = psp; 4113 psh.nPages = ARRAYSIZE(psp); 4114 // psh.pfnCallback = PropSheetCallback; 4115 4116 /* Log properties page */ 4117 psp[0].dwSize = sizeof(psp[0]); 4118 psp[0].dwFlags = PSP_HASHELP; 4119 psp[0].hInstance = hInstance; 4120 psp[0].pszTemplate = MAKEINTRESOURCEW(IDD_LOGPROPERTIES_GENERAL); 4121 psp[0].pfnDlgProc = EventLogPropProc; 4122 psp[0].lParam = (LPARAM)EventLogFilter->EventLogs[0]; 4123 4124 #if 0 4125 /* TODO: Log sources page */ 4126 psp[1].dwSize = sizeof(psp[1]); 4127 psp[1].dwFlags = PSP_HASHELP; 4128 psp[1].hInstance = hInstance; 4129 psp[1].pszTemplate = MAKEINTRESOURCEW(IDD_GENERAL_PAGE); 4130 psp[1].pfnDlgProc = GeneralPageWndProc; 4131 psp[0].lParam = (LPARAM)EventLogFilter->EventLogs[0]; 4132 #endif 4133 4134 /* Create the property sheet */ 4135 ret = PropertySheetW(&psh); 4136 4137 Quit: 4138 EventLogFilter_Release(EventLogFilter); 4139 return ret; 4140 } 4141 4142 /* Message handler for Event Details dialog */ 4143 static HWND hWndDetailsCtrl = NULL; // May go into the DWLP_USER 4144 static HWND hWndGrip = NULL; 4145 static INT cxMin, cyMin; // In window coordinates 4146 static INT cxOld, cyOld; // In client coordinates 4147 4148 INT_PTR CALLBACK 4149 EventDetails(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) 4150 { 4151 switch (uMsg) 4152 { 4153 case WM_INITDIALOG: 4154 { 4155 LONG_PTR dwStyle; 4156 INT sbVXSize, sbHYSize; 4157 RECT rcWnd, rect; 4158 4159 hWndDetailsCtrl = CreateEventDetailsCtrl(hInst, hDlg, lParam); 4160 if (!hWndDetailsCtrl) 4161 { 4162 EndDialog(hDlg, 0); 4163 return (INT_PTR)TRUE; 4164 } 4165 4166 /* Create a size grip if the dialog has a sizing border */ 4167 GetClientRect(hDlg, &rcWnd); 4168 dwStyle = GetWindowLongPtrW(hDlg, GWL_STYLE); 4169 sbVXSize = GetSystemMetrics(SM_CXVSCROLL); 4170 sbHYSize = GetSystemMetrics(SM_CYHSCROLL); 4171 if (dwStyle & WS_THICKFRAME /* == WS_SIZEBOX */) 4172 { 4173 hWndGrip = CreateWindowW(WC_SCROLLBARW, 4174 NULL, 4175 WS_CHILD | WS_VISIBLE | /**/ WS_CLIPSIBLINGS | /**/ SBS_SIZEGRIP | SBS_SIZEBOXBOTTOMRIGHTALIGN, 4176 rcWnd.right - sbVXSize, 4177 rcWnd.bottom - sbHYSize, 4178 sbVXSize, sbHYSize, 4179 hDlg, 4180 NULL, 4181 hInst, 4182 NULL); 4183 } 4184 4185 // SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)hWndDetailsCtrl); 4186 4187 /* 4188 * Compute the minimum window size (in window coordinates) by 4189 * adding the widths/heights of the "Help" and "Close" buttons, 4190 * together with the margins, and add some minimal spacing 4191 * between the buttons. 4192 */ 4193 GetWindowRect(hDlg, &rcWnd); 4194 cxMin = cyMin = 0; 4195 4196 GetWindowRect(GetDlgItem(hDlg, IDHELP), &rect); 4197 cxMin += (rect.right - rect.left) + (rect.left - rcWnd.left); // == (rect.right - rcWnd.left); 4198 cyMin += (rect.bottom - rect.top) + (rcWnd.bottom - rect.bottom); // == (rcWnd.bottom - rect.top); 4199 4200 GetWindowRect(GetDlgItem(hDlg, IDOK), &rect); 4201 cxMin += (rect.right - rect.left) + (rcWnd.right - rect.right); // == (rcWnd.right - rect.left); 4202 cyMin += (rect.bottom - rect.top) + (rcWnd.bottom - rect.bottom); // == (rcWnd.bottom - rect.top); 4203 4204 /* 4205 * Convert the window rect from window to client coordinates 4206 * in order to retrieve the sizes of the left and top margins, 4207 * and add some extra space. 4208 */ 4209 MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rcWnd, sizeof(RECT)/sizeof(POINT)); 4210 4211 cxMin += -2*rcWnd.left; // Minimal spacing between the buttons == 2 * left margin 4212 cyMin += -rcWnd.top + 12; // Add some space on top 4213 4214 GetClientRect(hDlg, &rcWnd); 4215 cxOld = rcWnd.right - rcWnd.left; 4216 cyOld = rcWnd.bottom - rcWnd.top; 4217 4218 /* Show event info on dialog control */ 4219 SendMessageW(hWndDetailsCtrl, EVT_DISPLAY, 0, 0); 4220 4221 // SetWindowPos(hWndDetailsCtrl, NULL, 4222 // 0, 0, 4223 // (rcWnd.right - rcWnd.left), 4224 // (rcWnd.bottom - rcWnd.top), 4225 // SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW); 4226 4227 /* 4228 * Hide the placeholder static control and show the event details 4229 * control instead. Note that the placeholder is here so far just 4230 * to get the dimensions right in the dialog resource editor. 4231 * I plan to remove it and use a custom control with a suitable 4232 * window class for it, that would create the event details control 4233 * instead. 4234 */ 4235 ShowWindow(GetDlgItem(hDlg, IDC_STATIC), SW_HIDE); 4236 ShowWindow(hWndDetailsCtrl, SW_SHOW); 4237 return (INT_PTR)TRUE; 4238 } 4239 4240 case WM_DESTROY: 4241 if (IsWindow(hWndDetailsCtrl)) 4242 DestroyWindow(hWndDetailsCtrl); 4243 hWndDetailsCtrl = NULL; 4244 return (INT_PTR)TRUE; 4245 4246 case WM_COMMAND: 4247 switch (LOWORD(wParam)) 4248 { 4249 case IDOK: 4250 case IDCANCEL: 4251 EndDialog(hDlg, LOWORD(wParam)); 4252 return (INT_PTR)TRUE; 4253 4254 case IDHELP: 4255 MessageBoxW(hDlg, 4256 L"Help not implemented yet!", 4257 L"Event Log", 4258 MB_OK | MB_ICONINFORMATION); 4259 return (INT_PTR)TRUE; 4260 4261 default: 4262 break; 4263 } 4264 break; 4265 4266 case WM_SETCURSOR: 4267 if (((HWND)wParam == hWndGrip) && (LOWORD(lParam) == HTCLIENT)) 4268 { 4269 SetCursor(LoadCursorW(NULL, IDC_SIZENWSE)); 4270 SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, TRUE); 4271 return (INT_PTR)TRUE; 4272 } 4273 break; 4274 4275 case WM_SIZING: 4276 { 4277 /* Forbid resizing the dialog smaller than its minimal size */ 4278 PRECT dragRect = (PRECT)lParam; 4279 4280 if ((wParam == WMSZ_LEFT) || (wParam == WMSZ_TOPLEFT) || (wParam == WMSZ_BOTTOMLEFT)) 4281 { 4282 if (dragRect->right - dragRect->left < cxMin) 4283 dragRect->left = dragRect->right - cxMin; 4284 } 4285 4286 if ((wParam == WMSZ_RIGHT) || (wParam == WMSZ_TOPRIGHT) || (wParam == WMSZ_BOTTOMRIGHT)) 4287 { 4288 if (dragRect->right - dragRect->left < cxMin) 4289 dragRect->right = dragRect->left + cxMin; 4290 } 4291 4292 if ((wParam == WMSZ_TOP) || (wParam == WMSZ_TOPLEFT) || (wParam == WMSZ_TOPRIGHT)) 4293 { 4294 if (dragRect->bottom - dragRect->top < cyMin) 4295 dragRect->top = dragRect->bottom - cyMin; 4296 } 4297 4298 if ((wParam == WMSZ_BOTTOM) || (wParam == WMSZ_BOTTOMLEFT) || (wParam == WMSZ_BOTTOMRIGHT)) 4299 { 4300 if (dragRect->bottom - dragRect->top < cyMin) 4301 dragRect->bottom = dragRect->top + cyMin; 4302 } 4303 4304 SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, TRUE); 4305 return (INT_PTR)TRUE; 4306 } 4307 4308 case WM_SIZE: 4309 { 4310 INT cx = LOWORD(lParam); 4311 INT cy = HIWORD(lParam); 4312 4313 HDWP hdwp; 4314 HWND hItemWnd; 4315 RECT rect; 4316 4317 hdwp = BeginDeferWindowPos(4); 4318 4319 /* Resize the event details control window */ 4320 4321 hItemWnd = hWndDetailsCtrl; 4322 GetWindowRect(hItemWnd, &rect); 4323 MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT)); 4324 4325 if (hdwp) 4326 hdwp = DeferWindowPos(hdwp, 4327 hItemWnd, 4328 HWND_TOP, 4329 0, 0, 4330 (rect.right - rect.left) + (cx - cxOld), 4331 (rect.bottom - rect.top) + (cy - cyOld), 4332 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); 4333 4334 /* Move the buttons */ 4335 4336 hItemWnd = GetDlgItem(hDlg, IDHELP); 4337 GetWindowRect(hItemWnd, &rect); 4338 MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT)); 4339 4340 if (hdwp) 4341 hdwp = DeferWindowPos(hdwp, 4342 hItemWnd, 4343 HWND_TOP, 4344 rect.left, 4345 rect.top + (cy - cyOld), 4346 0, 0, 4347 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); 4348 4349 hItemWnd = GetDlgItem(hDlg, IDOK); 4350 GetWindowRect(hItemWnd, &rect); 4351 MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT)); 4352 4353 if (hdwp) 4354 hdwp = DeferWindowPos(hdwp, 4355 hItemWnd, 4356 HWND_TOP, 4357 rect.left + (cx - cxOld), 4358 rect.top + (cy - cyOld), 4359 0, 0, 4360 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); 4361 4362 /* Move the size grip */ 4363 if (hWndGrip && hdwp) 4364 { 4365 GetWindowRect(hWndGrip, &rect); 4366 MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT)); 4367 4368 hdwp = DeferWindowPos(hdwp, 4369 hWndGrip, 4370 HWND_TOP, 4371 rect.left + (cx - cxOld), 4372 rect.top + (cy - cyOld), 4373 0, 0, 4374 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); 4375 } 4376 4377 if (hdwp) 4378 EndDeferWindowPos(hdwp); 4379 4380 /* Hide the size grip if we are in maximized mode */ 4381 if (hWndGrip) 4382 ShowWindow(hWndGrip, (wParam == SIZE_MAXIMIZED) ? SW_HIDE : SW_SHOW); 4383 4384 cxOld = cx; 4385 cyOld = cy; 4386 4387 SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, 0); 4388 return (INT_PTR)TRUE; 4389 } 4390 } 4391 4392 return (INT_PTR)FALSE; 4393 } 4394