1 /* 2 * PROJECT: ReactOS User API Server DLL 3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) 4 * PURPOSE: Hard errors support. 5 * COPYRIGHT: Copyright 2007-2018 Dmitry Philippov (shedon@mail.ru) 6 * Copyright 2010-2018 Timo Kreuzer (timo.kreuzer@reactos.org) 7 * Copyright 2012-2018 Hermes Belusca-Maito 8 * Copyright 2018 Giannis Adamopoulos 9 */ 10 11 /* INCLUDES *******************************************************************/ 12 13 #include "usersrv.h" 14 15 #define NTOS_MODE_USER 16 #include <ndk/mmfuncs.h> 17 18 #include <undocelfapi.h> 19 #include <ntstrsafe.h> 20 21 #include "resource.h" 22 23 #define NDEBUG 24 #include <debug.h> 25 26 27 /* FUNCTIONS ******************************************************************/ 28 29 /* Cache for the localized hard-error message box strings */ 30 LANGID g_CurrentUserLangId = 0; 31 UNICODE_STRING g_SuccessU = {0, 0, NULL}; 32 UNICODE_STRING g_InformationU = {0, 0, NULL}; 33 UNICODE_STRING g_WarningU = {0, 0, NULL}; 34 UNICODE_STRING g_ErrorU = {0, 0, NULL}; 35 UNICODE_STRING g_SystemProcessU = {0, 0, NULL}; 36 UNICODE_STRING g_OKTerminateU = {0, 0, NULL}; 37 UNICODE_STRING g_CancelDebugU = {0, 0, NULL}; 38 39 VOID 40 RtlLoadUnicodeString( 41 IN HINSTANCE hInstance OPTIONAL, 42 IN UINT uID, 43 OUT PUNICODE_STRING pUnicodeString, 44 IN PCWSTR pDefaultString) 45 { 46 UINT Length; 47 48 /* Try to load the string from the resource */ 49 Length = LoadStringW(hInstance, uID, (LPWSTR)&pUnicodeString->Buffer, 0); 50 if (Length == 0) 51 { 52 /* If the resource string was not found, use the fallback default one */ 53 RtlInitUnicodeString(pUnicodeString, pDefaultString); 54 } 55 else 56 { 57 /* Set the string length (not NULL-terminated!) */ 58 pUnicodeString->MaximumLength = (USHORT)(Length * sizeof(WCHAR)); 59 pUnicodeString->Length = pUnicodeString->MaximumLength; 60 } 61 } 62 63 64 /* 65 * NOTE: _scwprintf() is NOT exported by ntdll.dll, 66 * only _vscwprintf() is, so we need to implement it here. 67 * Code comes from sdk/lib/crt/printf/_scwprintf.c . 68 */ 69 int 70 __cdecl 71 _scwprintf( 72 const wchar_t *format, 73 ...) 74 { 75 int len; 76 va_list args; 77 78 va_start(args, format); 79 len = _vscwprintf(format, args); 80 va_end(args); 81 82 return len; 83 } 84 85 86 /* FIXME */ 87 int 88 WINAPI 89 MessageBoxTimeoutW( 90 HWND hWnd, 91 LPCWSTR lpText, 92 LPCWSTR lpCaption, 93 UINT uType, 94 WORD wLanguageId, 95 DWORD dwTime); 96 97 98 static 99 VOID 100 UserpCaptureStringParameters( 101 OUT PULONG_PTR Parameters, 102 OUT PULONG SizeOfAllUnicodeStrings, 103 IN PHARDERROR_MSG Message, 104 IN HANDLE hProcess OPTIONAL) 105 { 106 NTSTATUS Status; 107 ULONG nParam, Size = 0; 108 UNICODE_STRING TempStringU, ParamStringU; 109 ANSI_STRING TempStringA; 110 111 if (SizeOfAllUnicodeStrings) 112 *SizeOfAllUnicodeStrings = 0; 113 114 /* Read all strings from client space */ 115 for (nParam = 0; nParam < Message->NumberOfParameters; ++nParam) 116 { 117 Parameters[nParam] = 0; 118 119 /* Check if the current parameter is a unicode string */ 120 if (Message->UnicodeStringParameterMask & (1 << nParam)) 121 { 122 /* Skip this string if we do not have a client process */ 123 if (!hProcess) 124 continue; 125 126 /* Read the UNICODE_STRING from the process memory */ 127 Status = NtReadVirtualMemory(hProcess, 128 (PVOID)Message->Parameters[nParam], 129 &ParamStringU, 130 sizeof(ParamStringU), 131 NULL); 132 if (!NT_SUCCESS(Status)) 133 { 134 /* We failed, skip this string */ 135 DPRINT1("NtReadVirtualMemory(Message->Parameters) failed, Status 0x%lx, skipping.\n", Status); 136 continue; 137 } 138 139 /* Allocate a buffer for the string and reserve a NULL terminator */ 140 TempStringU.MaximumLength = ParamStringU.Length + sizeof(UNICODE_NULL); 141 TempStringU.Length = ParamStringU.Length; 142 TempStringU.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 143 HEAP_ZERO_MEMORY, 144 TempStringU.MaximumLength); 145 if (!TempStringU.Buffer) 146 { 147 /* We failed, skip this string */ 148 DPRINT1("Cannot allocate memory with size %u, skipping.\n", TempStringU.MaximumLength); 149 continue; 150 } 151 152 /* Read the string buffer from the process memory */ 153 Status = NtReadVirtualMemory(hProcess, 154 ParamStringU.Buffer, 155 TempStringU.Buffer, 156 ParamStringU.Length, 157 NULL); 158 if (!NT_SUCCESS(Status)) 159 { 160 /* We failed, skip this string */ 161 DPRINT1("NtReadVirtualMemory(ParamStringU) failed, Status 0x%lx, skipping.\n", Status); 162 RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringU.Buffer); 163 continue; 164 } 165 /* NULL-terminate the string */ 166 TempStringU.Buffer[TempStringU.Length / sizeof(WCHAR)] = UNICODE_NULL; 167 168 DPRINT("ParamString = \'%wZ\'\n", &TempStringU); 169 170 if (Message->Status == STATUS_SERVICE_NOTIFICATION) 171 { 172 /* Just keep the allocated NULL-terminated UNICODE string */ 173 Parameters[nParam] = (ULONG_PTR)TempStringU.Buffer; 174 Size += TempStringU.Length; 175 } 176 else 177 { 178 /* Allocate a buffer for conversion to ANSI string */ 179 TempStringA.MaximumLength = (USHORT)RtlUnicodeStringToAnsiSize(&TempStringU); 180 TempStringA.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 181 HEAP_ZERO_MEMORY, 182 TempStringA.MaximumLength); 183 if (!TempStringA.Buffer) 184 { 185 /* We failed, skip this string */ 186 DPRINT1("Cannot allocate memory with size %u, skipping.\n", TempStringA.MaximumLength); 187 RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringU.Buffer); 188 continue; 189 } 190 191 /* Convert string to ANSI and free temporary buffer */ 192 Status = RtlUnicodeStringToAnsiString(&TempStringA, &TempStringU, FALSE); 193 RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringU.Buffer); 194 if (!NT_SUCCESS(Status)) 195 { 196 /* We failed, skip this string */ 197 DPRINT1("RtlUnicodeStringToAnsiString() failed, Status 0x%lx, skipping.\n", Status); 198 RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringA.Buffer); 199 continue; 200 } 201 202 /* Note: RtlUnicodeStringToAnsiString() returns a NULL-terminated string */ 203 Parameters[nParam] = (ULONG_PTR)TempStringA.Buffer; 204 Size += TempStringU.Length; 205 } 206 } 207 else 208 { 209 /* It's not a unicode string, just copy the parameter */ 210 Parameters[nParam] = Message->Parameters[nParam]; 211 } 212 } 213 214 if (SizeOfAllUnicodeStrings) 215 *SizeOfAllUnicodeStrings = Size; 216 } 217 218 static 219 VOID 220 UserpFreeStringParameters( 221 IN OUT PULONG_PTR Parameters, 222 IN PHARDERROR_MSG Message) 223 { 224 ULONG nParam; 225 226 /* Loop all parameters */ 227 for (nParam = 0; nParam < Message->NumberOfParameters; ++nParam) 228 { 229 /* Check if the current parameter is a string */ 230 if ((Message->UnicodeStringParameterMask & (1 << nParam)) && (Parameters[nParam] != 0)) 231 { 232 /* Free the string buffer */ 233 RtlFreeHeap(RtlGetProcessHeap(), 0, (PVOID)Parameters[nParam]); 234 } 235 } 236 } 237 238 static 239 NTSTATUS 240 UserpGetClientFileName( 241 OUT PUNICODE_STRING ClientFileNameU, 242 IN HANDLE hProcess) 243 { 244 PLIST_ENTRY ModuleListHead; 245 PLIST_ENTRY Entry; 246 PLDR_DATA_TABLE_ENTRY Module; 247 PPEB_LDR_DATA Ldr; 248 PROCESS_BASIC_INFORMATION ClientBasicInfo; 249 LDR_DATA_TABLE_ENTRY ModuleData; 250 PVOID ClientDllBase; 251 NTSTATUS Status; 252 PPEB Peb; 253 254 /* Initialize string */ 255 RtlInitEmptyUnicodeString(ClientFileNameU, NULL, 0); 256 257 /* Query process information */ 258 Status = NtQueryInformationProcess(hProcess, 259 ProcessBasicInformation, 260 &ClientBasicInfo, 261 sizeof(ClientBasicInfo), 262 NULL); 263 if (!NT_SUCCESS(Status)) return Status; 264 265 /* Locate the process loader data table and retrieve its name from it */ 266 267 Peb = ClientBasicInfo.PebBaseAddress; 268 if (!Peb) return STATUS_UNSUCCESSFUL; 269 270 Status = NtReadVirtualMemory(hProcess, &Peb->Ldr, &Ldr, sizeof(Ldr), NULL); 271 if (!NT_SUCCESS(Status)) return Status; 272 273 ModuleListHead = &Ldr->InLoadOrderModuleList; 274 Status = NtReadVirtualMemory(hProcess, 275 &ModuleListHead->Flink, 276 &Entry, 277 sizeof(Entry), 278 NULL); 279 if (!NT_SUCCESS(Status)) return Status; 280 281 if (Entry == ModuleListHead) return STATUS_UNSUCCESSFUL; 282 283 Module = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); 284 285 Status = NtReadVirtualMemory(hProcess, 286 Module, 287 &ModuleData, 288 sizeof(ModuleData), 289 NULL); 290 if (!NT_SUCCESS(Status)) return Status; 291 292 Status = NtReadVirtualMemory(hProcess, 293 &Peb->ImageBaseAddress, 294 &ClientDllBase, 295 sizeof(ClientDllBase), 296 NULL); 297 if (!NT_SUCCESS(Status)) return Status; 298 299 if (ClientDllBase != ModuleData.DllBase) return STATUS_UNSUCCESSFUL; 300 301 ClientFileNameU->MaximumLength = ModuleData.BaseDllName.MaximumLength; 302 ClientFileNameU->Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 303 HEAP_ZERO_MEMORY, 304 ClientFileNameU->MaximumLength); 305 if (!ClientFileNameU->Buffer) 306 { 307 RtlInitEmptyUnicodeString(ClientFileNameU, NULL, 0); 308 return STATUS_NO_MEMORY; 309 } 310 311 Status = NtReadVirtualMemory(hProcess, 312 ModuleData.BaseDllName.Buffer, 313 ClientFileNameU->Buffer, 314 ClientFileNameU->MaximumLength, 315 NULL); 316 if (!NT_SUCCESS(Status)) 317 { 318 RtlFreeHeap(RtlGetProcessHeap(), 0, ClientFileNameU->Buffer); 319 RtlInitEmptyUnicodeString(ClientFileNameU, NULL, 0); 320 return Status; 321 } 322 323 ClientFileNameU->Length = (USHORT)(wcslen(ClientFileNameU->Buffer) * sizeof(WCHAR)); 324 DPRINT("ClientFileNameU = \'%wZ\'\n", &ClientFileNameU); 325 326 return STATUS_SUCCESS; 327 } 328 329 static 330 VOID 331 UserpDuplicateParamStringToUnicodeString( 332 IN OUT PUNICODE_STRING UnicodeString, 333 IN PCWSTR ParamString) 334 { 335 UNICODE_STRING FormatU, TempStringU; 336 337 /* Calculate buffer length for the text message */ 338 RtlInitUnicodeString(&FormatU, (PWSTR)ParamString); 339 if (UnicodeString->MaximumLength < FormatU.MaximumLength) 340 { 341 /* Duplicate the text message in a larger buffer */ 342 if (NT_SUCCESS(RtlDuplicateUnicodeString(RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE, 343 &FormatU, &TempStringU))) 344 { 345 *UnicodeString = TempStringU; 346 } 347 else 348 { 349 /* We could not allocate a larger buffer; continue using the smaller original buffer */ 350 DPRINT1("Cannot allocate memory for UnicodeString, use original buffer.\n"); 351 352 /* Copy the truncated string, NULL-terminate it */ 353 FormatU.MaximumLength = UnicodeString->MaximumLength; 354 FormatU.Length = FormatU.MaximumLength - sizeof(UNICODE_NULL); 355 RtlCopyUnicodeString(UnicodeString, &FormatU); 356 } 357 } 358 else 359 { 360 /* Copy the string, NULL-terminate it */ 361 RtlCopyUnicodeString(UnicodeString, &FormatU); 362 } 363 } 364 365 static 366 VOID 367 UserpFormatMessages( 368 IN OUT PUNICODE_STRING TextStringU, 369 IN OUT PUNICODE_STRING CaptionStringU, 370 OUT PUINT pdwType, 371 OUT PULONG pdwTimeout, 372 IN PHARDERROR_MSG Message) 373 { 374 /* Special hardcoded messages */ 375 static const PCWSTR pszUnknownHardError = 376 L"Unknown Hard Error 0x%08lx\n" 377 L"Parameters: 0x%p 0x%p 0x%p 0x%p"; 378 static const PCWSTR pszExceptionHardError = 379 L"Exception processing message 0x%08lx\n" 380 L"Parameters: 0x%p 0x%p 0x%p 0x%p"; 381 382 NTSTATUS Status; 383 OBJECT_ATTRIBUTES ObjectAttributes; 384 HANDLE hProcess; 385 ULONG Severity = (ULONG)(Message->Status) >> 30; 386 ULONG SizeOfStrings; 387 ULONG_PTR Parameters[MAXIMUM_HARDERROR_PARAMETERS] = {0}; 388 ULONG_PTR CopyParameters[MAXIMUM_HARDERROR_PARAMETERS]; 389 UNICODE_STRING WindowTitleU, FileNameU, TempStringU, FormatU, Format2U; 390 ANSI_STRING FormatA, Format2A; 391 HWND hwndOwner; 392 PMESSAGE_RESOURCE_ENTRY MessageResource; 393 PWSTR FormatString, pszBuffer; 394 size_t cszBuffer; 395 396 /* Open client process */ 397 InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL); 398 Status = NtOpenProcess(&hProcess, 399 PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, 400 &ObjectAttributes, 401 &Message->h.ClientId); 402 if (!NT_SUCCESS(Status)) 403 { 404 DPRINT1("NtOpenProcess failed with status 0x%08lx, possibly SYSTEM process.\n", Status); 405 hProcess = NULL; 406 } 407 408 /* Capture all string parameters from the process memory */ 409 UserpCaptureStringParameters(Parameters, &SizeOfStrings, Message, hProcess); 410 411 /* Initialize the output strings */ 412 TextStringU->Length = 0; 413 TextStringU->Buffer[0] = UNICODE_NULL; 414 415 CaptionStringU->Length = 0; 416 CaptionStringU->Buffer[0] = UNICODE_NULL; 417 418 /* 419 * Check whether it is a service notification, in which case 420 * we format the parameters and take the short route. 421 */ 422 if (Message->Status == STATUS_SERVICE_NOTIFICATION) 423 { 424 /* Close the process handle */ 425 if (hProcess) NtClose(hProcess); 426 427 /* 428 * Retrieve the message box flags. Note that we filter out 429 * MB_SERVICE_NOTIFICATION to not enter an infinite recursive 430 * loop when we will call MessageBox() later on. 431 */ 432 *pdwType = (UINT)Parameters[2] & ~MB_SERVICE_NOTIFICATION; 433 434 /* 435 * Duplicate the UNICODE text message and caption. 436 * If no strings or invalid ones have been provided, keep 437 * the original buffers and reset the string lengths to zero. 438 */ 439 if (Message->UnicodeStringParameterMask & 0x1) 440 UserpDuplicateParamStringToUnicodeString(TextStringU, (PCWSTR)Parameters[0]); 441 if (Message->UnicodeStringParameterMask & 0x2) 442 UserpDuplicateParamStringToUnicodeString(CaptionStringU, (PCWSTR)Parameters[1]); 443 444 /* Set the timeout */ 445 if (Message->NumberOfParameters >= 4) 446 *pdwTimeout = (ULONG)Parameters[3]; 447 else 448 *pdwTimeout = INFINITE; 449 450 goto Quit; 451 } 452 453 /* Set the message box type */ 454 *pdwType = 0; 455 switch (Message->ValidResponseOptions) 456 { 457 case OptionAbortRetryIgnore: 458 *pdwType = MB_ABORTRETRYIGNORE; 459 break; 460 case OptionOk: 461 *pdwType = MB_OK; 462 break; 463 case OptionOkCancel: 464 *pdwType = MB_OKCANCEL; 465 break; 466 case OptionRetryCancel: 467 *pdwType = MB_RETRYCANCEL; 468 break; 469 case OptionYesNo: 470 *pdwType = MB_YESNO; 471 break; 472 case OptionYesNoCancel: 473 *pdwType = MB_YESNOCANCEL; 474 break; 475 case OptionShutdownSystem: 476 *pdwType = MB_OK; 477 break; 478 case OptionOkNoWait: 479 *pdwType = MB_OK; 480 break; 481 case OptionCancelTryContinue: 482 *pdwType = MB_CANCELTRYCONTINUE; 483 break; 484 } 485 486 /* Set the severity icon */ 487 // STATUS_SEVERITY_SUCCESS 488 if (Severity == STATUS_SEVERITY_INFORMATIONAL) *pdwType |= MB_ICONINFORMATION; 489 else if (Severity == STATUS_SEVERITY_WARNING) *pdwType |= MB_ICONWARNING; 490 else if (Severity == STATUS_SEVERITY_ERROR) *pdwType |= MB_ICONERROR; 491 492 *pdwType |= MB_SYSTEMMODAL | MB_SETFOREGROUND; 493 494 /* Set the timeout */ 495 *pdwTimeout = INFINITE; 496 497 /* Copy the Parameters array locally */ 498 RtlCopyMemory(&CopyParameters, Parameters, sizeof(CopyParameters)); 499 500 /* Get the file name of the client process */ 501 Status = STATUS_SUCCESS; 502 if (hProcess) 503 Status = UserpGetClientFileName(&FileNameU, hProcess); 504 505 /* Close the process handle but keep its original value to know where stuff came from */ 506 if (hProcess) NtClose(hProcess); 507 508 /* 509 * Fall back to SYSTEM process if the client process handle 510 * was NULL or we failed retrieving a file name. 511 */ 512 if (!hProcess || !NT_SUCCESS(Status) || !FileNameU.Buffer) 513 { 514 hProcess = NULL; 515 FileNameU = g_SystemProcessU; 516 } 517 518 /* Retrieve the description of the error code */ 519 FormatA.Buffer = NULL; 520 Status = RtlFindMessage(GetModuleHandleW(L"ntdll"), 521 (ULONG_PTR)RT_MESSAGETABLE, 522 LANG_NEUTRAL, 523 Message->Status, 524 &MessageResource); 525 if (NT_SUCCESS(Status)) 526 { 527 if (MessageResource->Flags) 528 { 529 RtlInitUnicodeString(&FormatU, (PWSTR)MessageResource->Text); 530 FormatA.Buffer = NULL; 531 } 532 else 533 { 534 RtlInitAnsiString(&FormatA, (PSTR)MessageResource->Text); 535 /* Status = */ RtlAnsiStringToUnicodeString(&FormatU, &FormatA, TRUE); 536 } 537 ASSERT(FormatU.Buffer); 538 } 539 else 540 { 541 /* 542 * Fall back to unknown hard error format string. 543 * NOTE: The value used here is ReactOS-specific: it allows specifying 544 * the exact hard error status value and the parameters, contrary to 545 * the one on Windows that only says: "Unknown Hard Error". 546 */ 547 RtlInitEmptyUnicodeString(&FormatU, NULL, 0); 548 FormatA.Buffer = NULL; 549 } 550 551 FormatString = FormatU.Buffer; 552 553 /* Check whether a caption is specified in the format string */ 554 if (FormatString && FormatString[0] == L'{') 555 { 556 /* Set caption start */ 557 TempStringU.Buffer = ++FormatString; 558 559 /* Get the caption size and find where the format string really starts */ 560 for (TempStringU.Length = 0; 561 *FormatString != UNICODE_NULL && *FormatString != L'}'; 562 ++TempStringU.Length) 563 { 564 ++FormatString; 565 } 566 567 /* Skip '}', '\r', '\n' */ 568 FormatString += 3; 569 570 TempStringU.Length *= sizeof(WCHAR); 571 TempStringU.MaximumLength = TempStringU.Length; 572 } 573 else 574 { 575 if (Severity == STATUS_SEVERITY_SUCCESS) 576 TempStringU = g_SuccessU; 577 else if (Severity == STATUS_SEVERITY_INFORMATIONAL) 578 TempStringU = g_InformationU; 579 else if (Severity == STATUS_SEVERITY_WARNING) 580 TempStringU = g_WarningU; 581 else if (Severity == STATUS_SEVERITY_ERROR) 582 TempStringU = g_ErrorU; 583 else 584 ASSERT(FALSE); // Unexpected, since Severity is only <= 3. 585 } 586 587 /* Retrieve the window title of the client, if it has one */ 588 RtlInitEmptyUnicodeString(&WindowTitleU, L"", 0); 589 hwndOwner = NULL; 590 EnumThreadWindows(HandleToUlong(Message->h.ClientId.UniqueThread), 591 FindTopLevelWnd, (LPARAM)&hwndOwner); 592 if (hwndOwner) 593 { 594 cszBuffer = GetWindowTextLengthW(hwndOwner); 595 if (cszBuffer != 0) 596 { 597 cszBuffer += 3; // 2 characters for ": " and a NULL terminator. 598 cszBuffer *= sizeof(WCHAR); 599 pszBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 600 HEAP_ZERO_MEMORY, 601 cszBuffer); 602 if (pszBuffer) 603 { 604 RtlInitEmptyUnicodeString(&WindowTitleU, pszBuffer, (USHORT)cszBuffer); 605 cszBuffer = GetWindowTextW(hwndOwner, 606 WindowTitleU.Buffer, 607 WindowTitleU.MaximumLength / sizeof(WCHAR)); 608 WindowTitleU.Length = (USHORT)(cszBuffer * sizeof(WCHAR)); 609 RtlAppendUnicodeToString(&WindowTitleU, L": "); 610 } 611 } 612 } 613 614 /* Calculate buffer length for the caption */ 615 cszBuffer = WindowTitleU.Length + FileNameU.Length + TempStringU.Length + 616 3 * sizeof(WCHAR) + sizeof(UNICODE_NULL); 617 if (CaptionStringU->MaximumLength < cszBuffer) 618 { 619 /* Allocate a larger buffer for the caption */ 620 pszBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 621 HEAP_ZERO_MEMORY, 622 cszBuffer); 623 if (!pszBuffer) 624 { 625 /* We could not allocate a larger buffer; continue using the smaller original buffer */ 626 DPRINT1("Cannot allocate memory for CaptionStringU, use original buffer.\n"); 627 } 628 else 629 { 630 RtlInitEmptyUnicodeString(CaptionStringU, pszBuffer, (USHORT)cszBuffer); 631 } 632 } 633 CaptionStringU->Length = 0; 634 CaptionStringU->Buffer[0] = UNICODE_NULL; 635 636 /* Build the caption */ 637 RtlStringCbPrintfW(CaptionStringU->Buffer, 638 CaptionStringU->MaximumLength, 639 L"%wZ%wZ - %wZ", 640 &WindowTitleU, &FileNameU, &TempStringU); 641 CaptionStringU->Length = (USHORT)(wcslen(CaptionStringU->Buffer) * sizeof(WCHAR)); 642 643 /* Free the strings if needed */ 644 if (WindowTitleU.Buffer && (WindowTitleU.MaximumLength != 0)) 645 RtlFreeUnicodeString(&WindowTitleU); 646 if (hProcess) 647 RtlFreeUnicodeString(&FileNameU); 648 649 Format2A.Buffer = NULL; 650 651 /* If we have an unknown hard error, skip the special cases handling */ 652 if (!FormatString) 653 goto BuildMessage; 654 655 /* Check if this is an exception message */ 656 if (Message->Status == STATUS_UNHANDLED_EXCEPTION) 657 { 658 ULONG ExceptionCode = CopyParameters[0]; 659 660 /* Retrieve the description of the exception code */ 661 Status = RtlFindMessage(GetModuleHandleW(L"ntdll"), 662 (ULONG_PTR)RT_MESSAGETABLE, 663 LANG_NEUTRAL, 664 ExceptionCode, 665 &MessageResource); 666 if (NT_SUCCESS(Status)) 667 { 668 if (MessageResource->Flags) 669 { 670 RtlInitUnicodeString(&Format2U, (PWSTR)MessageResource->Text); 671 Format2A.Buffer = NULL; 672 } 673 else 674 { 675 RtlInitAnsiString(&Format2A, (PSTR)MessageResource->Text); 676 /* Status = */ RtlAnsiStringToUnicodeString(&Format2U, &Format2A, TRUE); 677 } 678 ASSERT(Format2U.Buffer); 679 680 /* Handle special cases */ 681 if (ExceptionCode == STATUS_ACCESS_VIOLATION) 682 { 683 /* Use a new FormatString */ 684 FormatString = Format2U.Buffer; 685 CopyParameters[0] = CopyParameters[1]; 686 CopyParameters[1] = CopyParameters[3]; 687 if (CopyParameters[2]) 688 CopyParameters[2] = (ULONG_PTR)L"written"; 689 else 690 CopyParameters[2] = (ULONG_PTR)L"read"; 691 } 692 else if (ExceptionCode == STATUS_IN_PAGE_ERROR) 693 { 694 /* Use a new FormatString */ 695 FormatString = Format2U.Buffer; 696 CopyParameters[0] = CopyParameters[1]; 697 CopyParameters[1] = CopyParameters[3]; 698 } 699 else 700 { 701 /* Keep the existing FormatString */ 702 CopyParameters[2] = CopyParameters[1]; 703 CopyParameters[1] = CopyParameters[0]; 704 705 pszBuffer = Format2U.Buffer; 706 if (!_wcsnicmp(pszBuffer, L"{EXCEPTION}", 11)) 707 { 708 /* 709 * This is a named exception. Skip the mark and 710 * retrieve the exception name that follows it. 711 */ 712 pszBuffer += 11; 713 714 /* Skip '\r', '\n' */ 715 pszBuffer += 2; 716 717 CopyParameters[0] = (ULONG_PTR)pszBuffer; 718 } 719 else 720 { 721 /* Fall back to hardcoded value */ 722 CopyParameters[0] = (ULONG_PTR)L"unknown software exception"; 723 } 724 } 725 } 726 else 727 { 728 /* Fall back to hardcoded value, and keep the existing FormatString */ 729 CopyParameters[2] = CopyParameters[1]; 730 CopyParameters[1] = CopyParameters[0]; 731 CopyParameters[0] = (ULONG_PTR)L"unknown software exception"; 732 } 733 } 734 735 BuildMessage: 736 /* 737 * Calculate buffer length for the text message. If FormatString 738 * is NULL this means we have an unknown hard error whose format 739 * string is in FormatU. 740 */ 741 cszBuffer = 0; 742 /* Wrap in SEH to protect from invalid string parameters */ 743 _SEH2_TRY 744 { 745 if (!FormatString) 746 { 747 /* Fall back to unknown hard error format string, and use the original parameters */ 748 cszBuffer = _scwprintf(pszUnknownHardError, 749 Message->Status, 750 Parameters[0], Parameters[1], 751 Parameters[2], Parameters[3]); 752 cszBuffer *= sizeof(WCHAR); 753 } 754 else 755 { 756 cszBuffer = _scwprintf(FormatString, 757 CopyParameters[0], CopyParameters[1], 758 CopyParameters[2], CopyParameters[3]); 759 cszBuffer *= sizeof(WCHAR); 760 761 /* Add a description for the dialog buttons */ 762 if (Message->Status == STATUS_UNHANDLED_EXCEPTION) 763 { 764 if (Message->ValidResponseOptions == OptionOk || 765 Message->ValidResponseOptions == OptionOkCancel) 766 { 767 /* Reserve space for one newline and the OK-terminate-program string */ 768 cszBuffer += sizeof(WCHAR) + g_OKTerminateU.Length; 769 } 770 if (Message->ValidResponseOptions == OptionOkCancel) 771 { 772 /* Reserve space for one newline and the CANCEL-debug-program string */ 773 cszBuffer += sizeof(WCHAR) + g_CancelDebugU.Length; 774 } 775 } 776 } 777 } 778 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 779 { 780 /* An exception occurred, use a default string with the original parameters */ 781 cszBuffer = _scwprintf(pszExceptionHardError, 782 Message->Status, 783 Parameters[0], Parameters[1], 784 Parameters[2], Parameters[3]); 785 cszBuffer *= sizeof(WCHAR); 786 } 787 _SEH2_END; 788 789 cszBuffer += SizeOfStrings + sizeof(UNICODE_NULL); 790 791 if (TextStringU->MaximumLength < cszBuffer) 792 { 793 /* Allocate a larger buffer for the text message */ 794 pszBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 795 HEAP_ZERO_MEMORY, 796 cszBuffer); 797 if (!pszBuffer) 798 { 799 /* We could not allocate a larger buffer; continue using the smaller original buffer */ 800 DPRINT1("Cannot allocate memory for TextStringU, use original buffer.\n"); 801 } 802 else 803 { 804 RtlInitEmptyUnicodeString(TextStringU, pszBuffer, (USHORT)cszBuffer); 805 } 806 } 807 TextStringU->Length = 0; 808 TextStringU->Buffer[0] = UNICODE_NULL; 809 810 /* Wrap in SEH to protect from invalid string parameters */ 811 _SEH2_TRY 812 { 813 /* Print the string into the buffer */ 814 pszBuffer = TextStringU->Buffer; 815 cszBuffer = TextStringU->MaximumLength; 816 817 if (!FormatString) 818 { 819 /* Fall back to unknown hard error format string, and use the original parameters */ 820 RtlStringCbPrintfW(pszBuffer, cszBuffer, 821 pszUnknownHardError, 822 Message->Status, 823 Parameters[0], Parameters[1], 824 Parameters[2], Parameters[3]); 825 } 826 else 827 { 828 RtlStringCbPrintfExW(pszBuffer, cszBuffer, 829 &pszBuffer, &cszBuffer, 830 0, 831 FormatString, 832 CopyParameters[0], CopyParameters[1], 833 CopyParameters[2], CopyParameters[3]); 834 835 /* Add a description for the dialog buttons */ 836 if (Message->Status == STATUS_UNHANDLED_EXCEPTION) 837 { 838 if (Message->ValidResponseOptions == OptionOk || 839 Message->ValidResponseOptions == OptionOkCancel) 840 { 841 RtlStringCbPrintfExW(pszBuffer, cszBuffer, 842 &pszBuffer, &cszBuffer, 843 0, 844 L"\n%wZ", 845 &g_OKTerminateU); 846 } 847 if (Message->ValidResponseOptions == OptionOkCancel) 848 { 849 RtlStringCbPrintfExW(pszBuffer, cszBuffer, 850 &pszBuffer, &cszBuffer, 851 0, 852 L"\n%wZ", 853 &g_CancelDebugU); 854 } 855 } 856 } 857 } 858 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 859 { 860 /* An exception occurred, use a default string with the original parameters */ 861 DPRINT1("Exception 0x%08lx occurred while building hard-error message, fall back to default message.\n", 862 _SEH2_GetExceptionCode()); 863 864 RtlStringCbPrintfW(TextStringU->Buffer, 865 TextStringU->MaximumLength, 866 pszExceptionHardError, 867 Message->Status, 868 Parameters[0], Parameters[1], 869 Parameters[2], Parameters[3]); 870 } 871 _SEH2_END; 872 873 TextStringU->Length = (USHORT)(wcslen(TextStringU->Buffer) * sizeof(WCHAR)); 874 875 /* Free the converted UNICODE strings */ 876 if (Format2A.Buffer) RtlFreeUnicodeString(&Format2U); 877 if (FormatA.Buffer) RtlFreeUnicodeString(&FormatU); 878 879 Quit: 880 /* Free the captured parameters */ 881 UserpFreeStringParameters(Parameters, Message); 882 } 883 884 static ULONG 885 GetRegInt( 886 IN PCWSTR KeyName, 887 IN PCWSTR ValueName, 888 IN ULONG DefaultValue) 889 { 890 NTSTATUS Status; 891 ULONG Value = DefaultValue; 892 UNICODE_STRING String; 893 OBJECT_ATTRIBUTES ObjectAttributes; 894 HANDLE KeyHandle; 895 ULONG ResultLength; 896 UCHAR ValueBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)]; 897 PKEY_VALUE_PARTIAL_INFORMATION ValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer; 898 899 RtlInitUnicodeString(&String, KeyName); 900 InitializeObjectAttributes(&ObjectAttributes, 901 &String, 902 OBJ_CASE_INSENSITIVE, 903 NULL, 904 NULL); 905 906 /* Open the registry key */ 907 Status = NtOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes); 908 if (NT_SUCCESS(Status)) 909 { 910 /* Query the value */ 911 RtlInitUnicodeString(&String, ValueName); 912 Status = NtQueryValueKey(KeyHandle, 913 &String, 914 KeyValuePartialInformation, 915 ValueInfo, 916 sizeof(ValueBuffer), 917 &ResultLength); 918 919 /* Close the registry key */ 920 NtClose(KeyHandle); 921 922 if (NT_SUCCESS(Status) && (ValueInfo->Type == REG_DWORD)) 923 { 924 /* Directly retrieve the data */ 925 Value = *(PULONG)ValueInfo->Data; 926 } 927 } 928 929 return Value; 930 } 931 932 static BOOL 933 UserpShowInformationBalloon( 934 IN PUNICODE_STRING TextStringU, 935 IN PUNICODE_STRING CaptionStringU, 936 IN UINT Type, 937 IN PHARDERROR_MSG Message) 938 { 939 ULONG ShellErrorMode; 940 HWND hWndTaskman; 941 COPYDATASTRUCT CopyData; 942 PBALLOON_HARD_ERROR_DATA pdata; 943 DWORD dwSize, cbTextLen, cbTitleLen; 944 PWCHAR pText, pCaption; 945 DWORD ret; 946 DWORD_PTR dwResult; 947 948 /* Query the shell error mode value */ 949 ShellErrorMode = GetRegInt(L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Windows", 950 L"ShellErrorMode", 0); 951 952 /* Make the shell display the hard error message only if necessary */ 953 if (ShellErrorMode != 1) 954 return FALSE; 955 956 /* Retrieve the shell task window */ 957 hWndTaskman = GetTaskmanWindow(); 958 if (!hWndTaskman) 959 { 960 DPRINT1("Failed to find shell task window (last error %lu)\n", GetLastError()); 961 return FALSE; 962 } 963 964 cbTextLen = TextStringU->Length + sizeof(UNICODE_NULL); 965 cbTitleLen = CaptionStringU->Length + sizeof(UNICODE_NULL); 966 967 dwSize = sizeof(BALLOON_HARD_ERROR_DATA); 968 dwSize += cbTextLen + cbTitleLen; 969 970 /* Build the data buffer */ 971 pdata = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, dwSize); 972 if (!pdata) 973 { 974 DPRINT1("Failed to allocate balloon data\n"); 975 return FALSE; 976 } 977 978 pdata->cbHeaderSize = sizeof(BALLOON_HARD_ERROR_DATA); 979 pdata->Status = Message->Status; 980 pdata->dwType = Type; 981 982 pdata->TitleOffset = pdata->cbHeaderSize; 983 pdata->MessageOffset = pdata->TitleOffset + cbTitleLen; 984 pCaption = (PWCHAR)((ULONG_PTR)pdata + pdata->TitleOffset); 985 pText = (PWCHAR)((ULONG_PTR)pdata + pdata->MessageOffset); 986 RtlStringCbCopyNW(pCaption, cbTitleLen, CaptionStringU->Buffer, CaptionStringU->Length); 987 RtlStringCbCopyNW(pText, cbTextLen, TextStringU->Buffer, TextStringU->Length); 988 989 /* Send the message */ 990 991 /* Retrieve a unique system-wide message to communicate hard error data with the shell */ 992 CopyData.dwData = RegisterWindowMessageW(L"HardError"); 993 CopyData.cbData = dwSize; 994 CopyData.lpData = pdata; 995 996 dwResult = FALSE; 997 ret = SendMessageTimeoutW(hWndTaskman, WM_COPYDATA, 0, (LPARAM)&CopyData, 998 SMTO_NORMAL | SMTO_ABORTIFHUNG, 3000, &dwResult); 999 1000 /* Free the buffer */ 1001 RtlFreeHeap(RtlGetProcessHeap(), 0, pdata); 1002 1003 return (ret && dwResult) ? TRUE : FALSE; 1004 } 1005 1006 static 1007 HARDERROR_RESPONSE 1008 UserpMessageBox( 1009 IN PUNICODE_STRING TextStringU, 1010 IN PUNICODE_STRING CaptionStringU, 1011 IN UINT Type, 1012 IN ULONG Timeout) 1013 { 1014 ULONG MessageBoxResponse; 1015 HDESK hDesk, hOldDesk; 1016 1017 DPRINT("Text = '%S', Caption = '%S', Type = 0x%lx\n", 1018 TextStringU->Buffer, CaptionStringU->Buffer, Type); 1019 1020 // TEMPORARY HACK to fix desktop assignment for harderror message boxes. 1021 hDesk = OpenInputDesktop(0, FALSE, GENERIC_WRITE); 1022 if (!hDesk) 1023 return ResponseNotHandled; 1024 1025 /* Assign the desktop to this thread */ 1026 hOldDesk = GetThreadDesktop(GetCurrentThreadId()); 1027 if (!SetThreadDesktop(hDesk)) 1028 { 1029 CloseDesktop(hDesk); 1030 return ResponseNotHandled; 1031 } 1032 1033 /* Display a message box */ 1034 MessageBoxResponse = MessageBoxTimeoutW(NULL, 1035 TextStringU->Buffer, 1036 CaptionStringU->Buffer, 1037 Type, 0, Timeout); 1038 1039 /* Restore the original desktop */ 1040 SetThreadDesktop(hOldDesk); 1041 CloseDesktop(hDesk); 1042 1043 /* Return response value */ 1044 switch (MessageBoxResponse) 1045 { 1046 case IDOK: return ResponseOk; 1047 case IDCANCEL: return ResponseCancel; 1048 case IDYES: return ResponseYes; 1049 case IDNO: return ResponseNo; 1050 case IDABORT: return ResponseAbort; 1051 case IDIGNORE: return ResponseIgnore; 1052 case IDRETRY: return ResponseRetry; 1053 case IDTRYAGAIN: return ResponseTryAgain; 1054 case IDCONTINUE: return ResponseContinue; 1055 default: return ResponseNotHandled; 1056 } 1057 1058 return ResponseNotHandled; 1059 } 1060 1061 static 1062 VOID 1063 UserpLogHardError( 1064 IN PUNICODE_STRING TextStringU, 1065 IN PUNICODE_STRING CaptionStringU) 1066 { 1067 NTSTATUS Status; 1068 HANDLE hEventLog; 1069 UNICODE_STRING UNCServerNameU = {0, 0, NULL}; 1070 UNICODE_STRING SourceNameU = RTL_CONSTANT_STRING(L"Application Popup"); 1071 PUNICODE_STRING Strings[] = {CaptionStringU, TextStringU}; 1072 1073 Status = ElfRegisterEventSourceW(&UNCServerNameU, &SourceNameU, &hEventLog); 1074 if (!NT_SUCCESS(Status) || !hEventLog) 1075 { 1076 DPRINT1("ElfRegisterEventSourceW failed with Status 0x%08lx\n", Status); 1077 return; 1078 } 1079 1080 Status = ElfReportEventW(hEventLog, 1081 EVENTLOG_INFORMATION_TYPE, 1082 0, 1083 STATUS_LOG_HARD_ERROR, 1084 NULL, // lpUserSid 1085 ARRAYSIZE(Strings), 1086 0, // dwDataSize 1087 Strings, 1088 NULL, // lpRawData 1089 0, 1090 NULL, 1091 NULL); 1092 if (!NT_SUCCESS(Status)) 1093 DPRINT1("ElfReportEventW failed with Status 0x%08lx\n", Status); 1094 1095 ElfDeregisterEventSource(hEventLog); 1096 } 1097 1098 VOID 1099 NTAPI 1100 UserServerHardError( 1101 IN PCSR_THREAD ThreadData, 1102 IN PHARDERROR_MSG Message) 1103 { 1104 ULONG ErrorMode; 1105 UINT dwType = 0; 1106 ULONG Timeout = INFINITE; 1107 UNICODE_STRING TextU, CaptionU; 1108 WCHAR LocalTextBuffer[256]; 1109 WCHAR LocalCaptionBuffer[256]; 1110 1111 ASSERT(ThreadData->Process != NULL); 1112 1113 /* Default to not handled */ 1114 Message->Response = ResponseNotHandled; 1115 1116 /* Make sure we don't have too many parameters */ 1117 if (Message->NumberOfParameters > MAXIMUM_HARDERROR_PARAMETERS) 1118 { 1119 // NOTE: Windows just fails (STATUS_INVALID_PARAMETER) & returns ResponseNotHandled. 1120 DPRINT1("Invalid NumberOfParameters = %d\n", Message->NumberOfParameters); 1121 Message->NumberOfParameters = MAXIMUM_HARDERROR_PARAMETERS; 1122 } 1123 if (Message->ValidResponseOptions > OptionCancelTryContinue) 1124 { 1125 DPRINT1("Unknown ValidResponseOptions = %d\n", Message->ValidResponseOptions); 1126 return; // STATUS_INVALID_PARAMETER; 1127 } 1128 if (Message->Status == STATUS_SERVICE_NOTIFICATION) 1129 { 1130 if (Message->NumberOfParameters < 3) 1131 { 1132 DPRINT1("Invalid NumberOfParameters = %d for STATUS_SERVICE_NOTIFICATION\n", 1133 Message->NumberOfParameters); 1134 return; // STATUS_INVALID_PARAMETER; 1135 } 1136 // (Message->UnicodeStringParameterMask & 0x3) 1137 } 1138 1139 /* Re-initialize the hard errors cache */ 1140 UserInitHardErrorsCache(); 1141 1142 /* Format the message caption and text */ 1143 RtlInitEmptyUnicodeString(&TextU, LocalTextBuffer, sizeof(LocalTextBuffer)); 1144 RtlInitEmptyUnicodeString(&CaptionU, LocalCaptionBuffer, sizeof(LocalCaptionBuffer)); 1145 UserpFormatMessages(&TextU, &CaptionU, &dwType, &Timeout, Message); 1146 1147 /* Log the hard error message */ 1148 UserpLogHardError(&TextU, &CaptionU); 1149 1150 /* Display a hard error popup depending on the current ErrorMode */ 1151 1152 /* Query the error mode value */ 1153 ErrorMode = GetRegInt(L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Windows", 1154 L"ErrorMode", 0); 1155 1156 if (Message->Status != STATUS_SERVICE_NOTIFICATION && ErrorMode != 0) 1157 { 1158 /* Returns OK for the hard error */ 1159 Message->Response = ResponseOk; 1160 goto Quit; 1161 } 1162 1163 if (Message->ValidResponseOptions == OptionOkNoWait) 1164 { 1165 /* Display the balloon */ 1166 Message->Response = ResponseOk; 1167 if (UserpShowInformationBalloon(&TextU, 1168 &CaptionU, 1169 dwType, 1170 Message)) 1171 { 1172 Message->Response = ResponseOk; 1173 goto Quit; 1174 } 1175 } 1176 1177 /* Display the message box */ 1178 Message->Response = UserpMessageBox(&TextU, 1179 &CaptionU, 1180 dwType, 1181 Timeout); 1182 1183 Quit: 1184 /* Free the strings if they have been reallocated */ 1185 if (TextU.Buffer != LocalTextBuffer) 1186 RtlFreeUnicodeString(&TextU); 1187 if (CaptionU.Buffer != LocalCaptionBuffer) 1188 RtlFreeUnicodeString(&CaptionU); 1189 1190 return; 1191 } 1192 1193 VOID 1194 UserInitHardErrorsCache(VOID) 1195 { 1196 NTSTATUS Status; 1197 LCID CurrentUserLCID = 0; 1198 1199 Status = NtQueryDefaultLocale(TRUE, &CurrentUserLCID); 1200 if (!NT_SUCCESS(Status) || CurrentUserLCID == 0) 1201 { 1202 /* Fall back to english locale */ 1203 DPRINT1("NtQueryDefaultLocale failed with Status = 0x%08lx\n", Status); 1204 // LOCALE_SYSTEM_DEFAULT; 1205 CurrentUserLCID = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); 1206 } 1207 if (g_CurrentUserLangId == LANGIDFROMLCID(CurrentUserLCID)) 1208 { 1209 /* The current lang ID and the hard error strings have already been cached */ 1210 return; 1211 } 1212 1213 /* Load the strings using the current system locale */ 1214 RtlLoadUnicodeString(UserServerDllInstance, IDS_SEVERITY_SUCCESS, 1215 &g_SuccessU, L"Success"); 1216 RtlLoadUnicodeString(UserServerDllInstance, IDS_SEVERITY_INFORMATIONAL, 1217 &g_InformationU, L"System Information"); 1218 RtlLoadUnicodeString(UserServerDllInstance, IDS_SEVERITY_WARNING, 1219 &g_WarningU, L"System Warning"); 1220 RtlLoadUnicodeString(UserServerDllInstance, IDS_SEVERITY_ERROR, 1221 &g_ErrorU, L"System Error"); 1222 // "unknown software exception" 1223 RtlLoadUnicodeString(UserServerDllInstance, IDS_SYSTEM_PROCESS, 1224 &g_SystemProcessU, L"System Process"); 1225 RtlLoadUnicodeString(UserServerDllInstance, IDS_OK_TERMINATE_PROGRAM, 1226 &g_OKTerminateU, L"Click on OK to terminate the program."); 1227 RtlLoadUnicodeString(UserServerDllInstance, IDS_CANCEL_DEBUG_PROGRAM, 1228 &g_CancelDebugU, L"Click on CANCEL to debug the program."); 1229 1230 /* Remember that we cached the hard error strings */ 1231 g_CurrentUserLangId = LANGIDFROMLCID(CurrentUserLCID); 1232 } 1233 1234 /* EOF */ 1235