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