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 converted to ANSI string */
179 TempStringA
.MaximumLength
= 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 which 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
, NULL
, 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 WindowTitleU
.MaximumLength
= (USHORT
)(cszBuffer
* sizeof(WCHAR
));
599 WindowTitleU
.Buffer
= RtlAllocateHeap(RtlGetProcessHeap(),
601 WindowTitleU
.MaximumLength
);
602 if (WindowTitleU
.Buffer
)
604 cszBuffer
= GetWindowTextW(hwndOwner
,
606 WindowTitleU
.MaximumLength
/ sizeof(WCHAR
));
607 WindowTitleU
.Length
= (USHORT
)(cszBuffer
* sizeof(WCHAR
));
608 RtlAppendUnicodeToString(&WindowTitleU
, L
": ");
612 RtlInitEmptyUnicodeString(&WindowTitleU
, NULL
, 0);
617 /* Calculate buffer length for the caption */
618 cszBuffer
= WindowTitleU
.Length
+ FileNameU
.Length
+ TempStringU
.Length
+
619 3 * sizeof(WCHAR
) + sizeof(UNICODE_NULL
);
620 if (CaptionStringU
->MaximumLength
< cszBuffer
)
622 /* Allocate a larger buffer for the caption */
623 pszBuffer
= RtlAllocateHeap(RtlGetProcessHeap(),
628 /* We could not allocate a larger buffer; continue using the smaller original buffer */
629 DPRINT1("Cannot allocate memory for CaptionStringU, use original buffer.\n");
633 RtlInitEmptyUnicodeString(CaptionStringU
, pszBuffer
, (USHORT
)cszBuffer
);
636 CaptionStringU
->Length
= 0;
637 CaptionStringU
->Buffer
[0] = UNICODE_NULL
;
639 /* Append the file name, the separator and the caption text */
640 RtlStringCbPrintfW(CaptionStringU
->Buffer
,
641 CaptionStringU
->MaximumLength
,
643 &WindowTitleU
, &FileNameU
, &TempStringU
);
644 CaptionStringU
->Length
= (USHORT
)(wcslen(CaptionStringU
->Buffer
) * sizeof(WCHAR
));
646 /* Free the strings if needed */
647 if (WindowTitleU
.Buffer
) RtlFreeUnicodeString(&WindowTitleU
);
648 if (hProcess
) RtlFreeUnicodeString(&FileNameU
);
650 Format2A
.Buffer
= NULL
;
652 /* If we have an unknown hard error, skip the special cases handling */
656 /* Check if this is an exception message */
657 if (Message
->Status
== STATUS_UNHANDLED_EXCEPTION
)
659 ULONG ExceptionCode
= CopyParameters
[0];
661 /* Retrieve the description of the exception code */
662 Status
= RtlFindMessage(GetModuleHandleW(L
"ntdll"),
663 (ULONG_PTR
)RT_MESSAGETABLE
,
667 if (NT_SUCCESS(Status
))
669 if (MessageResource
->Flags
)
671 RtlInitUnicodeString(&Format2U
, (PWSTR
)MessageResource
->Text
);
672 Format2A
.Buffer
= NULL
;
676 RtlInitAnsiString(&Format2A
, (PSTR
)MessageResource
->Text
);
677 /* Status = */ RtlAnsiStringToUnicodeString(&Format2U
, &Format2A
, TRUE
);
679 ASSERT(Format2U
.Buffer
);
681 /* Handle special cases */
682 if (ExceptionCode
== STATUS_ACCESS_VIOLATION
)
684 /* Use a new FormatString */
685 FormatString
= Format2U
.Buffer
;
686 CopyParameters
[0] = CopyParameters
[1];
687 CopyParameters
[1] = CopyParameters
[3];
688 if (CopyParameters
[2])
689 CopyParameters
[2] = (ULONG_PTR
)L
"written";
691 CopyParameters
[2] = (ULONG_PTR
)L
"read";
693 else if (ExceptionCode
== STATUS_IN_PAGE_ERROR
)
695 /* Use a new FormatString */
696 FormatString
= Format2U
.Buffer
;
697 CopyParameters
[0] = CopyParameters
[1];
698 CopyParameters
[1] = CopyParameters
[3];
702 /* Keep the existing FormatString */
703 CopyParameters
[2] = CopyParameters
[1];
704 CopyParameters
[1] = CopyParameters
[0];
706 pszBuffer
= Format2U
.Buffer
;
707 if (!_wcsnicmp(pszBuffer
, L
"{EXCEPTION}", 11))
710 * This is a named exception. Skip the mark and
711 * retrieve the exception name that follows it.
715 /* Skip '\r', '\n' */
718 CopyParameters
[0] = (ULONG_PTR
)pszBuffer
;
722 /* Fall back to hardcoded value */
723 CopyParameters
[0] = (ULONG_PTR
)L
"unknown software exception";
729 /* Fall back to hardcoded value, and keep the existing FormatString */
730 CopyParameters
[2] = CopyParameters
[1];
731 CopyParameters
[1] = CopyParameters
[0];
732 CopyParameters
[0] = (ULONG_PTR
)L
"unknown software exception";
738 * Calculate buffer length for the text message. If FormatString
739 * is NULL this means we have an unknown hard error whose format
740 * string is in FormatU.
743 /* Wrap in SEH to protect from invalid string parameters */
748 /* Fall back to unknown hard error format string, and use the original parameters */
749 cszBuffer
= _scwprintf(pszUnknownHardError
,
751 Parameters
[0], Parameters
[1],
752 Parameters
[2], Parameters
[3]);
753 cszBuffer
*= sizeof(WCHAR
);
757 cszBuffer
= _scwprintf(FormatString
,
758 CopyParameters
[0], CopyParameters
[1],
759 CopyParameters
[2], CopyParameters
[3]);
760 cszBuffer
*= sizeof(WCHAR
);
762 /* Add a description for the dialog buttons */
763 if (Message
->Status
== STATUS_UNHANDLED_EXCEPTION
)
765 if (Message
->ValidResponseOptions
== OptionOk
||
766 Message
->ValidResponseOptions
== OptionOkCancel
)
768 /* Reserve space for one newline and the OK-terminate-program string */
769 cszBuffer
+= sizeof(WCHAR
) + g_OKTerminateU
.Length
;
771 if (Message
->ValidResponseOptions
== OptionOkCancel
)
773 /* Reserve space for one newline and the CANCEL-debug-program string */
774 cszBuffer
+= sizeof(WCHAR
) + g_CancelDebugU
.Length
;
779 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
781 /* An exception occurred, use a default string with the original parameters */
782 cszBuffer
= _scwprintf(pszExceptionHardError
,
784 Parameters
[0], Parameters
[1],
785 Parameters
[2], Parameters
[3]);
786 cszBuffer
*= sizeof(WCHAR
);
790 cszBuffer
+= SizeOfStrings
+ sizeof(UNICODE_NULL
);
792 if (TextStringU
->MaximumLength
< cszBuffer
)
794 /* Allocate a larger buffer for the text message */
795 pszBuffer
= RtlAllocateHeap(RtlGetProcessHeap(),
800 /* We could not allocate a larger buffer; continue using the smaller original buffer */
801 DPRINT1("Cannot allocate memory for TextStringU, use original buffer.\n");
805 RtlInitEmptyUnicodeString(TextStringU
, pszBuffer
, (USHORT
)cszBuffer
);
808 TextStringU
->Length
= 0;
809 TextStringU
->Buffer
[0] = UNICODE_NULL
;
811 /* Wrap in SEH to protect from invalid string parameters */
814 /* Print the string into the buffer */
815 pszBuffer
= TextStringU
->Buffer
;
816 cszBuffer
= TextStringU
->MaximumLength
;
820 /* Fall back to unknown hard error format string, and use the original parameters */
821 RtlStringCbPrintfW(pszBuffer
, cszBuffer
,
824 Parameters
[0], Parameters
[1],
825 Parameters
[2], Parameters
[3]);
829 RtlStringCbPrintfExW(pszBuffer
, cszBuffer
,
830 &pszBuffer
, &cszBuffer
,
833 CopyParameters
[0], CopyParameters
[1],
834 CopyParameters
[2], CopyParameters
[3]);
836 /* Add a description for the dialog buttons */
837 if (Message
->Status
== STATUS_UNHANDLED_EXCEPTION
)
839 if (Message
->ValidResponseOptions
== OptionOk
||
840 Message
->ValidResponseOptions
== OptionOkCancel
)
842 RtlStringCbPrintfExW(pszBuffer
, cszBuffer
,
843 &pszBuffer
, &cszBuffer
,
848 if (Message
->ValidResponseOptions
== OptionOkCancel
)
850 RtlStringCbPrintfExW(pszBuffer
, cszBuffer
,
851 &pszBuffer
, &cszBuffer
,
859 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
861 /* An exception occurred, use a default string with the original parameters */
862 DPRINT1("Exception 0x%08lx occurred while building hard-error message, fall back to default message.\n",
863 _SEH2_GetExceptionCode());
865 RtlStringCbPrintfW(TextStringU
->Buffer
,
866 TextStringU
->MaximumLength
,
867 pszExceptionHardError
,
869 Parameters
[0], Parameters
[1],
870 Parameters
[2], Parameters
[3]);
874 TextStringU
->Length
= (USHORT
)(wcslen(TextStringU
->Buffer
) * sizeof(WCHAR
));
876 /* Free the converted UNICODE strings */
877 if (Format2A
.Buffer
) RtlFreeUnicodeString(&Format2U
);
878 if (FormatA
.Buffer
) RtlFreeUnicodeString(&FormatU
);
881 /* Free the captured parameters */
882 UserpFreeStringParameters(Parameters
, Message
);
889 IN ULONG DefaultValue
)
892 ULONG Value
= DefaultValue
;
893 UNICODE_STRING String
;
894 OBJECT_ATTRIBUTES ObjectAttributes
;
897 UCHAR ValueBuffer
[sizeof(KEY_VALUE_PARTIAL_INFORMATION
) + sizeof(ULONG
)];
898 PKEY_VALUE_PARTIAL_INFORMATION ValueInfo
= (PKEY_VALUE_PARTIAL_INFORMATION
)ValueBuffer
;
900 RtlInitUnicodeString(&String
, KeyName
);
901 InitializeObjectAttributes(&ObjectAttributes
,
903 OBJ_CASE_INSENSITIVE
,
907 /* Open the registry key */
908 Status
= NtOpenKey(&KeyHandle
, KEY_READ
, &ObjectAttributes
);
909 if (NT_SUCCESS(Status
))
911 /* Query the value */
912 RtlInitUnicodeString(&String
, ValueName
);
913 Status
= NtQueryValueKey(KeyHandle
,
915 KeyValuePartialInformation
,
920 /* Close the registry key */
923 if (NT_SUCCESS(Status
) && (ValueInfo
->Type
== REG_DWORD
))
925 /* Directly retrieve the data */
926 Value
= *(PULONG
)ValueInfo
->Data
;
934 UserpShowInformationBalloon(
935 IN PUNICODE_STRING TextStringU
,
936 IN PUNICODE_STRING CaptionStringU
,
938 IN PHARDERROR_MSG Message
)
940 ULONG ShellErrorMode
;
942 COPYDATASTRUCT CopyData
;
943 PBALLOON_HARD_ERROR_DATA pdata
;
944 DWORD dwSize
, cbTextLen
, cbTitleLen
;
945 PWCHAR pText
, pCaption
;
949 /* Query the shell error mode value */
950 ShellErrorMode
= GetRegInt(L
"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Windows",
951 L
"ShellErrorMode", 0);
953 /* Make the shell display the hard error message only if necessary */
954 if (ShellErrorMode
!= 1)
957 /* Retrieve the shell task window */
958 hWndTaskman
= GetTaskmanWindow();
961 DPRINT1("Failed to find shell task window (last error %lu)\n", GetLastError());
965 cbTextLen
= TextStringU
->Length
+ sizeof(UNICODE_NULL
);
966 cbTitleLen
= CaptionStringU
->Length
+ sizeof(UNICODE_NULL
);
968 dwSize
= sizeof(BALLOON_HARD_ERROR_DATA
);
969 dwSize
+= cbTextLen
+ cbTitleLen
;
971 /* Build the data buffer */
972 pdata
= RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, dwSize
);
975 DPRINT1("Failed to allocate balloon data\n");
979 pdata
->cbHeaderSize
= sizeof(BALLOON_HARD_ERROR_DATA
);
980 pdata
->Status
= Message
->Status
;
981 pdata
->dwType
= Type
;
983 pdata
->TitleOffset
= pdata
->cbHeaderSize
;
984 pdata
->MessageOffset
= pdata
->TitleOffset
+ cbTitleLen
;
985 pCaption
= (PWCHAR
)((ULONG_PTR
)pdata
+ pdata
->TitleOffset
);
986 pText
= (PWCHAR
)((ULONG_PTR
)pdata
+ pdata
->MessageOffset
);
987 RtlStringCbCopyNW(pCaption
, cbTitleLen
, CaptionStringU
->Buffer
, CaptionStringU
->Length
);
988 RtlStringCbCopyNW(pText
, cbTextLen
, TextStringU
->Buffer
, TextStringU
->Length
);
990 /* Send the message */
992 /* Retrieve a unique system-wide message to communicate hard error data with the shell */
993 CopyData
.dwData
= RegisterWindowMessageW(L
"HardError");
994 CopyData
.cbData
= dwSize
;
995 CopyData
.lpData
= pdata
;
998 ret
= SendMessageTimeoutW(hWndTaskman
, WM_COPYDATA
, 0, (LPARAM
)&CopyData
,
999 SMTO_NORMAL
| SMTO_ABORTIFHUNG
, 3000, &dwResult
);
1001 /* Free the buffer */
1002 RtlFreeHeap(RtlGetProcessHeap(), 0, pdata
);
1004 return (ret
&& dwResult
) ? TRUE
: FALSE
;
1010 IN PUNICODE_STRING TextStringU
,
1011 IN PUNICODE_STRING CaptionStringU
,
1015 ULONG MessageBoxResponse
;
1017 DPRINT("Text = '%S', Caption = '%S', Type = 0x%lx\n",
1018 TextStringU
->Buffer
, CaptionStringU
->Buffer
, Type
);
1020 /* Display a message box */
1021 MessageBoxResponse
= MessageBoxTimeoutW(NULL
,
1022 TextStringU
->Buffer
,
1023 CaptionStringU
->Buffer
,
1026 /* Return response value */
1027 switch (MessageBoxResponse
)
1029 case IDOK
: return ResponseOk
;
1030 case IDCANCEL
: return ResponseCancel
;
1031 case IDYES
: return ResponseYes
;
1032 case IDNO
: return ResponseNo
;
1033 case IDABORT
: return ResponseAbort
;
1034 case IDIGNORE
: return ResponseIgnore
;
1035 case IDRETRY
: return ResponseRetry
;
1036 case IDTRYAGAIN
: return ResponseTryAgain
;
1037 case IDCONTINUE
: return ResponseContinue
;
1038 default: return ResponseNotHandled
;
1041 return ResponseNotHandled
;
1047 IN PUNICODE_STRING TextStringU
,
1048 IN PUNICODE_STRING CaptionStringU
)
1052 UNICODE_STRING UNCServerNameU
= {0, 0, NULL
};
1053 UNICODE_STRING SourceNameU
= RTL_CONSTANT_STRING(L
"Application Popup");
1054 PUNICODE_STRING Strings
[] = {CaptionStringU
, TextStringU
};
1056 Status
= ElfRegisterEventSourceW(&UNCServerNameU
, &SourceNameU
, &hEventLog
);
1057 if (!NT_SUCCESS(Status
) || !hEventLog
)
1059 DPRINT1("ElfRegisterEventSourceW failed with Status 0x%08lx\n", Status
);
1063 Status
= ElfReportEventW(hEventLog
,
1064 EVENTLOG_INFORMATION_TYPE
,
1066 STATUS_LOG_HARD_ERROR
,
1075 if (!NT_SUCCESS(Status
))
1076 DPRINT1("ElfReportEventW failed with Status 0x%08lx\n", Status
);
1078 ElfDeregisterEventSource(hEventLog
);
1083 UserServerHardError(
1084 IN PCSR_THREAD ThreadData
,
1085 IN PHARDERROR_MSG Message
)
1089 ULONG Timeout
= INFINITE
;
1090 UNICODE_STRING TextU
, CaptionU
;
1091 WCHAR LocalTextBuffer
[256];
1092 WCHAR LocalCaptionBuffer
[256];
1094 ASSERT(ThreadData
->Process
!= NULL
);
1096 /* Default to not handled */
1097 Message
->Response
= ResponseNotHandled
;
1099 /* Make sure we don't have too many parameters */
1100 if (Message
->NumberOfParameters
> MAXIMUM_HARDERROR_PARAMETERS
)
1102 // NOTE: Windows just fails (STATUS_INVALID_PARAMETER) & returns ResponseNotHandled.
1103 DPRINT1("Invalid NumberOfParameters = %d\n", Message
->NumberOfParameters
);
1104 Message
->NumberOfParameters
= MAXIMUM_HARDERROR_PARAMETERS
;
1106 if (Message
->ValidResponseOptions
> OptionCancelTryContinue
)
1108 DPRINT1("Unknown ValidResponseOptions = %d\n", Message
->ValidResponseOptions
);
1109 return; // STATUS_INVALID_PARAMETER;
1111 if (Message
->Status
== STATUS_SERVICE_NOTIFICATION
)
1113 if (Message
->NumberOfParameters
< 3)
1115 DPRINT1("Invalid NumberOfParameters = %d for STATUS_SERVICE_NOTIFICATION\n",
1116 Message
->NumberOfParameters
);
1117 return; // STATUS_INVALID_PARAMETER;
1119 // (Message->UnicodeStringParameterMask & 0x3)
1122 /* Re-initialize the hard errors cache */
1123 UserInitHardErrorsCache();
1125 /* Format the message caption and text */
1126 RtlInitEmptyUnicodeString(&TextU
, LocalTextBuffer
, sizeof(LocalTextBuffer
));
1127 RtlInitEmptyUnicodeString(&CaptionU
, LocalCaptionBuffer
, sizeof(LocalCaptionBuffer
));
1128 UserpFormatMessages(&TextU
, &CaptionU
, &dwType
, &Timeout
, Message
);
1130 /* Log the hard error message */
1131 UserpLogHardError(&TextU
, &CaptionU
);
1133 /* Display a hard error popup depending on the current ErrorMode */
1135 /* Query the error mode value */
1136 ErrorMode
= GetRegInt(L
"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Windows",
1139 if (Message
->Status
!= STATUS_SERVICE_NOTIFICATION
&& ErrorMode
!= 0)
1141 /* Returns OK for the hard error */
1142 Message
->Response
= ResponseOk
;
1146 if (Message
->ValidResponseOptions
== OptionOkNoWait
)
1148 /* Display the balloon */
1149 Message
->Response
= ResponseOk
;
1150 if (UserpShowInformationBalloon(&TextU
,
1155 Message
->Response
= ResponseOk
;
1160 /* Display the message box */
1161 Message
->Response
= UserpMessageBox(&TextU
,
1167 /* Free the strings if they have been reallocated */
1168 if (TextU
.Buffer
!= LocalTextBuffer
)
1169 RtlFreeUnicodeString(&TextU
);
1170 if (CaptionU
.Buffer
!= LocalCaptionBuffer
)
1171 RtlFreeUnicodeString(&CaptionU
);
1177 UserInitHardErrorsCache(VOID
)
1180 LCID CurrentUserLCID
= 0;
1182 Status
= NtQueryDefaultLocale(TRUE
, &CurrentUserLCID
);
1183 if (!NT_SUCCESS(Status
) || CurrentUserLCID
== 0)
1185 /* Fall back to english locale */
1186 DPRINT1("NtQueryDefaultLocale failed with Status = 0x%08lx\n", Status
);
1187 // LOCALE_SYSTEM_DEFAULT;
1188 CurrentUserLCID
= MAKELCID(MAKELANGID(LANG_ENGLISH
, SUBLANG_ENGLISH_US
), SORT_DEFAULT
);
1190 if (g_CurrentUserLangId
== LANGIDFROMLCID(CurrentUserLCID
))
1192 /* The current lang ID and the hard error strings have already been cached */
1196 /* Load the strings using the current system locale */
1197 RtlLoadUnicodeString(UserServerDllInstance
, IDS_SEVERITY_SUCCESS
,
1198 &g_SuccessU
, L
"Success");
1199 RtlLoadUnicodeString(UserServerDllInstance
, IDS_SEVERITY_INFORMATIONAL
,
1200 &g_InformationU
, L
"System Information");
1201 RtlLoadUnicodeString(UserServerDllInstance
, IDS_SEVERITY_WARNING
,
1202 &g_WarningU
, L
"System Warning");
1203 RtlLoadUnicodeString(UserServerDllInstance
, IDS_SEVERITY_ERROR
,
1204 &g_ErrorU
, L
"System Error");
1205 // "unknown software exception"
1206 RtlLoadUnicodeString(UserServerDllInstance
, IDS_SYSTEM_PROCESS
,
1207 &g_SystemProcessU
, L
"System Process");
1208 RtlLoadUnicodeString(UserServerDllInstance
, IDS_OK_TERMINATE_PROGRAM
,
1209 &g_OKTerminateU
, L
"Click on OK to terminate the program.");
1210 RtlLoadUnicodeString(UserServerDllInstance
, IDS_CANCEL_DEBUG_PROGRAM
,
1211 &g_CancelDebugU
, L
"Click on CANCEL to debug the program.");
1213 /* Remember that we cached the hard error strings */
1214 g_CurrentUserLangId
= LANGIDFROMLCID(CurrentUserLCID
);