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
11 /* INCLUDES *******************************************************************/
15 #define NTOS_MODE_USER
16 #include <ndk/mmfuncs.h>
18 #include <undocelfapi.h>
19 #include <ntstrsafe.h>
27 /* FUNCTIONS ******************************************************************/
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
};
41 IN HINSTANCE hInstance OPTIONAL
,
43 OUT PUNICODE_STRING pUnicodeString
,
44 IN PCWSTR pDefaultString
)
48 /* Try to load the string from the resource */
49 Length
= LoadStringW(hInstance
, uID
, (LPWSTR
)&pUnicodeString
->Buffer
, 0);
52 /* If the resource string was not found, use the fallback default one */
53 RtlInitUnicodeString(pUnicodeString
, pDefaultString
);
57 /* Set the string length (not NULL-terminated!) */
58 pUnicodeString
->MaximumLength
= (USHORT
)(Length
* sizeof(WCHAR
));
59 pUnicodeString
->Length
= pUnicodeString
->MaximumLength
;
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 .
72 const wchar_t *format
,
78 va_start(args
, format
);
79 len
= _vscwprintf(format
, args
);
100 UserpCaptureStringParameters(
101 OUT PULONG_PTR Parameters
,
102 OUT PULONG SizeOfAllUnicodeStrings
,
103 IN PHARDERROR_MSG Message
,
104 IN HANDLE hProcess OPTIONAL
)
107 ULONG nParam
, Size
= 0;
108 UNICODE_STRING TempStringU
, ParamStringU
;
109 ANSI_STRING TempStringA
;
111 if (SizeOfAllUnicodeStrings
)
112 *SizeOfAllUnicodeStrings
= 0;
114 /* Read all strings from client space */
115 for (nParam
= 0; nParam
< Message
->NumberOfParameters
; ++nParam
)
117 Parameters
[nParam
] = 0;
119 /* Check if the current parameter is a unicode string */
120 if (Message
->UnicodeStringParameterMask
& (1 << nParam
))
122 /* Skip this string if we do not have a client process */
126 /* Read the UNICODE_STRING from the process memory */
127 Status
= NtReadVirtualMemory(hProcess
,
128 (PVOID
)Message
->Parameters
[nParam
],
130 sizeof(ParamStringU
),
132 if (!NT_SUCCESS(Status
))
134 /* We failed, skip this string */
135 DPRINT1("NtReadVirtualMemory(Message->Parameters) failed, Status 0x%lx, skipping.\n", Status
);
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(),
144 TempStringU
.MaximumLength
);
145 if (!TempStringU
.Buffer
)
147 /* We failed, skip this string */
148 DPRINT1("Cannot allocate memory with size %u, skipping.\n", TempStringU
.MaximumLength
);
152 /* Read the string buffer from the process memory */
153 Status
= NtReadVirtualMemory(hProcess
,
158 if (!NT_SUCCESS(Status
))
160 /* We failed, skip this string */
161 DPRINT1("NtReadVirtualMemory(ParamStringU) failed, Status 0x%lx, skipping.\n", Status
);
162 RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringU
.Buffer
);
165 /* NULL-terminate the string */
166 TempStringU
.Buffer
[TempStringU
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
168 DPRINT("ParamString = \'%wZ\'\n", &TempStringU
);
170 if (Message
->Status
== STATUS_SERVICE_NOTIFICATION
)
172 /* Just keep the allocated NULL-terminated UNICODE string */
173 Parameters
[nParam
] = (ULONG_PTR
)TempStringU
.Buffer
;
174 Size
+= TempStringU
.Length
;
178 /* Allocate a buffer for conversion to ANSI string */
179 TempStringA
.MaximumLength
= (USHORT
)RtlUnicodeStringToAnsiSize(&TempStringU
);
180 TempStringA
.Buffer
= RtlAllocateHeap(RtlGetProcessHeap(),
182 TempStringA
.MaximumLength
);
183 if (!TempStringA
.Buffer
)
185 /* We failed, skip this string */
186 DPRINT1("Cannot allocate memory with size %u, skipping.\n", TempStringA
.MaximumLength
);
187 RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringU
.Buffer
);
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
))
196 /* We failed, skip this string */
197 DPRINT1("RtlUnicodeStringToAnsiString() failed, Status 0x%lx, skipping.\n", Status
);
198 RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringA
.Buffer
);
202 /* Note: RtlUnicodeStringToAnsiString() returns a NULL-terminated string */
203 Parameters
[nParam
] = (ULONG_PTR
)TempStringA
.Buffer
;
204 Size
+= TempStringU
.Length
;
209 /* It's not a unicode string, just copy the parameter */
210 Parameters
[nParam
] = Message
->Parameters
[nParam
];
214 if (SizeOfAllUnicodeStrings
)
215 *SizeOfAllUnicodeStrings
= Size
;
220 UserpFreeStringParameters(
221 IN OUT PULONG_PTR Parameters
,
222 IN PHARDERROR_MSG Message
)
226 /* Loop all parameters */
227 for (nParam
= 0; nParam
< Message
->NumberOfParameters
; ++nParam
)
229 /* Check if the current parameter is a string */
230 if ((Message
->UnicodeStringParameterMask
& (1 << nParam
)) && (Parameters
[nParam
] != 0))
232 /* Free the string buffer */
233 RtlFreeHeap(RtlGetProcessHeap(), 0, (PVOID
)Parameters
[nParam
]);
240 UserpGetClientFileName(
241 OUT PUNICODE_STRING ClientFileNameU
,
244 PLIST_ENTRY ModuleListHead
;
246 PLDR_DATA_TABLE_ENTRY Module
;
248 PROCESS_BASIC_INFORMATION ClientBasicInfo
;
249 LDR_DATA_TABLE_ENTRY ModuleData
;
254 /* Initialize string */
255 RtlInitEmptyUnicodeString(ClientFileNameU
, NULL
, 0);
257 /* Query process information */
258 Status
= NtQueryInformationProcess(hProcess
,
259 ProcessBasicInformation
,
261 sizeof(ClientBasicInfo
),
263 if (!NT_SUCCESS(Status
)) return Status
;
265 /* Locate the process loader data table and retrieve its name from it */
267 Peb
= ClientBasicInfo
.PebBaseAddress
;
268 if (!Peb
) return STATUS_UNSUCCESSFUL
;
270 Status
= NtReadVirtualMemory(hProcess
, &Peb
->Ldr
, &Ldr
, sizeof(Ldr
), NULL
);
271 if (!NT_SUCCESS(Status
)) return Status
;
273 ModuleListHead
= &Ldr
->InLoadOrderModuleList
;
274 Status
= NtReadVirtualMemory(hProcess
,
275 &ModuleListHead
->Flink
,
279 if (!NT_SUCCESS(Status
)) return Status
;
281 if (Entry
== ModuleListHead
) return STATUS_UNSUCCESSFUL
;
283 Module
= CONTAINING_RECORD(Entry
, LDR_DATA_TABLE_ENTRY
, InLoadOrderLinks
);
285 Status
= NtReadVirtualMemory(hProcess
,
290 if (!NT_SUCCESS(Status
)) return Status
;
292 Status
= NtReadVirtualMemory(hProcess
,
293 &Peb
->ImageBaseAddress
,
295 sizeof(ClientDllBase
),
297 if (!NT_SUCCESS(Status
)) return Status
;
299 if (ClientDllBase
!= ModuleData
.DllBase
) return STATUS_UNSUCCESSFUL
;
301 ClientFileNameU
->MaximumLength
= ModuleData
.BaseDllName
.MaximumLength
;
302 ClientFileNameU
->Buffer
= RtlAllocateHeap(RtlGetProcessHeap(),
304 ClientFileNameU
->MaximumLength
);
305 if (!ClientFileNameU
->Buffer
)
307 RtlInitEmptyUnicodeString(ClientFileNameU
, NULL
, 0);
308 return STATUS_NO_MEMORY
;
311 Status
= NtReadVirtualMemory(hProcess
,
312 ModuleData
.BaseDllName
.Buffer
,
313 ClientFileNameU
->Buffer
,
314 ClientFileNameU
->MaximumLength
,
316 if (!NT_SUCCESS(Status
))
318 RtlFreeHeap(RtlGetProcessHeap(), 0, ClientFileNameU
->Buffer
);
319 RtlInitEmptyUnicodeString(ClientFileNameU
, NULL
, 0);
323 ClientFileNameU
->Length
= (USHORT
)(wcslen(ClientFileNameU
->Buffer
) * sizeof(WCHAR
));
324 DPRINT("ClientFileNameU = \'%wZ\'\n", &ClientFileNameU
);
326 return STATUS_SUCCESS
;
331 UserpDuplicateParamStringToUnicodeString(
332 IN OUT PUNICODE_STRING UnicodeString
,
333 IN PCWSTR ParamString
)
335 UNICODE_STRING FormatU
, TempStringU
;
337 /* Calculate buffer length for the text message */
338 RtlInitUnicodeString(&FormatU
, (PWSTR
)ParamString
);
339 if (UnicodeString
->MaximumLength
< FormatU
.MaximumLength
)
341 /* Duplicate the text message in a larger buffer */
342 if (NT_SUCCESS(RtlDuplicateUnicodeString(RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE
,
343 &FormatU
, &TempStringU
)))
345 *UnicodeString
= TempStringU
;
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");
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
);
360 /* Copy the string, NULL-terminate it */
361 RtlCopyUnicodeString(UnicodeString
, &FormatU
);
368 IN OUT PUNICODE_STRING TextStringU
,
369 IN OUT PUNICODE_STRING CaptionStringU
,
371 OUT PULONG pdwTimeout
,
372 IN PHARDERROR_MSG Message
)
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";
383 OBJECT_ATTRIBUTES ObjectAttributes
;
385 ULONG Severity
= (ULONG
)(Message
->Status
) >> 30;
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
;
392 PMESSAGE_RESOURCE_ENTRY MessageResource
;
393 PWSTR FormatString
, pszBuffer
;
396 /* Open client process */
397 InitializeObjectAttributes(&ObjectAttributes
, NULL
, 0, NULL
, NULL
);
398 Status
= NtOpenProcess(&hProcess
,
399 PROCESS_VM_READ
| PROCESS_QUERY_INFORMATION
,
401 &Message
->h
.ClientId
);
402 if (!NT_SUCCESS(Status
))
404 DPRINT1("NtOpenProcess failed with status 0x%08lx, possibly SYSTEM process.\n", Status
);
408 /* Capture all string parameters from the process memory */
409 UserpCaptureStringParameters(Parameters
, &SizeOfStrings
, Message
, hProcess
);
411 /* Initialize the output strings */
412 TextStringU
->Length
= 0;
413 TextStringU
->Buffer
[0] = UNICODE_NULL
;
415 CaptionStringU
->Length
= 0;
416 CaptionStringU
->Buffer
[0] = UNICODE_NULL
;
419 * Check whether it is a service notification, in which case
420 * we format the parameters and take the short route.
422 if (Message
->Status
== STATUS_SERVICE_NOTIFICATION
)
424 /* Close the process handle */
425 if (hProcess
) NtClose(hProcess
);
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.
432 *pdwType
= (UINT
)Parameters
[2] & ~MB_SERVICE_NOTIFICATION
;
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.
439 if (Message
->UnicodeStringParameterMask
& 0x1)
440 UserpDuplicateParamStringToUnicodeString(TextStringU
, (PCWSTR
)Parameters
[0]);
441 if (Message
->UnicodeStringParameterMask
& 0x2)
442 UserpDuplicateParamStringToUnicodeString(CaptionStringU
, (PCWSTR
)Parameters
[1]);
444 /* Set the timeout */
445 if (Message
->NumberOfParameters
>= 4)
446 *pdwTimeout
= (ULONG
)Parameters
[3];
448 *pdwTimeout
= INFINITE
;
453 /* Set the message box type */
455 switch (Message
->ValidResponseOptions
)
457 case OptionAbortRetryIgnore
:
458 *pdwType
= MB_ABORTRETRYIGNORE
;
464 *pdwType
= MB_OKCANCEL
;
466 case OptionRetryCancel
:
467 *pdwType
= MB_RETRYCANCEL
;
472 case OptionYesNoCancel
:
473 *pdwType
= MB_YESNOCANCEL
;
475 case OptionShutdownSystem
:
481 case OptionCancelTryContinue
:
482 *pdwType
= MB_CANCELTRYCONTINUE
;
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
;
492 *pdwType
|= MB_SYSTEMMODAL
| MB_SETFOREGROUND
;
494 /* Set the timeout */
495 *pdwTimeout
= INFINITE
;
497 /* Copy the Parameters array locally */
498 RtlCopyMemory(&CopyParameters
, Parameters
, sizeof(CopyParameters
));
500 /* Get the file name of the client process */
501 Status
= STATUS_SUCCESS
;
503 Status
= UserpGetClientFileName(&FileNameU
, hProcess
);
505 /* Close the process handle but keep its original value to know where stuff came from */
506 if (hProcess
) NtClose(hProcess
);
509 * Fall back to SYSTEM process if the client process handle
510 * was NULL or we failed retrieving a file name.
512 if (!hProcess
|| !NT_SUCCESS(Status
) || !FileNameU
.Buffer
)
515 FileNameU
= g_SystemProcessU
;
518 /* Retrieve the description of the error code */
519 FormatA
.Buffer
= NULL
;
520 Status
= RtlFindMessage(GetModuleHandleW(L
"ntdll"),
521 (ULONG_PTR
)RT_MESSAGETABLE
,
525 if (NT_SUCCESS(Status
))
527 if (MessageResource
->Flags
)
529 RtlInitUnicodeString(&FormatU
, (PWSTR
)MessageResource
->Text
);
530 FormatA
.Buffer
= NULL
;
534 RtlInitAnsiString(&FormatA
, (PSTR
)MessageResource
->Text
);
535 /* Status = */ RtlAnsiStringToUnicodeString(&FormatU
, &FormatA
, TRUE
);
537 ASSERT(FormatU
.Buffer
);
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".
547 RtlInitEmptyUnicodeString(&FormatU
, NULL
, 0);
548 FormatA
.Buffer
= NULL
;
551 FormatString
= FormatU
.Buffer
;
553 /* Check whether a caption is specified in the format string */
554 if (FormatString
&& FormatString
[0] == L
'{')
556 /* Set caption start */
557 TempStringU
.Buffer
= ++FormatString
;
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
)
567 /* Skip '}', '\r', '\n' */
570 TempStringU
.Length
*= sizeof(WCHAR
);
571 TempStringU
.MaximumLength
= TempStringU
.Length
;
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
;
584 ASSERT(FALSE
); // Unexpected, since Severity is only <= 3.
587 /* Retrieve the window title of the client, if it has one */
588 RtlInitEmptyUnicodeString(&WindowTitleU
, L
"", 0);
590 EnumThreadWindows(HandleToUlong(Message
->h
.ClientId
.UniqueThread
),
591 FindTopLevelWnd
, (LPARAM
)&hwndOwner
);
594 cszBuffer
= GetWindowTextLengthW(hwndOwner
);
597 cszBuffer
+= 3; // 2 characters for ": " and a NULL terminator.
598 cszBuffer
*= sizeof(WCHAR
);
599 pszBuffer
= RtlAllocateHeap(RtlGetProcessHeap(),
604 RtlInitEmptyUnicodeString(&WindowTitleU
, pszBuffer
, (USHORT
)cszBuffer
);
605 cszBuffer
= GetWindowTextW(hwndOwner
,
607 WindowTitleU
.MaximumLength
/ sizeof(WCHAR
));
608 WindowTitleU
.Length
= (USHORT
)(cszBuffer
* sizeof(WCHAR
));
609 RtlAppendUnicodeToString(&WindowTitleU
, L
": ");
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
)
619 /* Allocate a larger buffer for the caption */
620 pszBuffer
= RtlAllocateHeap(RtlGetProcessHeap(),
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");
630 RtlInitEmptyUnicodeString(CaptionStringU
, pszBuffer
, (USHORT
)cszBuffer
);
633 CaptionStringU
->Length
= 0;
634 CaptionStringU
->Buffer
[0] = UNICODE_NULL
;
636 /* Build the caption */
637 RtlStringCbPrintfW(CaptionStringU
->Buffer
,
638 CaptionStringU
->MaximumLength
,
640 &WindowTitleU
, &FileNameU
, &TempStringU
);
641 CaptionStringU
->Length
= (USHORT
)(wcslen(CaptionStringU
->Buffer
) * sizeof(WCHAR
));
643 /* Free the strings if needed */
644 if (WindowTitleU
.Buffer
&& (WindowTitleU
.MaximumLength
!= 0))
645 RtlFreeUnicodeString(&WindowTitleU
);
647 RtlFreeUnicodeString(&FileNameU
);
649 Format2A
.Buffer
= NULL
;
651 /* If we have an unknown hard error, skip the special cases handling */
655 /* Check if this is an exception message */
656 if (Message
->Status
== STATUS_UNHANDLED_EXCEPTION
)
658 ULONG ExceptionCode
= CopyParameters
[0];
660 /* Retrieve the description of the exception code */
661 Status
= RtlFindMessage(GetModuleHandleW(L
"ntdll"),
662 (ULONG_PTR
)RT_MESSAGETABLE
,
666 if (NT_SUCCESS(Status
))
668 if (MessageResource
->Flags
)
670 RtlInitUnicodeString(&Format2U
, (PWSTR
)MessageResource
->Text
);
671 Format2A
.Buffer
= NULL
;
675 RtlInitAnsiString(&Format2A
, (PSTR
)MessageResource
->Text
);
676 /* Status = */ RtlAnsiStringToUnicodeString(&Format2U
, &Format2A
, TRUE
);
678 ASSERT(Format2U
.Buffer
);
680 /* Handle special cases */
681 if (ExceptionCode
== STATUS_ACCESS_VIOLATION
)
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";
690 CopyParameters
[2] = (ULONG_PTR
)L
"read";
692 else if (ExceptionCode
== STATUS_IN_PAGE_ERROR
)
694 /* Use a new FormatString */
695 FormatString
= Format2U
.Buffer
;
696 CopyParameters
[0] = CopyParameters
[1];
697 CopyParameters
[1] = CopyParameters
[3];
701 /* Keep the existing FormatString */
702 CopyParameters
[2] = CopyParameters
[1];
703 CopyParameters
[1] = CopyParameters
[0];
705 pszBuffer
= Format2U
.Buffer
;
706 if (!_wcsnicmp(pszBuffer
, L
"{EXCEPTION}", 11))
709 * This is a named exception. Skip the mark and
710 * retrieve the exception name that follows it.
714 /* Skip '\r', '\n' */
717 CopyParameters
[0] = (ULONG_PTR
)pszBuffer
;
721 /* Fall back to hardcoded value */
722 CopyParameters
[0] = (ULONG_PTR
)L
"unknown software exception";
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";
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.
742 /* Wrap in SEH to protect from invalid string parameters */
747 /* Fall back to unknown hard error format string, and use the original parameters */
748 cszBuffer
= _scwprintf(pszUnknownHardError
,
750 Parameters
[0], Parameters
[1],
751 Parameters
[2], Parameters
[3]);
752 cszBuffer
*= sizeof(WCHAR
);
756 cszBuffer
= _scwprintf(FormatString
,
757 CopyParameters
[0], CopyParameters
[1],
758 CopyParameters
[2], CopyParameters
[3]);
759 cszBuffer
*= sizeof(WCHAR
);
761 /* Add a description for the dialog buttons */
762 if (Message
->Status
== STATUS_UNHANDLED_EXCEPTION
)
764 if (Message
->ValidResponseOptions
== OptionOk
||
765 Message
->ValidResponseOptions
== OptionOkCancel
)
767 /* Reserve space for one newline and the OK-terminate-program string */
768 cszBuffer
+= sizeof(WCHAR
) + g_OKTerminateU
.Length
;
770 if (Message
->ValidResponseOptions
== OptionOkCancel
)
772 /* Reserve space for one newline and the CANCEL-debug-program string */
773 cszBuffer
+= sizeof(WCHAR
) + g_CancelDebugU
.Length
;
778 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
780 /* An exception occurred, use a default string with the original parameters */
781 cszBuffer
= _scwprintf(pszExceptionHardError
,
783 Parameters
[0], Parameters
[1],
784 Parameters
[2], Parameters
[3]);
785 cszBuffer
*= sizeof(WCHAR
);
789 cszBuffer
+= SizeOfStrings
+ sizeof(UNICODE_NULL
);
791 if (TextStringU
->MaximumLength
< cszBuffer
)
793 /* Allocate a larger buffer for the text message */
794 pszBuffer
= RtlAllocateHeap(RtlGetProcessHeap(),
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");
804 RtlInitEmptyUnicodeString(TextStringU
, pszBuffer
, (USHORT
)cszBuffer
);
807 TextStringU
->Length
= 0;
808 TextStringU
->Buffer
[0] = UNICODE_NULL
;
810 /* Wrap in SEH to protect from invalid string parameters */
813 /* Print the string into the buffer */
814 pszBuffer
= TextStringU
->Buffer
;
815 cszBuffer
= TextStringU
->MaximumLength
;
819 /* Fall back to unknown hard error format string, and use the original parameters */
820 RtlStringCbPrintfW(pszBuffer
, cszBuffer
,
823 Parameters
[0], Parameters
[1],
824 Parameters
[2], Parameters
[3]);
828 RtlStringCbPrintfExW(pszBuffer
, cszBuffer
,
829 &pszBuffer
, &cszBuffer
,
832 CopyParameters
[0], CopyParameters
[1],
833 CopyParameters
[2], CopyParameters
[3]);
835 /* Add a description for the dialog buttons */
836 if (Message
->Status
== STATUS_UNHANDLED_EXCEPTION
)
838 if (Message
->ValidResponseOptions
== OptionOk
||
839 Message
->ValidResponseOptions
== OptionOkCancel
)
841 RtlStringCbPrintfExW(pszBuffer
, cszBuffer
,
842 &pszBuffer
, &cszBuffer
,
847 if (Message
->ValidResponseOptions
== OptionOkCancel
)
849 RtlStringCbPrintfExW(pszBuffer
, cszBuffer
,
850 &pszBuffer
, &cszBuffer
,
858 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
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());
864 RtlStringCbPrintfW(TextStringU
->Buffer
,
865 TextStringU
->MaximumLength
,
866 pszExceptionHardError
,
868 Parameters
[0], Parameters
[1],
869 Parameters
[2], Parameters
[3]);
873 TextStringU
->Length
= (USHORT
)(wcslen(TextStringU
->Buffer
) * sizeof(WCHAR
));
875 /* Free the converted UNICODE strings */
876 if (Format2A
.Buffer
) RtlFreeUnicodeString(&Format2U
);
877 if (FormatA
.Buffer
) RtlFreeUnicodeString(&FormatU
);
880 /* Free the captured parameters */
881 UserpFreeStringParameters(Parameters
, Message
);
888 IN ULONG DefaultValue
)
891 ULONG Value
= DefaultValue
;
892 UNICODE_STRING String
;
893 OBJECT_ATTRIBUTES ObjectAttributes
;
896 UCHAR ValueBuffer
[sizeof(KEY_VALUE_PARTIAL_INFORMATION
) + sizeof(ULONG
)];
897 PKEY_VALUE_PARTIAL_INFORMATION ValueInfo
= (PKEY_VALUE_PARTIAL_INFORMATION
)ValueBuffer
;
899 RtlInitUnicodeString(&String
, KeyName
);
900 InitializeObjectAttributes(&ObjectAttributes
,
902 OBJ_CASE_INSENSITIVE
,
906 /* Open the registry key */
907 Status
= NtOpenKey(&KeyHandle
, KEY_READ
, &ObjectAttributes
);
908 if (NT_SUCCESS(Status
))
910 /* Query the value */
911 RtlInitUnicodeString(&String
, ValueName
);
912 Status
= NtQueryValueKey(KeyHandle
,
914 KeyValuePartialInformation
,
919 /* Close the registry key */
922 if (NT_SUCCESS(Status
) && (ValueInfo
->Type
== REG_DWORD
))
924 /* Directly retrieve the data */
925 Value
= *(PULONG
)ValueInfo
->Data
;
933 UserpShowInformationBalloon(
934 IN PUNICODE_STRING TextStringU
,
935 IN PUNICODE_STRING CaptionStringU
,
937 IN PHARDERROR_MSG Message
)
939 ULONG ShellErrorMode
;
941 COPYDATASTRUCT CopyData
;
942 PBALLOON_HARD_ERROR_DATA pdata
;
943 DWORD dwSize
, cbTextLen
, cbTitleLen
;
944 PWCHAR pText
, pCaption
;
948 /* Query the shell error mode value */
949 ShellErrorMode
= GetRegInt(L
"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Windows",
950 L
"ShellErrorMode", 0);
952 /* Make the shell display the hard error message only if necessary */
953 if (ShellErrorMode
!= 1)
956 /* Retrieve the shell task window */
957 hWndTaskman
= GetTaskmanWindow();
960 DPRINT1("Failed to find shell task window (last error %lu)\n", GetLastError());
964 cbTextLen
= TextStringU
->Length
+ sizeof(UNICODE_NULL
);
965 cbTitleLen
= CaptionStringU
->Length
+ sizeof(UNICODE_NULL
);
967 dwSize
= sizeof(BALLOON_HARD_ERROR_DATA
);
968 dwSize
+= cbTextLen
+ cbTitleLen
;
970 /* Build the data buffer */
971 pdata
= RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, dwSize
);
974 DPRINT1("Failed to allocate balloon data\n");
978 pdata
->cbHeaderSize
= sizeof(BALLOON_HARD_ERROR_DATA
);
979 pdata
->Status
= Message
->Status
;
980 pdata
->dwType
= Type
;
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
);
989 /* Send the message */
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
;
997 ret
= SendMessageTimeoutW(hWndTaskman
, WM_COPYDATA
, 0, (LPARAM
)&CopyData
,
998 SMTO_NORMAL
| SMTO_ABORTIFHUNG
, 3000, &dwResult
);
1000 /* Free the buffer */
1001 RtlFreeHeap(RtlGetProcessHeap(), 0, pdata
);
1003 return (ret
&& dwResult
) ? TRUE
: FALSE
;
1009 IN PUNICODE_STRING TextStringU
,
1010 IN PUNICODE_STRING CaptionStringU
,
1014 ULONG MessageBoxResponse
;
1016 DPRINT("Text = '%S', Caption = '%S', Type = 0x%lx\n",
1017 TextStringU
->Buffer
, CaptionStringU
->Buffer
, Type
);
1019 /* Display a message box */
1020 MessageBoxResponse
= MessageBoxTimeoutW(NULL
,
1021 TextStringU
->Buffer
,
1022 CaptionStringU
->Buffer
,
1025 /* Return response value */
1026 switch (MessageBoxResponse
)
1028 case IDOK
: return ResponseOk
;
1029 case IDCANCEL
: return ResponseCancel
;
1030 case IDYES
: return ResponseYes
;
1031 case IDNO
: return ResponseNo
;
1032 case IDABORT
: return ResponseAbort
;
1033 case IDIGNORE
: return ResponseIgnore
;
1034 case IDRETRY
: return ResponseRetry
;
1035 case IDTRYAGAIN
: return ResponseTryAgain
;
1036 case IDCONTINUE
: return ResponseContinue
;
1037 default: return ResponseNotHandled
;
1040 return ResponseNotHandled
;
1046 IN PUNICODE_STRING TextStringU
,
1047 IN PUNICODE_STRING CaptionStringU
)
1051 UNICODE_STRING UNCServerNameU
= {0, 0, NULL
};
1052 UNICODE_STRING SourceNameU
= RTL_CONSTANT_STRING(L
"Application Popup");
1053 PUNICODE_STRING Strings
[] = {CaptionStringU
, TextStringU
};
1055 Status
= ElfRegisterEventSourceW(&UNCServerNameU
, &SourceNameU
, &hEventLog
);
1056 if (!NT_SUCCESS(Status
) || !hEventLog
)
1058 DPRINT1("ElfRegisterEventSourceW failed with Status 0x%08lx\n", Status
);
1062 Status
= ElfReportEventW(hEventLog
,
1063 EVENTLOG_INFORMATION_TYPE
,
1065 STATUS_LOG_HARD_ERROR
,
1074 if (!NT_SUCCESS(Status
))
1075 DPRINT1("ElfReportEventW failed with Status 0x%08lx\n", Status
);
1077 ElfDeregisterEventSource(hEventLog
);
1082 UserServerHardError(
1083 IN PCSR_THREAD ThreadData
,
1084 IN PHARDERROR_MSG Message
)
1088 ULONG Timeout
= INFINITE
;
1089 UNICODE_STRING TextU
, CaptionU
;
1090 WCHAR LocalTextBuffer
[256];
1091 WCHAR LocalCaptionBuffer
[256];
1093 ASSERT(ThreadData
->Process
!= NULL
);
1095 /* Default to not handled */
1096 Message
->Response
= ResponseNotHandled
;
1098 /* Make sure we don't have too many parameters */
1099 if (Message
->NumberOfParameters
> MAXIMUM_HARDERROR_PARAMETERS
)
1101 // NOTE: Windows just fails (STATUS_INVALID_PARAMETER) & returns ResponseNotHandled.
1102 DPRINT1("Invalid NumberOfParameters = %d\n", Message
->NumberOfParameters
);
1103 Message
->NumberOfParameters
= MAXIMUM_HARDERROR_PARAMETERS
;
1105 if (Message
->ValidResponseOptions
> OptionCancelTryContinue
)
1107 DPRINT1("Unknown ValidResponseOptions = %d\n", Message
->ValidResponseOptions
);
1108 return; // STATUS_INVALID_PARAMETER;
1110 if (Message
->Status
== STATUS_SERVICE_NOTIFICATION
)
1112 if (Message
->NumberOfParameters
< 3)
1114 DPRINT1("Invalid NumberOfParameters = %d for STATUS_SERVICE_NOTIFICATION\n",
1115 Message
->NumberOfParameters
);
1116 return; // STATUS_INVALID_PARAMETER;
1118 // (Message->UnicodeStringParameterMask & 0x3)
1121 /* Re-initialize the hard errors cache */
1122 UserInitHardErrorsCache();
1124 /* Format the message caption and text */
1125 RtlInitEmptyUnicodeString(&TextU
, LocalTextBuffer
, sizeof(LocalTextBuffer
));
1126 RtlInitEmptyUnicodeString(&CaptionU
, LocalCaptionBuffer
, sizeof(LocalCaptionBuffer
));
1127 UserpFormatMessages(&TextU
, &CaptionU
, &dwType
, &Timeout
, Message
);
1129 /* Log the hard error message */
1130 UserpLogHardError(&TextU
, &CaptionU
);
1132 /* Display a hard error popup depending on the current ErrorMode */
1134 /* Query the error mode value */
1135 ErrorMode
= GetRegInt(L
"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Windows",
1138 if (Message
->Status
!= STATUS_SERVICE_NOTIFICATION
&& ErrorMode
!= 0)
1140 /* Returns OK for the hard error */
1141 Message
->Response
= ResponseOk
;
1145 if (Message
->ValidResponseOptions
== OptionOkNoWait
)
1147 /* Display the balloon */
1148 Message
->Response
= ResponseOk
;
1149 if (UserpShowInformationBalloon(&TextU
,
1154 Message
->Response
= ResponseOk
;
1159 /* Display the message box */
1160 Message
->Response
= UserpMessageBox(&TextU
,
1166 /* Free the strings if they have been reallocated */
1167 if (TextU
.Buffer
!= LocalTextBuffer
)
1168 RtlFreeUnicodeString(&TextU
);
1169 if (CaptionU
.Buffer
!= LocalCaptionBuffer
)
1170 RtlFreeUnicodeString(&CaptionU
);
1176 UserInitHardErrorsCache(VOID
)
1179 LCID CurrentUserLCID
= 0;
1181 Status
= NtQueryDefaultLocale(TRUE
, &CurrentUserLCID
);
1182 if (!NT_SUCCESS(Status
) || CurrentUserLCID
== 0)
1184 /* Fall back to english locale */
1185 DPRINT1("NtQueryDefaultLocale failed with Status = 0x%08lx\n", Status
);
1186 // LOCALE_SYSTEM_DEFAULT;
1187 CurrentUserLCID
= MAKELCID(MAKELANGID(LANG_ENGLISH
, SUBLANG_ENGLISH_US
), SORT_DEFAULT
);
1189 if (g_CurrentUserLangId
== LANGIDFROMLCID(CurrentUserLCID
))
1191 /* The current lang ID and the hard error strings have already been cached */
1195 /* Load the strings using the current system locale */
1196 RtlLoadUnicodeString(UserServerDllInstance
, IDS_SEVERITY_SUCCESS
,
1197 &g_SuccessU
, L
"Success");
1198 RtlLoadUnicodeString(UserServerDllInstance
, IDS_SEVERITY_INFORMATIONAL
,
1199 &g_InformationU
, L
"System Information");
1200 RtlLoadUnicodeString(UserServerDllInstance
, IDS_SEVERITY_WARNING
,
1201 &g_WarningU
, L
"System Warning");
1202 RtlLoadUnicodeString(UserServerDllInstance
, IDS_SEVERITY_ERROR
,
1203 &g_ErrorU
, L
"System Error");
1204 // "unknown software exception"
1205 RtlLoadUnicodeString(UserServerDllInstance
, IDS_SYSTEM_PROCESS
,
1206 &g_SystemProcessU
, L
"System Process");
1207 RtlLoadUnicodeString(UserServerDllInstance
, IDS_OK_TERMINATE_PROGRAM
,
1208 &g_OKTerminateU
, L
"Click on OK to terminate the program.");
1209 RtlLoadUnicodeString(UserServerDllInstance
, IDS_CANCEL_DEBUG_PROGRAM
,
1210 &g_CancelDebugU
, L
"Click on CANCEL to debug the program.");
1212 /* Remember that we cached the hard error strings */
1213 g_CurrentUserLangId
= LANGIDFROMLCID(CurrentUserLCID
);