7cc3a67ca4070b29314415b888d4fdd24ced2b4f
[reactos.git] / win32ss / user / winsrv / usersrv / harderror.c
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) RtlFreeUnicodeString(&WindowTitleU);
645 if (hProcess) RtlFreeUnicodeString(&FileNameU);
646
647 Format2A.Buffer = NULL;
648
649 /* If we have an unknown hard error, skip the special cases handling */
650 if (!FormatString)
651 goto BuildMessage;
652
653 /* Check if this is an exception message */
654 if (Message->Status == STATUS_UNHANDLED_EXCEPTION)
655 {
656 ULONG ExceptionCode = CopyParameters[0];
657
658 /* Retrieve the description of the exception code */
659 Status = RtlFindMessage(GetModuleHandleW(L"ntdll"),
660 (ULONG_PTR)RT_MESSAGETABLE,
661 LANG_NEUTRAL,
662 ExceptionCode,
663 &MessageResource);
664 if (NT_SUCCESS(Status))
665 {
666 if (MessageResource->Flags)
667 {
668 RtlInitUnicodeString(&Format2U, (PWSTR)MessageResource->Text);
669 Format2A.Buffer = NULL;
670 }
671 else
672 {
673 RtlInitAnsiString(&Format2A, (PSTR)MessageResource->Text);
674 /* Status = */ RtlAnsiStringToUnicodeString(&Format2U, &Format2A, TRUE);
675 }
676 ASSERT(Format2U.Buffer);
677
678 /* Handle special cases */
679 if (ExceptionCode == STATUS_ACCESS_VIOLATION)
680 {
681 /* Use a new FormatString */
682 FormatString = Format2U.Buffer;
683 CopyParameters[0] = CopyParameters[1];
684 CopyParameters[1] = CopyParameters[3];
685 if (CopyParameters[2])
686 CopyParameters[2] = (ULONG_PTR)L"written";
687 else
688 CopyParameters[2] = (ULONG_PTR)L"read";
689 }
690 else if (ExceptionCode == STATUS_IN_PAGE_ERROR)
691 {
692 /* Use a new FormatString */
693 FormatString = Format2U.Buffer;
694 CopyParameters[0] = CopyParameters[1];
695 CopyParameters[1] = CopyParameters[3];
696 }
697 else
698 {
699 /* Keep the existing FormatString */
700 CopyParameters[2] = CopyParameters[1];
701 CopyParameters[1] = CopyParameters[0];
702
703 pszBuffer = Format2U.Buffer;
704 if (!_wcsnicmp(pszBuffer, L"{EXCEPTION}", 11))
705 {
706 /*
707 * This is a named exception. Skip the mark and
708 * retrieve the exception name that follows it.
709 */
710 pszBuffer += 11;
711
712 /* Skip '\r', '\n' */
713 pszBuffer += 2;
714
715 CopyParameters[0] = (ULONG_PTR)pszBuffer;
716 }
717 else
718 {
719 /* Fall back to hardcoded value */
720 CopyParameters[0] = (ULONG_PTR)L"unknown software exception";
721 }
722 }
723 }
724 else
725 {
726 /* Fall back to hardcoded value, and keep the existing FormatString */
727 CopyParameters[2] = CopyParameters[1];
728 CopyParameters[1] = CopyParameters[0];
729 CopyParameters[0] = (ULONG_PTR)L"unknown software exception";
730 }
731 }
732
733 BuildMessage:
734 /*
735 * Calculate buffer length for the text message. If FormatString
736 * is NULL this means we have an unknown hard error whose format
737 * string is in FormatU.
738 */
739 cszBuffer = 0;
740 /* Wrap in SEH to protect from invalid string parameters */
741 _SEH2_TRY
742 {
743 if (!FormatString)
744 {
745 /* Fall back to unknown hard error format string, and use the original parameters */
746 cszBuffer = _scwprintf(pszUnknownHardError,
747 Message->Status,
748 Parameters[0], Parameters[1],
749 Parameters[2], Parameters[3]);
750 cszBuffer *= sizeof(WCHAR);
751 }
752 else
753 {
754 cszBuffer = _scwprintf(FormatString,
755 CopyParameters[0], CopyParameters[1],
756 CopyParameters[2], CopyParameters[3]);
757 cszBuffer *= sizeof(WCHAR);
758
759 /* Add a description for the dialog buttons */
760 if (Message->Status == STATUS_UNHANDLED_EXCEPTION)
761 {
762 if (Message->ValidResponseOptions == OptionOk ||
763 Message->ValidResponseOptions == OptionOkCancel)
764 {
765 /* Reserve space for one newline and the OK-terminate-program string */
766 cszBuffer += sizeof(WCHAR) + g_OKTerminateU.Length;
767 }
768 if (Message->ValidResponseOptions == OptionOkCancel)
769 {
770 /* Reserve space for one newline and the CANCEL-debug-program string */
771 cszBuffer += sizeof(WCHAR) + g_CancelDebugU.Length;
772 }
773 }
774 }
775 }
776 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
777 {
778 /* An exception occurred, use a default string with the original parameters */
779 cszBuffer = _scwprintf(pszExceptionHardError,
780 Message->Status,
781 Parameters[0], Parameters[1],
782 Parameters[2], Parameters[3]);
783 cszBuffer *= sizeof(WCHAR);
784 }
785 _SEH2_END;
786
787 cszBuffer += SizeOfStrings + sizeof(UNICODE_NULL);
788
789 if (TextStringU->MaximumLength < cszBuffer)
790 {
791 /* Allocate a larger buffer for the text message */
792 pszBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
793 HEAP_ZERO_MEMORY,
794 cszBuffer);
795 if (!pszBuffer)
796 {
797 /* We could not allocate a larger buffer; continue using the smaller original buffer */
798 DPRINT1("Cannot allocate memory for TextStringU, use original buffer.\n");
799 }
800 else
801 {
802 RtlInitEmptyUnicodeString(TextStringU, pszBuffer, (USHORT)cszBuffer);
803 }
804 }
805 TextStringU->Length = 0;
806 TextStringU->Buffer[0] = UNICODE_NULL;
807
808 /* Wrap in SEH to protect from invalid string parameters */
809 _SEH2_TRY
810 {
811 /* Print the string into the buffer */
812 pszBuffer = TextStringU->Buffer;
813 cszBuffer = TextStringU->MaximumLength;
814
815 if (!FormatString)
816 {
817 /* Fall back to unknown hard error format string, and use the original parameters */
818 RtlStringCbPrintfW(pszBuffer, cszBuffer,
819 pszUnknownHardError,
820 Message->Status,
821 Parameters[0], Parameters[1],
822 Parameters[2], Parameters[3]);
823 }
824 else
825 {
826 RtlStringCbPrintfExW(pszBuffer, cszBuffer,
827 &pszBuffer, &cszBuffer,
828 0,
829 FormatString,
830 CopyParameters[0], CopyParameters[1],
831 CopyParameters[2], CopyParameters[3]);
832
833 /* Add a description for the dialog buttons */
834 if (Message->Status == STATUS_UNHANDLED_EXCEPTION)
835 {
836 if (Message->ValidResponseOptions == OptionOk ||
837 Message->ValidResponseOptions == OptionOkCancel)
838 {
839 RtlStringCbPrintfExW(pszBuffer, cszBuffer,
840 &pszBuffer, &cszBuffer,
841 0,
842 L"\n%wZ",
843 &g_OKTerminateU);
844 }
845 if (Message->ValidResponseOptions == OptionOkCancel)
846 {
847 RtlStringCbPrintfExW(pszBuffer, cszBuffer,
848 &pszBuffer, &cszBuffer,
849 0,
850 L"\n%wZ",
851 &g_CancelDebugU);
852 }
853 }
854 }
855 }
856 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
857 {
858 /* An exception occurred, use a default string with the original parameters */
859 DPRINT1("Exception 0x%08lx occurred while building hard-error message, fall back to default message.\n",
860 _SEH2_GetExceptionCode());
861
862 RtlStringCbPrintfW(TextStringU->Buffer,
863 TextStringU->MaximumLength,
864 pszExceptionHardError,
865 Message->Status,
866 Parameters[0], Parameters[1],
867 Parameters[2], Parameters[3]);
868 }
869 _SEH2_END;
870
871 TextStringU->Length = (USHORT)(wcslen(TextStringU->Buffer) * sizeof(WCHAR));
872
873 /* Free the converted UNICODE strings */
874 if (Format2A.Buffer) RtlFreeUnicodeString(&Format2U);
875 if (FormatA.Buffer) RtlFreeUnicodeString(&FormatU);
876
877 Quit:
878 /* Free the captured parameters */
879 UserpFreeStringParameters(Parameters, Message);
880 }
881
882 static ULONG
883 GetRegInt(
884 IN PCWSTR KeyName,
885 IN PCWSTR ValueName,
886 IN ULONG DefaultValue)
887 {
888 NTSTATUS Status;
889 ULONG Value = DefaultValue;
890 UNICODE_STRING String;
891 OBJECT_ATTRIBUTES ObjectAttributes;
892 HANDLE KeyHandle;
893 ULONG ResultLength;
894 UCHAR ValueBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)];
895 PKEY_VALUE_PARTIAL_INFORMATION ValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer;
896
897 RtlInitUnicodeString(&String, KeyName);
898 InitializeObjectAttributes(&ObjectAttributes,
899 &String,
900 OBJ_CASE_INSENSITIVE,
901 NULL,
902 NULL);
903
904 /* Open the registry key */
905 Status = NtOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes);
906 if (NT_SUCCESS(Status))
907 {
908 /* Query the value */
909 RtlInitUnicodeString(&String, ValueName);
910 Status = NtQueryValueKey(KeyHandle,
911 &String,
912 KeyValuePartialInformation,
913 ValueInfo,
914 sizeof(ValueBuffer),
915 &ResultLength);
916
917 /* Close the registry key */
918 NtClose(KeyHandle);
919
920 if (NT_SUCCESS(Status) && (ValueInfo->Type == REG_DWORD))
921 {
922 /* Directly retrieve the data */
923 Value = *(PULONG)ValueInfo->Data;
924 }
925 }
926
927 return Value;
928 }
929
930 static BOOL
931 UserpShowInformationBalloon(
932 IN PUNICODE_STRING TextStringU,
933 IN PUNICODE_STRING CaptionStringU,
934 IN UINT Type,
935 IN PHARDERROR_MSG Message)
936 {
937 ULONG ShellErrorMode;
938 HWND hWndTaskman;
939 COPYDATASTRUCT CopyData;
940 PBALLOON_HARD_ERROR_DATA pdata;
941 DWORD dwSize, cbTextLen, cbTitleLen;
942 PWCHAR pText, pCaption;
943 DWORD ret;
944 DWORD_PTR dwResult;
945
946 /* Query the shell error mode value */
947 ShellErrorMode = GetRegInt(L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Windows",
948 L"ShellErrorMode", 0);
949
950 /* Make the shell display the hard error message only if necessary */
951 if (ShellErrorMode != 1)
952 return FALSE;
953
954 /* Retrieve the shell task window */
955 hWndTaskman = GetTaskmanWindow();
956 if (!hWndTaskman)
957 {
958 DPRINT1("Failed to find shell task window (last error %lu)\n", GetLastError());
959 return FALSE;
960 }
961
962 cbTextLen = TextStringU->Length + sizeof(UNICODE_NULL);
963 cbTitleLen = CaptionStringU->Length + sizeof(UNICODE_NULL);
964
965 dwSize = sizeof(BALLOON_HARD_ERROR_DATA);
966 dwSize += cbTextLen + cbTitleLen;
967
968 /* Build the data buffer */
969 pdata = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, dwSize);
970 if (!pdata)
971 {
972 DPRINT1("Failed to allocate balloon data\n");
973 return FALSE;
974 }
975
976 pdata->cbHeaderSize = sizeof(BALLOON_HARD_ERROR_DATA);
977 pdata->Status = Message->Status;
978 pdata->dwType = Type;
979
980 pdata->TitleOffset = pdata->cbHeaderSize;
981 pdata->MessageOffset = pdata->TitleOffset + cbTitleLen;
982 pCaption = (PWCHAR)((ULONG_PTR)pdata + pdata->TitleOffset);
983 pText = (PWCHAR)((ULONG_PTR)pdata + pdata->MessageOffset);
984 RtlStringCbCopyNW(pCaption, cbTitleLen, CaptionStringU->Buffer, CaptionStringU->Length);
985 RtlStringCbCopyNW(pText, cbTextLen, TextStringU->Buffer, TextStringU->Length);
986
987 /* Send the message */
988
989 /* Retrieve a unique system-wide message to communicate hard error data with the shell */
990 CopyData.dwData = RegisterWindowMessageW(L"HardError");
991 CopyData.cbData = dwSize;
992 CopyData.lpData = pdata;
993
994 dwResult = FALSE;
995 ret = SendMessageTimeoutW(hWndTaskman, WM_COPYDATA, 0, (LPARAM)&CopyData,
996 SMTO_NORMAL | SMTO_ABORTIFHUNG, 3000, &dwResult);
997
998 /* Free the buffer */
999 RtlFreeHeap(RtlGetProcessHeap(), 0, pdata);
1000
1001 return (ret && dwResult) ? TRUE : FALSE;
1002 }
1003
1004 static
1005 HARDERROR_RESPONSE
1006 UserpMessageBox(
1007 IN PUNICODE_STRING TextStringU,
1008 IN PUNICODE_STRING CaptionStringU,
1009 IN UINT Type,
1010 IN ULONG Timeout)
1011 {
1012 ULONG MessageBoxResponse;
1013
1014 DPRINT("Text = '%S', Caption = '%S', Type = 0x%lx\n",
1015 TextStringU->Buffer, CaptionStringU->Buffer, Type);
1016
1017 /* Display a message box */
1018 MessageBoxResponse = MessageBoxTimeoutW(NULL,
1019 TextStringU->Buffer,
1020 CaptionStringU->Buffer,
1021 Type, 0, Timeout);
1022
1023 /* Return response value */
1024 switch (MessageBoxResponse)
1025 {
1026 case IDOK: return ResponseOk;
1027 case IDCANCEL: return ResponseCancel;
1028 case IDYES: return ResponseYes;
1029 case IDNO: return ResponseNo;
1030 case IDABORT: return ResponseAbort;
1031 case IDIGNORE: return ResponseIgnore;
1032 case IDRETRY: return ResponseRetry;
1033 case IDTRYAGAIN: return ResponseTryAgain;
1034 case IDCONTINUE: return ResponseContinue;
1035 default: return ResponseNotHandled;
1036 }
1037
1038 return ResponseNotHandled;
1039 }
1040
1041 static
1042 VOID
1043 UserpLogHardError(
1044 IN PUNICODE_STRING TextStringU,
1045 IN PUNICODE_STRING CaptionStringU)
1046 {
1047 NTSTATUS Status;
1048 HANDLE hEventLog;
1049 UNICODE_STRING UNCServerNameU = {0, 0, NULL};
1050 UNICODE_STRING SourceNameU = RTL_CONSTANT_STRING(L"Application Popup");
1051 PUNICODE_STRING Strings[] = {CaptionStringU, TextStringU};
1052
1053 Status = ElfRegisterEventSourceW(&UNCServerNameU, &SourceNameU, &hEventLog);
1054 if (!NT_SUCCESS(Status) || !hEventLog)
1055 {
1056 DPRINT1("ElfRegisterEventSourceW failed with Status 0x%08lx\n", Status);
1057 return;
1058 }
1059
1060 Status = ElfReportEventW(hEventLog,
1061 EVENTLOG_INFORMATION_TYPE,
1062 0,
1063 STATUS_LOG_HARD_ERROR,
1064 NULL, // lpUserSid
1065 ARRAYSIZE(Strings),
1066 0, // dwDataSize
1067 Strings,
1068 NULL, // lpRawData
1069 0,
1070 NULL,
1071 NULL);
1072 if (!NT_SUCCESS(Status))
1073 DPRINT1("ElfReportEventW failed with Status 0x%08lx\n", Status);
1074
1075 ElfDeregisterEventSource(hEventLog);
1076 }
1077
1078 VOID
1079 NTAPI
1080 UserServerHardError(
1081 IN PCSR_THREAD ThreadData,
1082 IN PHARDERROR_MSG Message)
1083 {
1084 ULONG ErrorMode;
1085 UINT dwType = 0;
1086 ULONG Timeout = INFINITE;
1087 UNICODE_STRING TextU, CaptionU;
1088 WCHAR LocalTextBuffer[256];
1089 WCHAR LocalCaptionBuffer[256];
1090
1091 ASSERT(ThreadData->Process != NULL);
1092
1093 /* Default to not handled */
1094 Message->Response = ResponseNotHandled;
1095
1096 /* Make sure we don't have too many parameters */
1097 if (Message->NumberOfParameters > MAXIMUM_HARDERROR_PARAMETERS)
1098 {
1099 // NOTE: Windows just fails (STATUS_INVALID_PARAMETER) & returns ResponseNotHandled.
1100 DPRINT1("Invalid NumberOfParameters = %d\n", Message->NumberOfParameters);
1101 Message->NumberOfParameters = MAXIMUM_HARDERROR_PARAMETERS;
1102 }
1103 if (Message->ValidResponseOptions > OptionCancelTryContinue)
1104 {
1105 DPRINT1("Unknown ValidResponseOptions = %d\n", Message->ValidResponseOptions);
1106 return; // STATUS_INVALID_PARAMETER;
1107 }
1108 if (Message->Status == STATUS_SERVICE_NOTIFICATION)
1109 {
1110 if (Message->NumberOfParameters < 3)
1111 {
1112 DPRINT1("Invalid NumberOfParameters = %d for STATUS_SERVICE_NOTIFICATION\n",
1113 Message->NumberOfParameters);
1114 return; // STATUS_INVALID_PARAMETER;
1115 }
1116 // (Message->UnicodeStringParameterMask & 0x3)
1117 }
1118
1119 /* Re-initialize the hard errors cache */
1120 UserInitHardErrorsCache();
1121
1122 /* Format the message caption and text */
1123 RtlInitEmptyUnicodeString(&TextU, LocalTextBuffer, sizeof(LocalTextBuffer));
1124 RtlInitEmptyUnicodeString(&CaptionU, LocalCaptionBuffer, sizeof(LocalCaptionBuffer));
1125 UserpFormatMessages(&TextU, &CaptionU, &dwType, &Timeout, Message);
1126
1127 /* Log the hard error message */
1128 UserpLogHardError(&TextU, &CaptionU);
1129
1130 /* Display a hard error popup depending on the current ErrorMode */
1131
1132 /* Query the error mode value */
1133 ErrorMode = GetRegInt(L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Windows",
1134 L"ErrorMode", 0);
1135
1136 if (Message->Status != STATUS_SERVICE_NOTIFICATION && ErrorMode != 0)
1137 {
1138 /* Returns OK for the hard error */
1139 Message->Response = ResponseOk;
1140 goto Quit;
1141 }
1142
1143 if (Message->ValidResponseOptions == OptionOkNoWait)
1144 {
1145 /* Display the balloon */
1146 Message->Response = ResponseOk;
1147 if (UserpShowInformationBalloon(&TextU,
1148 &CaptionU,
1149 dwType,
1150 Message))
1151 {
1152 Message->Response = ResponseOk;
1153 goto Quit;
1154 }
1155 }
1156
1157 /* Display the message box */
1158 Message->Response = UserpMessageBox(&TextU,
1159 &CaptionU,
1160 dwType,
1161 Timeout);
1162
1163 Quit:
1164 /* Free the strings if they have been reallocated */
1165 if (TextU.Buffer != LocalTextBuffer)
1166 RtlFreeUnicodeString(&TextU);
1167 if (CaptionU.Buffer != LocalCaptionBuffer)
1168 RtlFreeUnicodeString(&CaptionU);
1169
1170 return;
1171 }
1172
1173 VOID
1174 UserInitHardErrorsCache(VOID)
1175 {
1176 NTSTATUS Status;
1177 LCID CurrentUserLCID = 0;
1178
1179 Status = NtQueryDefaultLocale(TRUE, &CurrentUserLCID);
1180 if (!NT_SUCCESS(Status) || CurrentUserLCID == 0)
1181 {
1182 /* Fall back to english locale */
1183 DPRINT1("NtQueryDefaultLocale failed with Status = 0x%08lx\n", Status);
1184 // LOCALE_SYSTEM_DEFAULT;
1185 CurrentUserLCID = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
1186 }
1187 if (g_CurrentUserLangId == LANGIDFROMLCID(CurrentUserLCID))
1188 {
1189 /* The current lang ID and the hard error strings have already been cached */
1190 return;
1191 }
1192
1193 /* Load the strings using the current system locale */
1194 RtlLoadUnicodeString(UserServerDllInstance, IDS_SEVERITY_SUCCESS,
1195 &g_SuccessU, L"Success");
1196 RtlLoadUnicodeString(UserServerDllInstance, IDS_SEVERITY_INFORMATIONAL,
1197 &g_InformationU, L"System Information");
1198 RtlLoadUnicodeString(UserServerDllInstance, IDS_SEVERITY_WARNING,
1199 &g_WarningU, L"System Warning");
1200 RtlLoadUnicodeString(UserServerDllInstance, IDS_SEVERITY_ERROR,
1201 &g_ErrorU, L"System Error");
1202 // "unknown software exception"
1203 RtlLoadUnicodeString(UserServerDllInstance, IDS_SYSTEM_PROCESS,
1204 &g_SystemProcessU, L"System Process");
1205 RtlLoadUnicodeString(UserServerDllInstance, IDS_OK_TERMINATE_PROGRAM,
1206 &g_OKTerminateU, L"Click on OK to terminate the program.");
1207 RtlLoadUnicodeString(UserServerDllInstance, IDS_CANCEL_DEBUG_PROGRAM,
1208 &g_CancelDebugU, L"Click on CANCEL to debug the program.");
1209
1210 /* Remember that we cached the hard error strings */
1211 g_CurrentUserLangId = LANGIDFROMLCID(CurrentUserLCID);
1212 }
1213
1214 /* EOF */