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