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