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