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