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
;
78 UserpCaptureStringParameters(
79 OUT PULONG_PTR Parameters
,
80 OUT PULONG SizeOfAllUnicodeStrings
,
81 IN PHARDERROR_MSG Message
,
82 IN HANDLE hProcess OPTIONAL
)
85 ULONG nParam
, Size
= 0;
86 UNICODE_STRING TempStringU
, ParamStringU
;
87 ANSI_STRING TempStringA
;
89 if (SizeOfAllUnicodeStrings
)
90 *SizeOfAllUnicodeStrings
= 0;
92 /* Read all strings from client space */
93 for (nParam
= 0; nParam
< Message
->NumberOfParameters
; ++nParam
)
95 Parameters
[nParam
] = 0;
97 /* Check if the current parameter is a unicode string */
98 if (Message
->UnicodeStringParameterMask
& (1 << nParam
))
100 /* Skip this string if we do not have a client process */
104 /* Read the UNICODE_STRING from the process memory */
105 Status
= NtReadVirtualMemory(hProcess
,
106 (PVOID
)Message
->Parameters
[nParam
],
108 sizeof(ParamStringU
),
110 if (!NT_SUCCESS(Status
))
112 /* We failed, skip this string */
113 DPRINT1("NtReadVirtualMemory(Message->Parameters) failed, Status 0x%lx, skipping.\n", Status
);
117 /* Allocate a buffer for the string and reserve a NULL terminator */
118 TempStringU
.MaximumLength
= ParamStringU
.Length
+ sizeof(UNICODE_NULL
);
119 TempStringU
.Length
= ParamStringU
.Length
;
120 TempStringU
.Buffer
= RtlAllocateHeap(RtlGetProcessHeap(),
122 TempStringU
.MaximumLength
);
123 if (!TempStringU
.Buffer
)
125 /* We failed, skip this string */
126 DPRINT1("Cannot allocate memory with size %u, skipping.\n", TempStringU
.MaximumLength
);
130 /* Read the string buffer from the process memory */
131 Status
= NtReadVirtualMemory(hProcess
,
136 if (!NT_SUCCESS(Status
))
138 /* We failed, skip this string */
139 DPRINT1("NtReadVirtualMemory(ParamStringU) failed, Status 0x%lx, skipping.\n", Status
);
140 RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringU
.Buffer
);
143 /* NULL-terminate the string */
144 TempStringU
.Buffer
[TempStringU
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
146 DPRINT("ParamString = \'%wZ\'\n", &TempStringU
);
148 if (Message
->Status
== STATUS_SERVICE_NOTIFICATION
)
150 /* Just keep the allocated NULL-terminated UNICODE string */
151 Parameters
[nParam
] = (ULONG_PTR
)TempStringU
.Buffer
;
152 Size
+= TempStringU
.Length
;
156 /* Allocate a buffer for converted to ANSI string */
157 TempStringA
.MaximumLength
= RtlUnicodeStringToAnsiSize(&TempStringU
);
158 TempStringA
.Buffer
= RtlAllocateHeap(RtlGetProcessHeap(),
160 TempStringA
.MaximumLength
);
161 if (!TempStringA
.Buffer
)
163 /* We failed, skip this string */
164 DPRINT1("Cannot allocate memory with size %u, skipping.\n", TempStringA
.MaximumLength
);
165 RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringU
.Buffer
);
169 /* Convert string to ANSI and free temporary buffer */
170 Status
= RtlUnicodeStringToAnsiString(&TempStringA
, &TempStringU
, FALSE
);
171 RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringU
.Buffer
);
172 if (!NT_SUCCESS(Status
))
174 /* We failed, skip this string */
175 DPRINT1("RtlUnicodeStringToAnsiString() failed, Status 0x%lx, skipping.\n", Status
);
176 RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringA
.Buffer
);
180 /* Note: RtlUnicodeStringToAnsiString() returns a NULL-terminated string */
181 Parameters
[nParam
] = (ULONG_PTR
)TempStringA
.Buffer
;
182 Size
+= TempStringU
.Length
;
187 /* It's not a unicode string, just copy the parameter */
188 Parameters
[nParam
] = Message
->Parameters
[nParam
];
192 if (SizeOfAllUnicodeStrings
)
193 *SizeOfAllUnicodeStrings
= Size
;
198 UserpFreeStringParameters(
199 IN OUT PULONG_PTR Parameters
,
200 IN PHARDERROR_MSG Message
)
204 /* Loop all parameters */
205 for (nParam
= 0; nParam
< Message
->NumberOfParameters
; ++nParam
)
207 /* Check if the current parameter is a string */
208 if ((Message
->UnicodeStringParameterMask
& (1 << nParam
)) && (Parameters
[nParam
] != 0))
210 /* Free the string buffer */
211 RtlFreeHeap(RtlGetProcessHeap(), 0, (PVOID
)Parameters
[nParam
]);
218 UserpGetClientFileName(
219 OUT PUNICODE_STRING ClientFileNameU
,
222 PLIST_ENTRY ModuleListHead
;
224 PLDR_DATA_TABLE_ENTRY Module
;
226 PROCESS_BASIC_INFORMATION ClientBasicInfo
;
227 LDR_DATA_TABLE_ENTRY ModuleData
;
232 /* Initialize string */
233 RtlInitEmptyUnicodeString(ClientFileNameU
, NULL
, 0);
235 /* Query process information */
236 Status
= NtQueryInformationProcess(hProcess
,
237 ProcessBasicInformation
,
239 sizeof(ClientBasicInfo
),
241 if (!NT_SUCCESS(Status
)) return Status
;
243 /* Locate the process loader data table and retrieve its name from it */
245 Peb
= ClientBasicInfo
.PebBaseAddress
;
246 if (!Peb
) return STATUS_UNSUCCESSFUL
;
248 Status
= NtReadVirtualMemory(hProcess
, &Peb
->Ldr
, &Ldr
, sizeof(Ldr
), NULL
);
249 if (!NT_SUCCESS(Status
)) return Status
;
251 ModuleListHead
= &Ldr
->InLoadOrderModuleList
;
252 Status
= NtReadVirtualMemory(hProcess
,
253 &ModuleListHead
->Flink
,
257 if (!NT_SUCCESS(Status
)) return Status
;
259 if (Entry
== ModuleListHead
) return STATUS_UNSUCCESSFUL
;
261 Module
= CONTAINING_RECORD(Entry
, LDR_DATA_TABLE_ENTRY
, InLoadOrderLinks
);
263 Status
= NtReadVirtualMemory(hProcess
,
268 if (!NT_SUCCESS(Status
)) return Status
;
270 Status
= NtReadVirtualMemory(hProcess
,
271 &Peb
->ImageBaseAddress
,
273 sizeof(ClientDllBase
),
275 if (!NT_SUCCESS(Status
)) return Status
;
277 if (ClientDllBase
!= ModuleData
.DllBase
) return STATUS_UNSUCCESSFUL
;
279 ClientFileNameU
->MaximumLength
= ModuleData
.BaseDllName
.MaximumLength
;
280 ClientFileNameU
->Buffer
= RtlAllocateHeap(RtlGetProcessHeap(),
282 ClientFileNameU
->MaximumLength
);
283 if (!ClientFileNameU
->Buffer
)
285 RtlInitEmptyUnicodeString(ClientFileNameU
, NULL
, 0);
286 return STATUS_NO_MEMORY
;
289 Status
= NtReadVirtualMemory(hProcess
,
290 ModuleData
.BaseDllName
.Buffer
,
291 ClientFileNameU
->Buffer
,
292 ClientFileNameU
->MaximumLength
,
294 if (!NT_SUCCESS(Status
))
296 RtlFreeHeap(RtlGetProcessHeap(), 0, ClientFileNameU
->Buffer
);
297 RtlInitEmptyUnicodeString(ClientFileNameU
, NULL
, 0);
301 ClientFileNameU
->Length
= (USHORT
)(wcslen(ClientFileNameU
->Buffer
) * sizeof(WCHAR
));
302 DPRINT("ClientFileNameU = \'%wZ\'\n", &ClientFileNameU
);
304 return STATUS_SUCCESS
;
309 UserpDuplicateParamStringToUnicodeString(
310 IN OUT PUNICODE_STRING UnicodeString
,
311 IN PCWSTR ParamString
)
313 UNICODE_STRING FormatU
, TempStringU
;
315 /* Calculate buffer length for the text message */
316 RtlInitUnicodeString(&FormatU
, (PWSTR
)ParamString
);
317 if (UnicodeString
->MaximumLength
< FormatU
.MaximumLength
)
319 /* Duplicate the text message in a larger buffer */
320 if (NT_SUCCESS(RtlDuplicateUnicodeString(RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE
,
321 &FormatU
, &TempStringU
)))
323 *UnicodeString
= TempStringU
;
327 /* We could not allocate a larger buffer; continue using the smaller original buffer */
328 DPRINT1("Cannot allocate memory for UnicodeString, use original buffer.\n");
330 /* Copy the truncated string, NULL-terminate it */
331 FormatU
.MaximumLength
= UnicodeString
->MaximumLength
;
332 FormatU
.Length
= FormatU
.MaximumLength
- sizeof(UNICODE_NULL
);
333 RtlCopyUnicodeString(UnicodeString
, &FormatU
);
338 /* Copy the string, NULL-terminate it */
339 RtlCopyUnicodeString(UnicodeString
, &FormatU
);
346 IN OUT PUNICODE_STRING TextStringU
,
347 IN OUT PUNICODE_STRING CaptionStringU
,
349 IN PHARDERROR_MSG Message
)
352 OBJECT_ATTRIBUTES ObjectAttributes
;
355 ULONG_PTR Parameters
[MAXIMUM_HARDERROR_PARAMETERS
] = {0};
356 ULONG_PTR CopyParameters
[MAXIMUM_HARDERROR_PARAMETERS
];
357 UNICODE_STRING WindowTitleU
, FileNameU
, TempStringU
, FormatU
, Format2U
;
358 ANSI_STRING FormatA
, Format2A
;
360 PMESSAGE_RESOURCE_ENTRY MessageResource
;
361 PWSTR FormatString
, pszBuffer
;
363 ULONG Severity
= (ULONG
)(Message
->Status
) >> 30;
366 /* Open client process */
367 InitializeObjectAttributes(&ObjectAttributes
, NULL
, 0, NULL
, NULL
);
368 Status
= NtOpenProcess(&hProcess
,
369 PROCESS_VM_READ
| PROCESS_QUERY_INFORMATION
,
371 &Message
->h
.ClientId
);
372 if (!NT_SUCCESS(Status
))
374 DPRINT1("NtOpenProcess failed with status 0x%08lx, possibly SYSTEM process.\n", Status
);
378 /* Capture all string parameters from the process memory */
379 UserpCaptureStringParameters(Parameters
, &SizeOfStrings
, Message
, hProcess
);
382 * Check whether it is a service notification, in which case
383 * we format the parameters and take the short route.
385 if (Message
->Status
== STATUS_SERVICE_NOTIFICATION
)
387 /* Close the process handle */
388 if (hProcess
) NtClose(hProcess
);
391 * Retrieve the message box flags. Note that we filter out
392 * MB_SERVICE_NOTIFICATION to not enter an infinite recursive
393 * loop when we will call MessageBox() later on.
395 *pdwType
= (UINT
)Parameters
[2] & ~MB_SERVICE_NOTIFICATION
;
397 /* Duplicate the UNICODE text message */
398 if (Message
->UnicodeStringParameterMask
& 0x1)
400 /* A string has been provided: duplicate it */
401 UserpDuplicateParamStringToUnicodeString(TextStringU
, (PCWSTR
)Parameters
[0]);
405 /* No string (or invalid one) has been provided: keep the original buffer and reset the string length to zero */
406 TextStringU
->Length
= 0;
409 /* Duplicate the UNICODE caption */
410 if (Message
->UnicodeStringParameterMask
& 0x2)
412 /* A string has been provided: duplicate it */
413 UserpDuplicateParamStringToUnicodeString(CaptionStringU
, (PCWSTR
)Parameters
[1]);
417 /* No string (or invalid one) has been provided: keep the original buffer and reset the string length to zero */
418 CaptionStringU
->Length
= 0;
424 /* Set the message box type */
426 switch (Message
->ValidResponseOptions
)
428 case OptionAbortRetryIgnore
:
429 *pdwType
= MB_ABORTRETRYIGNORE
;
435 *pdwType
= MB_OKCANCEL
;
437 case OptionRetryCancel
:
438 *pdwType
= MB_RETRYCANCEL
;
443 case OptionYesNoCancel
:
444 *pdwType
= MB_YESNOCANCEL
;
446 case OptionShutdownSystem
:
452 case OptionCancelTryContinue
:
453 *pdwType
= MB_CANCELTRYCONTINUE
;
457 /* Set the severity icon */
458 // STATUS_SEVERITY_SUCCESS
459 if (Severity
== STATUS_SEVERITY_INFORMATIONAL
) *pdwType
|= MB_ICONINFORMATION
;
460 else if (Severity
== STATUS_SEVERITY_WARNING
) *pdwType
|= MB_ICONWARNING
;
461 else if (Severity
== STATUS_SEVERITY_ERROR
) *pdwType
|= MB_ICONERROR
;
463 *pdwType
|= MB_SYSTEMMODAL
| MB_SETFOREGROUND
;
465 /* Copy the Parameters array locally */
466 RtlCopyMemory(&CopyParameters
, Parameters
, sizeof(CopyParameters
));
468 /* Get the file name of the client process */
469 Status
= STATUS_SUCCESS
;
471 Status
= UserpGetClientFileName(&FileNameU
, hProcess
);
473 /* Close the process handle but keep its original value to know where stuff came from */
474 if (hProcess
) NtClose(hProcess
);
477 * Fall back to SYSTEM process if the client process handle
478 * was NULL or we failed retrieving a file name.
480 if (!hProcess
|| !NT_SUCCESS(Status
) || !FileNameU
.Buffer
)
483 FileNameU
= g_SystemProcessU
;
486 /* Get text string of the error code */
487 Status
= RtlFindMessage(GetModuleHandleW(L
"ntdll"),
488 (ULONG_PTR
)RT_MESSAGETABLE
,
492 if (NT_SUCCESS(Status
))
494 if (MessageResource
->Flags
)
496 RtlInitUnicodeString(&FormatU
, (PWSTR
)MessageResource
->Text
);
497 FormatA
.Buffer
= NULL
;
501 RtlInitAnsiString(&FormatA
, (PSTR
)MessageResource
->Text
);
502 /* Status = */ RtlAnsiStringToUnicodeString(&FormatU
, &FormatA
, TRUE
);
508 * Fall back to hardcoded value.
509 * NOTE: The value used here is ReactOS-specific: it allows specifying
510 * the exact hard error status value and the parameters. The version
511 * used on Windows only says "Unknown Hard Error".
514 RtlInitUnicodeString(&FormatU
, L
"Unknown Hard Error 0x%08lx\n"
515 L
"Parameters: 0x%p 0x%p 0x%p 0x%p");
517 RtlInitUnicodeString(&FormatU
, L
"Unknown Hard Error 0x%08lx");
518 CopyParameters
[0] = Message
->Status
;
520 FormatA
.Buffer
= NULL
;
523 FormatString
= FormatU
.Buffer
;
525 /* Check whether a caption exists */
526 if (FormatString
[0] == L
'{')
528 /* Set caption start */
529 TempStringU
.Buffer
= ++FormatString
;
531 /* Get size of the caption */
532 for (Size
= 0; *FormatString
!= UNICODE_NULL
&& *FormatString
!= L
'}'; Size
++)
535 /* Skip '}', '\r', '\n' */
538 TempStringU
.Length
= (USHORT
)(Size
* sizeof(WCHAR
));
539 TempStringU
.MaximumLength
= TempStringU
.Length
;
543 if (Severity
== STATUS_SEVERITY_SUCCESS
)
544 TempStringU
= g_SuccessU
;
545 else if (Severity
== STATUS_SEVERITY_INFORMATIONAL
)
546 TempStringU
= g_InformationU
;
547 else if (Severity
== STATUS_SEVERITY_WARNING
)
548 TempStringU
= g_WarningU
;
549 else if (Severity
== STATUS_SEVERITY_ERROR
)
550 TempStringU
= g_ErrorU
;
552 ASSERT(FALSE
); // Unexpected, since Severity is only <= 3.
555 /* Retrieve the window title of the client, if it has one */
556 RtlInitEmptyUnicodeString(&WindowTitleU
, NULL
, 0);
558 EnumThreadWindows(HandleToUlong(Message
->h
.ClientId
.UniqueThread
),
559 FindTopLevelWnd
, (LPARAM
)&hwndOwner
);
562 cszBuffer
= GetWindowTextLengthW(hwndOwner
);
565 cszBuffer
+= 3; // 2 characters for ": " and a NULL terminator.
566 WindowTitleU
.MaximumLength
= (USHORT
)(cszBuffer
* sizeof(WCHAR
));
567 WindowTitleU
.Buffer
= RtlAllocateHeap(RtlGetProcessHeap(),
569 WindowTitleU
.MaximumLength
);
570 if (WindowTitleU
.Buffer
)
572 cszBuffer
= GetWindowTextW(hwndOwner
,
574 WindowTitleU
.MaximumLength
/ sizeof(WCHAR
));
575 WindowTitleU
.Length
= (USHORT
)(cszBuffer
* sizeof(WCHAR
));
576 RtlAppendUnicodeToString(&WindowTitleU
, L
": ");
580 RtlInitEmptyUnicodeString(&WindowTitleU
, NULL
, 0);
585 /* Calculate buffer length for the caption */
586 cszBuffer
= WindowTitleU
.Length
+ FileNameU
.Length
+ TempStringU
.Length
+
587 3 * sizeof(WCHAR
) + sizeof(UNICODE_NULL
);
588 if (CaptionStringU
->MaximumLength
< cszBuffer
)
590 /* Allocate a larger buffer for the caption */
591 pszBuffer
= RtlAllocateHeap(RtlGetProcessHeap(),
596 /* We could not allocate a larger buffer; continue using the smaller original buffer */
597 DPRINT1("Cannot allocate memory for CaptionStringU, use original buffer.\n");
601 RtlInitEmptyUnicodeString(CaptionStringU
, pszBuffer
, (USHORT
)cszBuffer
);
604 CaptionStringU
->Length
= 0;
606 /* Append the file name, the separator and the caption text */
607 RtlStringCbPrintfW(CaptionStringU
->Buffer
,
608 CaptionStringU
->MaximumLength
,
610 &WindowTitleU
, &FileNameU
, &TempStringU
);
611 CaptionStringU
->Length
= (USHORT
)(wcslen(CaptionStringU
->Buffer
) * sizeof(WCHAR
));
613 /* Free string buffers if needed */
614 if (WindowTitleU
.Buffer
) RtlFreeUnicodeString(&WindowTitleU
);
615 if (hProcess
) RtlFreeUnicodeString(&FileNameU
);
617 // FIXME: What is 42 == ??
620 /* Check if this is an exception message */
621 if (Message
->Status
== STATUS_UNHANDLED_EXCEPTION
)
623 ULONG ExceptionCode
= CopyParameters
[0];
625 /* Get text string of the exception code */
626 Status
= RtlFindMessage(GetModuleHandleW(L
"ntdll"),
627 (ULONG_PTR
)RT_MESSAGETABLE
,
631 if (NT_SUCCESS(Status
))
633 if (MessageResource
->Flags
)
635 RtlInitUnicodeString(&Format2U
, (PWSTR
)MessageResource
->Text
);
636 Format2A
.Buffer
= NULL
;
640 RtlInitAnsiString(&Format2A
, (PSTR
)MessageResource
->Text
);
641 /* Status = */ RtlAnsiStringToUnicodeString(&Format2U
, &Format2A
, TRUE
);
644 /* Handle special cases */
645 if (ExceptionCode
== STATUS_ACCESS_VIOLATION
)
647 FormatString
= Format2U
.Buffer
;
648 CopyParameters
[0] = CopyParameters
[1];
649 CopyParameters
[1] = CopyParameters
[3];
650 if (CopyParameters
[2])
651 CopyParameters
[2] = (ULONG_PTR
)L
"written";
653 CopyParameters
[2] = (ULONG_PTR
)L
"read";
655 else if (ExceptionCode
== STATUS_IN_PAGE_ERROR
)
657 FormatString
= Format2U
.Buffer
;
658 CopyParameters
[0] = CopyParameters
[1];
659 CopyParameters
[1] = CopyParameters
[3];
663 /* Keep the existing FormatString */
664 CopyParameters
[2] = CopyParameters
[1];
665 CopyParameters
[1] = CopyParameters
[0];
667 pszBuffer
= Format2U
.Buffer
;
668 if (!_wcsnicmp(pszBuffer
, L
"{EXCEPTION}", 11))
671 * This is a named exception. Skip the mark and
672 * retrieve the exception name that follows it.
676 /* Skip '\r', '\n' */
679 CopyParameters
[0] = (ULONG_PTR
)pszBuffer
;
683 /* Fall back to hardcoded value */
684 CopyParameters
[0] = (ULONG_PTR
)L
"unknown software exception";
690 /* Fall back to hardcoded value, and keep the existing FormatString */
691 CopyParameters
[2] = CopyParameters
[1];
692 CopyParameters
[1] = CopyParameters
[0];
693 CopyParameters
[0] = (ULONG_PTR
)L
"unknown software exception";
696 /* Add explanation text for dialog buttons */
697 if (Message
->ValidResponseOptions
== OptionOk
||
698 Message
->ValidResponseOptions
== OptionOkCancel
)
700 /* Reserve space for one newline and the OK-terminate-program string */
701 Size
+= 1 + (g_OKTerminateU
.Length
/ sizeof(WCHAR
));
703 if (Message
->ValidResponseOptions
== OptionOkCancel
)
705 /* Reserve space for one newline and the CANCEL-debug-program string */
706 Size
+= 1 + (g_CancelDebugU
.Length
/ sizeof(WCHAR
));
710 /* Calculate buffer length for the text message */
711 cszBuffer
= FormatU
.Length
+ SizeOfStrings
+ Size
* sizeof(WCHAR
) +
712 sizeof(UNICODE_NULL
);
713 if (TextStringU
->MaximumLength
< cszBuffer
)
715 /* Allocate a larger buffer for the text message */
716 pszBuffer
= RtlAllocateHeap(RtlGetProcessHeap(),
721 /* We could not allocate a larger buffer; continue using the smaller original buffer */
722 DPRINT1("Cannot allocate memory for TextStringU, use original buffer.\n");
726 RtlInitEmptyUnicodeString(TextStringU
, pszBuffer
, (USHORT
)cszBuffer
);
729 TextStringU
->Length
= 0;
731 /* Wrap in SEH to protect from invalid string parameters */
734 /* Print the string into the buffer */
735 pszBuffer
= TextStringU
->Buffer
;
736 cszBuffer
= TextStringU
->MaximumLength
;
737 RtlStringCbPrintfExW(pszBuffer
, cszBuffer
,
738 &pszBuffer
, &cszBuffer
,
739 STRSAFE_IGNORE_NULLS
,
741 CopyParameters
[0], CopyParameters
[1],
742 CopyParameters
[2], CopyParameters
[3]);
744 /* Add explanation text for dialog buttons */
745 if (Message
->Status
== STATUS_UNHANDLED_EXCEPTION
)
747 if (Message
->ValidResponseOptions
== OptionOk
||
748 Message
->ValidResponseOptions
== OptionOkCancel
)
750 RtlStringCbPrintfExW(pszBuffer
, cszBuffer
,
751 &pszBuffer
, &cszBuffer
,
752 STRSAFE_IGNORE_NULLS
,
756 if (Message
->ValidResponseOptions
== OptionOkCancel
)
758 RtlStringCbPrintfExW(pszBuffer
, cszBuffer
,
759 &pszBuffer
, &cszBuffer
,
760 STRSAFE_IGNORE_NULLS
,
766 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
768 /* An exception occurred, use a default string with the original parameters */
769 DPRINT1("Exception 0x%08lx occurred while building hard-error message, fall back to default message.\n",
770 _SEH2_GetExceptionCode());
772 RtlStringCbPrintfW(TextStringU
->Buffer
,
773 TextStringU
->MaximumLength
,
774 L
"Exception processing message 0x%08lx\n"
775 L
"Parameters: 0x%p 0x%p 0x%p 0x%p",
777 Parameters
[0], Parameters
[1],
778 Parameters
[2], Parameters
[3]);
782 TextStringU
->Length
= (USHORT
)(wcslen(TextStringU
->Buffer
) * sizeof(WCHAR
));
784 /* Free converted Unicode strings */
785 if (Format2A
.Buffer
) RtlFreeUnicodeString(&Format2U
);
786 if (FormatA
.Buffer
) RtlFreeUnicodeString(&FormatU
);
790 UserpFreeStringParameters(Parameters
, Message
);
797 IN ULONG DefaultValue
)
800 ULONG Value
= DefaultValue
;
801 UNICODE_STRING String
;
802 OBJECT_ATTRIBUTES ObjectAttributes
;
805 UCHAR ValueBuffer
[sizeof(KEY_VALUE_PARTIAL_INFORMATION
) + sizeof(ULONG
)];
806 PKEY_VALUE_PARTIAL_INFORMATION ValueInfo
= (PKEY_VALUE_PARTIAL_INFORMATION
)ValueBuffer
;
808 RtlInitUnicodeString(&String
, KeyName
);
809 InitializeObjectAttributes(&ObjectAttributes
,
811 OBJ_CASE_INSENSITIVE
,
815 /* Open the registry key */
816 Status
= NtOpenKey(&KeyHandle
, KEY_READ
, &ObjectAttributes
);
817 if (NT_SUCCESS(Status
))
819 /* Query the value */
820 RtlInitUnicodeString(&String
, ValueName
);
821 Status
= NtQueryValueKey(KeyHandle
,
823 KeyValuePartialInformation
,
828 /* Close the registry key */
831 if (NT_SUCCESS(Status
) && (ValueInfo
->Type
== REG_DWORD
))
833 /* Directly retrieve the data */
834 Value
= *(PULONG
)ValueInfo
->Data
;
842 UserpShowInformationBalloon(
846 IN PHARDERROR_MSG Message
)
848 ULONG ShellErrorMode
;
850 COPYDATASTRUCT CopyData
;
851 PBALLOON_HARD_ERROR_DATA pdata
;
852 DWORD dwSize
, cbTextLen
, cbTitleLen
;
853 PWCHAR pText
, pCaption
;
856 /* Query the shell error mode value */
857 ShellErrorMode
= GetRegInt(L
"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Windows",
858 L
"ShellErrorMode", 0);
860 /* Make the shell display the hard error message in balloon only if necessary */
861 if (ShellErrorMode
!= 1)
864 hwnd
= GetTaskmanWindow();
867 DPRINT1("Failed to find shell task window (last error %lu)\n", GetLastError());
871 cbTextLen
= ((Text
? wcslen(Text
) : 0) + 1) * sizeof(WCHAR
);
872 cbTitleLen
= ((Caption
? wcslen(Caption
) : 0) + 1) * sizeof(WCHAR
);
874 dwSize
= sizeof(BALLOON_HARD_ERROR_DATA
);
875 dwSize
+= cbTextLen
+ cbTitleLen
;
877 pdata
= RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, dwSize
);
880 DPRINT1("Failed to allocate balloon data\n");
884 pdata
->cbHeaderSize
= sizeof(BALLOON_HARD_ERROR_DATA
);
885 pdata
->Status
= Message
->Status
;
887 if (NT_SUCCESS(Message
->Status
))
888 pdata
->dwType
= MB_OK
;
889 else if (Message
->Status
== STATUS_SERVICE_NOTIFICATION
)
890 pdata
->dwType
= Type
;
892 pdata
->dwType
= MB_ICONINFORMATION
;
894 pdata
->TitleOffset
= pdata
->cbHeaderSize
;
895 pdata
->MessageOffset
= pdata
->TitleOffset
;
896 pdata
->MessageOffset
+= cbTitleLen
;
897 pCaption
= (PWCHAR
)((ULONG_PTR
)pdata
+ pdata
->TitleOffset
);
898 pText
= (PWCHAR
)((ULONG_PTR
)pdata
+ pdata
->MessageOffset
);
899 wcscpy(pCaption
, Caption
);
902 CopyData
.dwData
= RegisterWindowMessageW(L
"HardError");
903 CopyData
.cbData
= dwSize
;
904 CopyData
.lpData
= pdata
;
908 ret
= SendMessageTimeoutW(hwnd
, WM_COPYDATA
, 0, (LPARAM
)&CopyData
,
909 SMTO_NORMAL
| SMTO_ABORTIFHUNG
, 3000, &dwResult
);
911 RtlFreeHeap(RtlGetProcessHeap(), 0, pdata
);
913 return (ret
&& dwResult
) ? TRUE
: FALSE
;
924 ULONG MessageBoxResponse
;
926 DPRINT("Text = '%S', Caption = '%S', Type = 0x%lx\n",
927 Text
, Caption
, Type
);
929 /* Display a message box */
930 MessageBoxResponse
= MessageBoxTimeoutW(NULL
, Text
, Caption
, Type
, 0, Timeout
);
932 /* Return response value */
933 switch (MessageBoxResponse
)
935 case IDOK
: return ResponseOk
;
936 case IDCANCEL
: return ResponseCancel
;
937 case IDYES
: return ResponseYes
;
938 case IDNO
: return ResponseNo
;
939 case IDABORT
: return ResponseAbort
;
940 case IDIGNORE
: return ResponseIgnore
;
941 case IDRETRY
: return ResponseRetry
;
942 case IDTRYAGAIN
: return ResponseTryAgain
;
943 case IDCONTINUE
: return ResponseContinue
;
944 default: return ResponseNotHandled
;
947 return ResponseNotHandled
;
953 IN PUNICODE_STRING TextStringU
,
954 IN PUNICODE_STRING CaptionStringU
)
958 UNICODE_STRING UNCServerNameU
= {0, 0, NULL
};
959 UNICODE_STRING SourceNameU
= RTL_CONSTANT_STRING(L
"Application Popup");
960 PUNICODE_STRING Strings
[] = {CaptionStringU
, TextStringU
};
962 Status
= ElfRegisterEventSourceW(&UNCServerNameU
, &SourceNameU
, &hEventLog
);
963 if (!NT_SUCCESS(Status
) || !hEventLog
)
965 DPRINT1("ElfRegisterEventSourceW failed with Status 0x%08lx\n", Status
);
969 Status
= ElfReportEventW(hEventLog
,
970 EVENTLOG_INFORMATION_TYPE
,
972 STATUS_LOG_HARD_ERROR
,
981 if (!NT_SUCCESS(Status
))
982 DPRINT1("ElfReportEventW failed with Status 0x%08lx\n", Status
);
984 ElfDeregisterEventSource(hEventLog
);
990 IN PCSR_THREAD ThreadData
,
991 IN PHARDERROR_MSG Message
)
995 UNICODE_STRING TextU
, CaptionU
;
996 WCHAR LocalTextBuffer
[256];
997 WCHAR LocalCaptionBuffer
[256];
999 ASSERT(ThreadData
->Process
!= NULL
);
1001 /* Default to not handled */
1002 Message
->Response
= ResponseNotHandled
;
1004 /* Make sure we don't have too many parameters */
1005 if (Message
->NumberOfParameters
> MAXIMUM_HARDERROR_PARAMETERS
)
1007 // NOTE: Windows just fails (STATUS_INVALID_PARAMETER) & returns ResponseNotHandled.
1008 DPRINT1("Invalid NumberOfParameters = %d\n", Message
->NumberOfParameters
);
1009 Message
->NumberOfParameters
= MAXIMUM_HARDERROR_PARAMETERS
;
1011 if (Message
->ValidResponseOptions
> OptionCancelTryContinue
)
1013 DPRINT1("Unknown ValidResponseOptions = %d\n", Message
->ValidResponseOptions
);
1014 return; // STATUS_INVALID_PARAMETER;
1016 if (Message
->Status
== STATUS_SERVICE_NOTIFICATION
)
1018 if (Message
->NumberOfParameters
< 3)
1020 DPRINT1("Invalid NumberOfParameters = %d for STATUS_SERVICE_NOTIFICATION\n", Message
->NumberOfParameters
);
1021 return; // STATUS_INVALID_PARAMETER;
1023 // (Message->UnicodeStringParameterMask & 0x3)
1026 /* Re-initialize the hard errors cache */
1027 UserInitHardErrorsCache();
1029 /* Format the message caption and text */
1030 RtlInitEmptyUnicodeString(&TextU
, LocalTextBuffer
, sizeof(LocalTextBuffer
));
1031 RtlInitEmptyUnicodeString(&CaptionU
, LocalCaptionBuffer
, sizeof(LocalCaptionBuffer
));
1032 UserpFormatMessages(&TextU
, &CaptionU
, &dwType
, /* &Timeout, */ Message
);
1034 /* Log the hard error message */
1035 UserpLogHardError(&TextU
, &CaptionU
);
1037 /* Display a hard error popup depending on the current ErrorMode */
1039 /* Query the error mode value */
1040 ErrorMode
= GetRegInt(L
"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Windows",
1043 if (Message
->Status
!= STATUS_SERVICE_NOTIFICATION
&& ErrorMode
!= 0)
1045 /* Returns OK for the hard error */
1046 Message
->Response
= ResponseOk
;
1050 if (Message
->ValidResponseOptions
== OptionOkNoWait
)
1052 /* Display the balloon */
1053 Message
->Response
= ResponseOk
;
1054 if (UserpShowInformationBalloon(TextU
.Buffer
,
1059 Message
->Response
= ResponseOk
;
1064 /* Display the message box */
1065 Message
->Response
= UserpMessageBox(TextU
.Buffer
,
1071 /* Free the strings if they have been reallocated */
1072 if (TextU
.Buffer
!= LocalTextBuffer
)
1073 RtlFreeUnicodeString(&TextU
);
1074 if (CaptionU
.Buffer
!= LocalCaptionBuffer
)
1075 RtlFreeUnicodeString(&CaptionU
);
1081 UserInitHardErrorsCache(VOID
)
1084 LCID CurrentUserLCID
= 0;
1086 Status
= NtQueryDefaultLocale(TRUE
, &CurrentUserLCID
);
1087 if (!NT_SUCCESS(Status
) || CurrentUserLCID
== 0)
1089 /* Fall back to english locale */
1090 DPRINT1("NtQueryDefaultLocale failed with Status = 0x%08lx\n", Status
);
1091 // LOCALE_SYSTEM_DEFAULT;
1092 CurrentUserLCID
= MAKELCID(MAKELANGID(LANG_ENGLISH
, SUBLANG_ENGLISH_US
), SORT_DEFAULT
);
1094 if (g_CurrentUserLangId
== LANGIDFROMLCID(CurrentUserLCID
))
1096 /* The current lang ID and the hard error strings have already been cached */
1100 /* Load the strings using the current system locale */
1101 RtlLoadUnicodeString(UserServerDllInstance
, IDS_SEVERITY_SUCCESS
,
1102 &g_SuccessU
, L
"Success");
1103 RtlLoadUnicodeString(UserServerDllInstance
, IDS_SEVERITY_INFORMATIONAL
,
1104 &g_InformationU
, L
"System Information");
1105 RtlLoadUnicodeString(UserServerDllInstance
, IDS_SEVERITY_WARNING
,
1106 &g_WarningU
, L
"System Warning");
1107 RtlLoadUnicodeString(UserServerDllInstance
, IDS_SEVERITY_ERROR
,
1108 &g_ErrorU
, L
"System Error");
1109 // "unknown software exception"
1110 RtlLoadUnicodeString(UserServerDllInstance
, IDS_SYSTEM_PROCESS
,
1111 &g_SystemProcessU
, L
"System Process");
1112 RtlLoadUnicodeString(UserServerDllInstance
, IDS_OK_TERMINATE_PROGRAM
,
1113 &g_OKTerminateU
, L
"Click on OK to terminate the program.");
1114 RtlLoadUnicodeString(UserServerDllInstance
, IDS_CANCEL_DEBUG_PROGRAM
,
1115 &g_CancelDebugU
, L
"Click on CANCEL to debug the program.");
1117 /* Remember that we cached the hard error strings */
1118 g_CurrentUserLangId
= LANGIDFROMLCID(CurrentUserLCID
);