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