You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2084 lines
68 KiB
2084 lines
68 KiB
/**************************** Module Header ********************************\
|
|
* Module Name: harderr.c
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
* Hard error handler
|
|
*
|
|
* History:
|
|
* 07-03-91 JimA Created scaffolding.
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#include <ntlpcapi.h>
|
|
#include <winsta.h>
|
|
|
|
VOID UserHardErrorEx(
|
|
PCSR_THREAD pt,
|
|
PHARDERROR_MSG pmsg,
|
|
PCTXHARDERRORINFO pCtxHEInfo);
|
|
|
|
VOID ProcessHardErrorRequest(
|
|
BOOL fNewThread);
|
|
|
|
|
|
#ifdef PRERELEASE
|
|
HARDERRORINFO hiLastProcessed;
|
|
#endif
|
|
|
|
CONST UINT wIcons[] = {
|
|
0,
|
|
MB_ICONINFORMATION,
|
|
MB_ICONEXCLAMATION,
|
|
MB_ICONSTOP
|
|
};
|
|
|
|
CONST UINT wOptions[] = {
|
|
MB_ABORTRETRYIGNORE,
|
|
MB_OK,
|
|
MB_OKCANCEL,
|
|
MB_RETRYCANCEL,
|
|
MB_YESNO,
|
|
MB_YESNOCANCEL,
|
|
MB_OK, // OptionShutdownSystem
|
|
MB_OK, // OptionOkNoWait
|
|
MB_CANCELTRYCONTINUE
|
|
};
|
|
|
|
CONST DWORD dwResponses[] = {
|
|
ResponseNotHandled, // MessageBox error
|
|
ResponseOk, // IDOK
|
|
ResponseCancel, // IDCANCEL
|
|
ResponseAbort, // IDABORT
|
|
ResponseRetry, // IDRETRY
|
|
ResponseIgnore, // IDIGNORE
|
|
ResponseYes, // IDYES
|
|
ResponseNo, // IDNO
|
|
ResponseNotHandled, // Error as IDCLOSE can't show up
|
|
ResponseNotHandled, // error as IDHELP can't show up
|
|
ResponseTryAgain, // IDTRYAGAIN
|
|
ResponseContinue // IDCONTINUE
|
|
};
|
|
|
|
CONST DWORD dwResponseDefault[] = {
|
|
ResponseAbort, // OptionAbortRetryIgnore
|
|
ResponseOk, // OptionOK
|
|
ResponseOk, // OptionOKCancel
|
|
ResponseCancel, // OptionRetryCancel
|
|
ResponseYes, // OptionYesNo
|
|
ResponseYes, // OptionYesNoCancel
|
|
ResponseOk, // OptionShutdownSystem
|
|
ResponseOk, // OptionOKNoWait
|
|
ResponseCancel // OptionCancelTryContinue
|
|
};
|
|
|
|
/*
|
|
* Citrix SendMessage entry point to harderror handler and cleanup routine
|
|
*/
|
|
VOID HardErrorInsert(PCSR_THREAD, PHARDERROR_MSG, PCTXHARDERRORINFO);
|
|
VOID HardErrorRemove(PCTXHARDERRORINFO);
|
|
|
|
/***************************************************************************\
|
|
* LogErrorPopup
|
|
*
|
|
* History:
|
|
* 09-22-97 GerardoB Added Header
|
|
\***************************************************************************/
|
|
VOID LogErrorPopup(
|
|
IN LPWSTR Caption,
|
|
IN LPWSTR Message)
|
|
{
|
|
LPWSTR lps[2];
|
|
|
|
lps[0] = Caption;
|
|
lps[1] = Message;
|
|
|
|
UserAssert(gEventSource != NULL);
|
|
ReportEvent(gEventSource,
|
|
EVENTLOG_INFORMATION_TYPE,
|
|
0,
|
|
STATUS_LOG_HARD_ERROR,
|
|
NULL,
|
|
ARRAY_SIZE(lps),
|
|
0,
|
|
lps,
|
|
NULL);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* SubstituteDeviceName
|
|
*
|
|
* History:
|
|
* 09-22-97 GerardoB Added Header
|
|
\***************************************************************************/
|
|
static WCHAR wszDosDevices[] = L"\\??\\A:";
|
|
VOID
|
|
SubstituteDeviceName(
|
|
PUNICODE_STRING InputDeviceName,
|
|
LPSTR OutputDriveLetter
|
|
)
|
|
{
|
|
UNICODE_STRING LinkName;
|
|
UNICODE_STRING DeviceName;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
HANDLE LinkHandle;
|
|
NTSTATUS Status;
|
|
ULONG i;
|
|
PWCHAR p;
|
|
WCHAR DeviceNameBuffer[MAXIMUM_FILENAME_LENGTH];
|
|
|
|
RtlInitUnicodeString(&LinkName,wszDosDevices);
|
|
p = wszDosDevices + ARRAY_SIZE(wszDosDevices) - ARRAY_SIZE(L"A:");
|
|
for(i=0;i<26;i++){
|
|
*p = (WCHAR)('A' + i);
|
|
|
|
InitializeObjectAttributes(
|
|
&Obja,
|
|
&LinkName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
Status = NtOpenSymbolicLinkObject(
|
|
&LinkHandle,
|
|
SYMBOLIC_LINK_QUERY,
|
|
&Obja
|
|
);
|
|
if (NT_SUCCESS( Status )) {
|
|
|
|
//
|
|
// Open succeeded, Now get the link value
|
|
//
|
|
|
|
DeviceName.Length = 0;
|
|
DeviceName.MaximumLength = sizeof(DeviceNameBuffer);
|
|
DeviceName.Buffer = DeviceNameBuffer;
|
|
|
|
Status = NtQuerySymbolicLinkObject(
|
|
LinkHandle,
|
|
&DeviceName,
|
|
NULL
|
|
);
|
|
NtClose(LinkHandle);
|
|
if ( NT_SUCCESS(Status) ) {
|
|
if ( RtlEqualUnicodeString(InputDeviceName,&DeviceName,TRUE) ) {
|
|
OutputDriveLetter[0]=(CHAR)('A'+i);
|
|
OutputDriveLetter[1]=':';
|
|
OutputDriveLetter[2]='\0';
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/***************************************************************************\
|
|
* GetErrorMode
|
|
*
|
|
* History:
|
|
* 09-22-97 GerardoB Added Header
|
|
\***************************************************************************/
|
|
DWORD GetErrorMode(VOID)
|
|
{
|
|
HANDLE hKey;
|
|
UNICODE_STRING UnicodeString;
|
|
OBJECT_ATTRIBUTES OA;
|
|
LONG Status;
|
|
BYTE Buf[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(DWORD)];
|
|
DWORD cbSize;
|
|
DWORD dwRet = 0;
|
|
|
|
RtlInitUnicodeString(&UnicodeString,
|
|
L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Windows");
|
|
InitializeObjectAttributes(&OA, &UnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
|
|
Status = NtOpenKey(&hKey, KEY_READ, &OA);
|
|
if (NT_SUCCESS(Status)) {
|
|
RtlInitUnicodeString(&UnicodeString, L"ErrorMode");
|
|
Status = NtQueryValueKey(hKey,
|
|
&UnicodeString,
|
|
KeyValuePartialInformation,
|
|
(PKEY_VALUE_PARTIAL_INFORMATION)Buf,
|
|
sizeof(Buf),
|
|
&cbSize);
|
|
if (NT_SUCCESS(Status)) {
|
|
dwRet = *((PDWORD)((PKEY_VALUE_PARTIAL_INFORMATION)Buf)->Data);
|
|
}
|
|
NtClose(hKey);
|
|
}
|
|
return dwRet;
|
|
}
|
|
/***************************************************************************\
|
|
* FreePhi
|
|
*
|
|
* History:
|
|
* 09-18-97 GerardoB Created
|
|
\***************************************************************************/
|
|
void FreePhi (PHARDERRORINFO phi)
|
|
{
|
|
if (phi->dwHEIFFlags & HEIF_ALLOCATEDMSG) {
|
|
LocalFree(phi->pmsg);
|
|
}
|
|
|
|
RtlFreeUnicodeString(&phi->usText);
|
|
RtlFreeUnicodeString(&phi->usCaption);
|
|
|
|
LocalFree(phi);
|
|
}
|
|
/***************************************************************************\
|
|
* ReplyHardError
|
|
*
|
|
* This function is called when we are done with a hard error.
|
|
*
|
|
* History:
|
|
* 03-11-97 GerardoB Created
|
|
\***************************************************************************/
|
|
VOID ReplyHardError(
|
|
PHARDERRORINFO phi,
|
|
DWORD dwResponse)
|
|
{
|
|
phi->pmsg->Response = dwResponse;
|
|
|
|
/*
|
|
* Signal the event if any. If not, reply if we haven't done so already.
|
|
*/
|
|
if (phi->hEventHardError != NULL) {
|
|
NtSetEvent(phi->hEventHardError, NULL);
|
|
} else if (!(phi->dwHEIFFlags & HEIF_REPLIED)) {
|
|
NtReplyPort(((PCSR_THREAD)phi->pthread)->Process->ClientPort,
|
|
(PPORT_MESSAGE)phi->pmsg);
|
|
}
|
|
|
|
/*
|
|
* If we had locked the thread or were holding the client port, then let
|
|
* it go now.
|
|
*/
|
|
if (phi->dwHEIFFlags & HEIF_DEREFTHREAD) {
|
|
CsrDereferenceThread(phi->pthread);
|
|
}
|
|
|
|
/*
|
|
* We're done with this dude.
|
|
*/
|
|
FreePhi(phi);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* CheckDefaultDesktop
|
|
*
|
|
* This function is called by the HardErrorHandler when it's notified that
|
|
* we've switched desktops or upon waking up. If we're on the default desktop
|
|
* now, then we clear the HEIF_WRONGDESKTOP flag. This flag is set when we
|
|
* find a MB_DEFAULT_DESKTOP_ONLY request but we are not in the right
|
|
* (default) desktop.
|
|
*
|
|
* History:
|
|
* 06-02-97 GerardoB Created
|
|
\***************************************************************************/
|
|
VOID CheckDefaultDesktop(
|
|
VOID)
|
|
{
|
|
PHARDERRORINFO phi;
|
|
|
|
if (HEC_WRONGDESKTOP == NtUserHardErrorControl(HardErrorInDefDesktop, NULL, NULL)) {
|
|
return;
|
|
}
|
|
|
|
EnterCrit();
|
|
phi = gphiList;
|
|
while (phi != NULL) {
|
|
phi->dwHEIFFlags &= ~HEIF_WRONGDESKTOP;
|
|
phi = phi->phiNext;
|
|
}
|
|
LeaveCrit();
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* GetHardErrorText
|
|
*
|
|
* This function figures out the message box title, text and flags. We want
|
|
* to do this up front so we can log this error when the hard error is
|
|
* raised. Previously we used to log it after the user had dismissed the
|
|
* message box -- but that was not when the error occurred (DCR Bug 107590).
|
|
*
|
|
* History:
|
|
* 09-18-97 GerardoB Extracted (and cleaned up) from HardErrorHandler
|
|
\***************************************************************************/
|
|
VOID GetHardErrorText(
|
|
PHARDERRORINFO phi)
|
|
{
|
|
static WCHAR wszUnkownSoftwareException [] = L"unknown software exception";
|
|
static WCHAR wszException [] = L"{EXCEPTION}";
|
|
static WCHAR wszUnknownHardError [] = L"Unknown Hard Error";
|
|
ANSI_STRING asLocal, asMessage;
|
|
BOOL fFreeAppNameBuffer, fFreeCaption;
|
|
BOOL fResAllocated, fResAllocated1, fErrorIsFromSystem;
|
|
WCHAR wszErrorMessage[WSPRINTF_LIMIT + 1];
|
|
DWORD dwCounter, dwStringsToFreeMask, dwMBFlags, dwTimeout;
|
|
ULONG_PTR adwParameterVector[MAXIMUM_HARDERROR_PARAMETERS];
|
|
HANDLE hClientProcess;
|
|
HWND hwndOwner;
|
|
NTSTATUS Status;
|
|
PHARDERROR_MSG phemsg;
|
|
PMESSAGE_RESOURCE_ENTRY MessageEntry;
|
|
PWSTR pwszCaption, pwszFormatString;
|
|
PWSTR pwszAppName, pwszResBuffer, pwszResBuffer1;
|
|
PWSTR pwszMsg, pwszTitle, pwszFullCaption;
|
|
UINT uMsgLen, uCaptionLen, uTitleLen;
|
|
UNICODE_STRING usScratch, usLocal, usMessage, usCaption;
|
|
|
|
/*
|
|
* Initialize working variables
|
|
*/
|
|
fFreeAppNameBuffer = fFreeCaption = FALSE;
|
|
hClientProcess = NULL;
|
|
RtlInitUnicodeString(&usCaption, NULL);
|
|
RtlInitUnicodeString(&usMessage, NULL);
|
|
dwTimeout = INFINITE;
|
|
|
|
/*
|
|
* Initialize response in case something goes wrong
|
|
*/
|
|
phemsg = phi->pmsg;
|
|
phemsg->Response = ResponseNotHandled;
|
|
|
|
/*
|
|
* Make a copy of the parameters. Initialize unused ones to point to
|
|
* empty strings (in case we expect a string there).
|
|
*/
|
|
UserAssert(phemsg->NumberOfParameters <= MAXIMUM_HARDERROR_PARAMETERS);
|
|
RtlCopyMemory(adwParameterVector, phemsg->Parameters, phemsg->NumberOfParameters * sizeof(*phemsg->Parameters));
|
|
dwCounter = phemsg->NumberOfParameters;
|
|
while (dwCounter < MAXIMUM_HARDERROR_PARAMETERS) {
|
|
adwParameterVector[dwCounter++] = (ULONG_PTR)L"";
|
|
}
|
|
|
|
/*
|
|
* Open the client process so we can read the strings parameters, process
|
|
* name, etc ... from its address space.
|
|
*/
|
|
hClientProcess = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION,
|
|
FALSE, HandleToUlong(phemsg->h.ClientId.UniqueProcess));
|
|
fErrorIsFromSystem = (hClientProcess == NULL);
|
|
|
|
/*
|
|
* If there are unicode strings, then we need to convert them to ansi
|
|
* and store them in the parameter vector.
|
|
*/
|
|
dwStringsToFreeMask = 0;
|
|
if (phemsg->UnicodeStringParameterMask) {
|
|
|
|
for (dwCounter = 0; dwCounter < phemsg->NumberOfParameters; dwCounter++) {
|
|
/*
|
|
* If there is no string in this position, continue.
|
|
*/
|
|
if (!(phemsg->UnicodeStringParameterMask & (1 << dwCounter))) {
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Point to an empty string in case we don't have a client to
|
|
* read from or something fails later on.
|
|
*/
|
|
adwParameterVector[dwCounter] = (ULONG_PTR)L"";
|
|
if (hClientProcess == NULL) {
|
|
continue;
|
|
}
|
|
|
|
Status = NtReadVirtualMemory(hClientProcess,
|
|
(PVOID)phemsg->Parameters[dwCounter],
|
|
(PVOID)&usScratch,
|
|
sizeof(usScratch), NULL);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG0(RIP_WARNING, "Failed to read error string struct!");
|
|
continue;
|
|
}
|
|
|
|
usLocal = usScratch;
|
|
usLocal.Buffer = (PWSTR)LocalAlloc(LMEM_ZEROINIT, usLocal.Length + sizeof(UNICODE_NULL));
|
|
if (usLocal.Buffer == NULL) {
|
|
RIPMSG0(RIP_WARNING, "Failed to alloc string buffer!");
|
|
continue;
|
|
}
|
|
|
|
Status = NtReadVirtualMemory(hClientProcess,
|
|
(PVOID)usScratch.Buffer,
|
|
(PVOID)usLocal.Buffer,
|
|
usLocal.Length,
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
LocalFree(usLocal.Buffer);
|
|
RIPMSG0(RIP_WARNING, "Failed to read error string!");
|
|
continue;
|
|
}
|
|
|
|
usLocal.MaximumLength = usLocal.Length;
|
|
Status = RtlUnicodeStringToAnsiString(&asLocal, &usLocal, TRUE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
LocalFree(usLocal.Buffer);
|
|
RIPMSG0(RIP_WARNING, "Failed to translate error string!");
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Check to see if string contains an NT device name. If so,
|
|
* then attempt a drive letter substitution.
|
|
*/
|
|
if (strstr(asLocal.Buffer,"\\Device") == asLocal.Buffer) {
|
|
SubstituteDeviceName(&usLocal,asLocal.Buffer);
|
|
} else if ((asLocal.Length > 4) && !_strnicmp(asLocal.Buffer, "\\??\\", 4)) {
|
|
strcpy( asLocal.Buffer, asLocal.Buffer+4 );
|
|
asLocal.Length -= 4;
|
|
} else {
|
|
/*
|
|
* Processing some status code doesn't require ansi strings.
|
|
* Since no substitution took place, let's ignore the
|
|
* translation to avoid losing chars due to incorrect code
|
|
* page translation.
|
|
*/
|
|
switch (phemsg->Status) {
|
|
case STATUS_SERVICE_NOTIFICATION:
|
|
case STATUS_VDM_HARD_ERROR:
|
|
adwParameterVector[dwCounter] = (ULONG_PTR)usLocal.Buffer;
|
|
RtlFreeAnsiString(&asLocal);
|
|
continue;
|
|
}
|
|
|
|
}
|
|
|
|
LocalFree(usLocal.Buffer);
|
|
|
|
dwStringsToFreeMask |= (1 << dwCounter);
|
|
adwParameterVector[dwCounter] = (ULONG_PTR)asLocal.Buffer;
|
|
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Read additional MB flags, if provided.
|
|
*/
|
|
#if (HARDERROR_PARAMETERS_FLAGSPOS >= MAXIMUM_HARDERROR_PARAMETERS)
|
|
#error Invalid HARDERROR_PARAMETERS_FLAGSPOS value.
|
|
#endif
|
|
#if (HARDERROR_FLAGS_DEFDESKTOPONLY != MB_DEFAULT_DESKTOP_ONLY)
|
|
#error Invalid HARDERROR_FLAGS_DEFDESKTOPONLY
|
|
#endif
|
|
dwMBFlags = 0;
|
|
if (phemsg->NumberOfParameters > HARDERROR_PARAMETERS_FLAGSPOS) {
|
|
/*
|
|
* Currently we only use MB_DEFAULT_DESKTOP_ONLY
|
|
*/
|
|
UserAssert(!(adwParameterVector[HARDERROR_PARAMETERS_FLAGSPOS] & ~MB_DEFAULT_DESKTOP_ONLY));
|
|
if (adwParameterVector[HARDERROR_PARAMETERS_FLAGSPOS] & MB_DEFAULT_DESKTOP_ONLY) {
|
|
dwMBFlags |= MB_DEFAULT_DESKTOP_ONLY;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* For some status codes, all MessageBox parameters are provided in the
|
|
* HardError parameters.
|
|
*/
|
|
switch (phemsg->Status) {
|
|
case STATUS_SERVICE_NOTIFICATION:
|
|
if (phemsg->UnicodeStringParameterMask & 0x1) {
|
|
RtlInitUnicodeString(&usMessage,
|
|
*(PWSTR)adwParameterVector[0] ?
|
|
(PWSTR)adwParameterVector[0] : NULL);
|
|
} else {
|
|
RtlInitAnsiString(&asMessage, (PSTR)adwParameterVector[0]);
|
|
RtlAnsiStringToUnicodeString(&usMessage, &asMessage, TRUE);
|
|
}
|
|
|
|
if (phemsg->UnicodeStringParameterMask & 0x2) {
|
|
RtlInitUnicodeString(&usCaption,
|
|
*(PWSTR)adwParameterVector[1] ?
|
|
(PWSTR)adwParameterVector[1] : NULL);
|
|
} else {
|
|
RtlInitAnsiString(&asMessage, (PSTR)adwParameterVector[1]);
|
|
RtlAnsiStringToUnicodeString(&usCaption, &asMessage, TRUE);
|
|
}
|
|
|
|
dwMBFlags = (DWORD)adwParameterVector[2] & ~MB_SERVICE_NOTIFICATION;
|
|
if (phemsg->NumberOfParameters == 4) {
|
|
dwTimeout = (DWORD)adwParameterVector[3];
|
|
} else {
|
|
dwTimeout = INFINITE;
|
|
}
|
|
goto CleanUpAndSaveParams;
|
|
|
|
case STATUS_VDM_HARD_ERROR:
|
|
/*
|
|
* Parameters[0] = (fForWOW << 16) | wBtn1;
|
|
* Parameters[1] = (wBtn2 << 16) | wBtn3;
|
|
* Parameters[2] = (DWORD) szTitle;
|
|
* Parameters[3] = (DWORD) szMessage;
|
|
*/
|
|
phi->dwHEIFFlags |= HEIF_VDMERROR;
|
|
|
|
/*
|
|
* Save VDM's Button(s) info to be used later.
|
|
*/
|
|
phi->dwVDMParam0 = (DWORD)adwParameterVector[0];
|
|
phi->dwVDMParam1 = (DWORD)adwParameterVector[1];
|
|
|
|
/*
|
|
* Get caption and text.
|
|
*/
|
|
try {
|
|
if (phemsg->UnicodeStringParameterMask & 0x4) {
|
|
RtlInitUnicodeString(&usCaption,
|
|
*(PWSTR)adwParameterVector[2] ?
|
|
(PWSTR)adwParameterVector[2] : NULL);
|
|
} else {
|
|
if (!MBToWCS((LPSTR)adwParameterVector[2], -1, &pwszTitle, -1, TRUE)) {
|
|
goto CleanUpAndSaveParams;
|
|
}
|
|
RtlCreateUnicodeString(&usCaption, pwszTitle);
|
|
RtlFreeHeap(RtlProcessHeap(), 0, pwszTitle);
|
|
}
|
|
|
|
if (phemsg->UnicodeStringParameterMask & 0x8) {
|
|
RtlInitUnicodeString(&usMessage,
|
|
*(PWSTR)adwParameterVector[3] ?
|
|
(PWSTR)adwParameterVector[3] : NULL);
|
|
} else {
|
|
if (!MBToWCS((LPSTR)adwParameterVector[3], -1, &pwszMsg, -1, TRUE)) {
|
|
goto CleanUpAndSaveParams;
|
|
}
|
|
RtlCreateUnicodeString(&usMessage, pwszMsg);
|
|
RtlFreeHeap(RtlProcessHeap(), 0, pwszMsg);
|
|
}
|
|
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
RIPMSG0(RIP_WARNING, "Exception reading STATUS_VDM_HARD_ERROR paramerters");
|
|
|
|
RtlFreeUnicodeString(&usCaption);
|
|
RtlCreateUnicodeString(&usCaption, L"VDM Internal Error");
|
|
RtlFreeUnicodeString(&usMessage);
|
|
RtlCreateUnicodeString(&usMessage, L"Exception retrieving error text.");
|
|
}
|
|
goto CleanUpAndSaveParams;
|
|
}
|
|
|
|
/*
|
|
* For all other status codes, we generate the information from the
|
|
* status code. First, Map status code and valid response to MessageBox
|
|
* flags.
|
|
*/
|
|
dwMBFlags |= wIcons[(ULONG)(phemsg->Status) >> 30] | wOptions[phemsg->ValidResponseOptions];
|
|
|
|
/*
|
|
* If we have a client process, try to get the actual application name.
|
|
*/
|
|
pwszAppName = NULL;
|
|
if (!fErrorIsFromSystem) {
|
|
PPEB Peb;
|
|
PROCESS_BASIC_INFORMATION BasicInfo;
|
|
PLDR_DATA_TABLE_ENTRY LdrEntry;
|
|
LDR_DATA_TABLE_ENTRY LdrEntryData;
|
|
PLIST_ENTRY LdrHead, LdrNext;
|
|
PPEB_LDR_DATA Ldr;
|
|
PVOID ImageBaseAddress;
|
|
PWSTR ClientApplicationName;
|
|
|
|
/*
|
|
* This is cumbersome, but basically, we locate the processes loader
|
|
* data table and get its name directly out of the loader table.
|
|
*/
|
|
Status = NtQueryInformationProcess(hClientProcess,
|
|
ProcessBasicInformation,
|
|
&BasicInfo,
|
|
sizeof(BasicInfo),
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
fErrorIsFromSystem = TRUE;
|
|
goto noname;
|
|
}
|
|
|
|
Peb = BasicInfo.PebBaseAddress;
|
|
if (Peb == NULL) {
|
|
fErrorIsFromSystem = TRUE;
|
|
goto noname;
|
|
}
|
|
|
|
/*
|
|
* ldr = Peb->Ldr
|
|
*/
|
|
Status = NtReadVirtualMemory(hClientProcess,
|
|
&Peb->Ldr,
|
|
&Ldr,
|
|
sizeof(Ldr),
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto noname;
|
|
}
|
|
|
|
LdrHead = &Ldr->InLoadOrderModuleList;
|
|
|
|
/*
|
|
* LdrNext = Head->Flink;
|
|
*/
|
|
Status = NtReadVirtualMemory(hClientProcess,
|
|
&LdrHead->Flink,
|
|
&LdrNext,
|
|
sizeof(LdrNext),
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto noname;
|
|
}
|
|
|
|
if (LdrNext == LdrHead) {
|
|
goto noname;
|
|
}
|
|
|
|
/*
|
|
* This is the entry data for the image.
|
|
*/
|
|
LdrEntry = CONTAINING_RECORD(LdrNext, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
|
|
Status = NtReadVirtualMemory(hClientProcess,
|
|
LdrEntry,
|
|
&LdrEntryData,
|
|
sizeof(LdrEntryData),
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto noname;
|
|
}
|
|
|
|
Status = NtReadVirtualMemory(hClientProcess,
|
|
&Peb->ImageBaseAddress,
|
|
&ImageBaseAddress,
|
|
sizeof(ImageBaseAddress),
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto noname;
|
|
}
|
|
|
|
if (ImageBaseAddress != LdrEntryData.DllBase) {
|
|
goto noname;
|
|
}
|
|
|
|
LdrNext = LdrEntryData.InLoadOrderLinks.Flink;
|
|
|
|
ClientApplicationName = LocalAlloc(LMEM_ZEROINIT, LdrEntryData.BaseDllName.MaximumLength);
|
|
if (ClientApplicationName == NULL) {
|
|
goto noname;
|
|
}
|
|
|
|
Status = NtReadVirtualMemory(hClientProcess,
|
|
LdrEntryData.BaseDllName.Buffer,
|
|
ClientApplicationName,
|
|
LdrEntryData.BaseDllName.MaximumLength,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
LocalFree(ClientApplicationName);
|
|
goto noname;
|
|
}
|
|
|
|
pwszAppName = ClientApplicationName;
|
|
fFreeAppNameBuffer = TRUE;
|
|
|
|
noname:;
|
|
}
|
|
|
|
if (pwszAppName == NULL) {
|
|
/*
|
|
* Load default application name (to be used in the caption).
|
|
*/
|
|
pwszAppName = ServerLoadString(ghModuleWin,
|
|
STR_UNKNOWN_APPLICATION,
|
|
L"System Process",
|
|
&fFreeAppNameBuffer);
|
|
}
|
|
|
|
/*
|
|
* Map status code to (optional) caption and format string. If a caption
|
|
* is provided, it's enclosed in {} and it's the first thing in the
|
|
* format string.
|
|
*/
|
|
EnterCrit();
|
|
if (gNtDllHandle == NULL) {
|
|
gNtDllHandle = GetModuleHandle(TEXT("ntdll"));
|
|
UserAssert(gNtDllHandle != NULL);
|
|
}
|
|
LeaveCrit();
|
|
|
|
Status = RtlFindMessage((PVOID)gNtDllHandle,
|
|
(ULONG_PTR)RT_MESSAGETABLE,
|
|
LANG_NEUTRAL,
|
|
phemsg->Status,
|
|
&MessageEntry);
|
|
|
|
/*
|
|
* Parse the caption (if any) and the format string.
|
|
*/
|
|
pwszCaption = NULL;
|
|
if (!NT_SUCCESS(Status)) {
|
|
pwszFormatString = wszUnknownHardError;
|
|
} else {
|
|
pwszFormatString = (PWSTR)MessageEntry->Text;
|
|
/*
|
|
* If the message starts with a '{', it has a caption.
|
|
*/
|
|
if (*pwszFormatString == L'{') {
|
|
uCaptionLen = 0;
|
|
pwszFormatString++;
|
|
|
|
/*
|
|
* Find the closing bracket.
|
|
*/
|
|
while (*pwszFormatString != (WCHAR)0 && *pwszFormatString++ != L'}') {
|
|
uCaptionLen++;
|
|
}
|
|
|
|
/*
|
|
* Eat any non-printable stuff (\r\n), up to the NULL.
|
|
*/
|
|
while (*pwszFormatString != (WCHAR)0 && *pwszFormatString <= L' ') {
|
|
pwszFormatString++;
|
|
}
|
|
|
|
/*
|
|
* Allocate a buffer an copy the caption string.
|
|
*/
|
|
if (uCaptionLen++ > 0 && (pwszCaption = (PWSTR)LocalAlloc(LPTR, uCaptionLen * sizeof(WCHAR))) != NULL) {
|
|
RtlCopyMemory(pwszCaption, (PWSTR)MessageEntry->Text + 1, (uCaptionLen - 1) * sizeof(WCHAR));
|
|
fFreeCaption = TRUE;
|
|
}
|
|
}
|
|
|
|
if (*pwszFormatString == (WCHAR)0) {
|
|
pwszFormatString = wszUnknownHardError;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* If the message didn't include a caption (or we didn't find the message),
|
|
* default to something.
|
|
*/
|
|
if (pwszCaption == NULL) {
|
|
switch (phemsg->Status & ERROR_SEVERITY_ERROR) {
|
|
case ERROR_SEVERITY_SUCCESS:
|
|
pwszCaption = gpwszaSUCCESS;
|
|
break;
|
|
case ERROR_SEVERITY_INFORMATIONAL:
|
|
pwszCaption = gpwszaSYSTEM_INFORMATION;
|
|
break;
|
|
case ERROR_SEVERITY_WARNING:
|
|
pwszCaption = gpwszaSYSTEM_WARNING;
|
|
break;
|
|
case ERROR_SEVERITY_ERROR:
|
|
pwszCaption = gpwszaSYSTEM_ERROR;
|
|
break;
|
|
}
|
|
}
|
|
UserAssert(pwszCaption != NULL);
|
|
|
|
/*
|
|
* If the client has a window, get its title so it can be added to the
|
|
* caption.
|
|
*/
|
|
hwndOwner = NULL;
|
|
EnumThreadWindows(HandleToUlong(phemsg->h.ClientId.UniqueThread),
|
|
FindWindowFromThread,
|
|
(LPARAM)&hwndOwner);
|
|
if (hwndOwner == NULL) {
|
|
uTitleLen = 0;
|
|
} else {
|
|
uTitleLen = GetWindowTextLength(hwndOwner);
|
|
if (uTitleLen != 0) {
|
|
pwszTitle = (PWSTR)LocalAlloc(LPTR, (uTitleLen + 3) * sizeof(WCHAR));
|
|
if (pwszTitle != NULL) {
|
|
GetWindowText(hwndOwner, pwszTitle, uTitleLen + 1);
|
|
/*
|
|
* Add format chars.
|
|
*/
|
|
*(pwszTitle + uTitleLen++) = (WCHAR)':';
|
|
*(pwszTitle + uTitleLen++) = (WCHAR)' ';
|
|
} else {
|
|
/*
|
|
* We couldn't allocate a buffer to get the title.
|
|
*/
|
|
uTitleLen = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we don't have a window title, make it an empty string so we won't
|
|
* have to special case it later.
|
|
*/
|
|
if (uTitleLen == 0) {
|
|
pwszTitle = L"";
|
|
}
|
|
|
|
/*
|
|
* Finally we can build the caption string now. It looks like this:
|
|
* [WindowTile: ]ApplicationName - ErrorCaption
|
|
*/
|
|
uCaptionLen = uTitleLen + wcslen(pwszAppName) + 3 + wcslen(pwszCaption) + 1;
|
|
pwszFullCaption = (PWSTR)LocalAlloc(LPTR, uCaptionLen * sizeof(WCHAR));
|
|
if (pwszFullCaption != NULL) {
|
|
#if DBG
|
|
int iLen =
|
|
#endif
|
|
wsprintfW(pwszFullCaption, L"%s%s - %s", pwszTitle, pwszAppName, pwszCaption);
|
|
UserAssert((UINT)iLen < uCaptionLen);
|
|
RtlCreateUnicodeString(&usCaption, pwszFullCaption);
|
|
LocalFree(pwszFullCaption);
|
|
}
|
|
|
|
/*
|
|
* Free caption working buffers, as appropriate.
|
|
*/
|
|
if (fFreeCaption) {
|
|
LocalFree(pwszCaption);
|
|
}
|
|
if (fFreeAppNameBuffer) {
|
|
LocalFree(pwszAppName);
|
|
}
|
|
if (uTitleLen != 0) {
|
|
LocalFree(pwszTitle);
|
|
}
|
|
|
|
/*
|
|
* Build the error message using pszFormatString and adwParameterVector.
|
|
* Special case UAE.
|
|
*/
|
|
if (phemsg->Status == STATUS_UNHANDLED_EXCEPTION ) {
|
|
/*
|
|
* The first parameter has the exception status code. Map it to a
|
|
* format string and build the error message with it and the
|
|
* parameters.
|
|
*/
|
|
Status = RtlFindMessage((PVOID)gNtDllHandle,
|
|
(ULONG_PTR)RT_MESSAGETABLE,
|
|
LANG_NEUTRAL,
|
|
(ULONG)adwParameterVector[0],
|
|
&MessageEntry);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
/*
|
|
* We couldn't read the exception name so let's use unknown.
|
|
*/
|
|
pwszResBuffer = ServerLoadString(ghModuleWin, STR_UNKNOWN_EXCEPTION,
|
|
wszUnkownSoftwareException, &fResAllocated);
|
|
|
|
wsprintfW(wszErrorMessage, pwszFormatString, pwszResBuffer,
|
|
adwParameterVector[0], adwParameterVector[1]);
|
|
|
|
if (fResAllocated) {
|
|
LocalFree(pwszResBuffer);
|
|
}
|
|
|
|
RtlCreateUnicodeString(&usMessage, wszErrorMessage);
|
|
UserAssert(usMessage.MaximumLength <= sizeof(wszErrorMessage));
|
|
} else {
|
|
/*
|
|
* Access Violations are handled a bit differently.
|
|
*/
|
|
|
|
if (adwParameterVector[0] == STATUS_ACCESS_VIOLATION ) {
|
|
|
|
wsprintfW(wszErrorMessage, (PWSTR)MessageEntry->Text, adwParameterVector[1],
|
|
adwParameterVector[3], adwParameterVector[2] ? L"written" : L"read");
|
|
|
|
} else if (adwParameterVector[0] == STATUS_IN_PAGE_ERROR) {
|
|
wsprintfW(wszErrorMessage, (PWSTR)MessageEntry->Text, adwParameterVector[1],
|
|
adwParameterVector[3], adwParameterVector[2]);
|
|
|
|
} else {
|
|
/*
|
|
* If this is a marked exception, skip the mark; the
|
|
* exception name follows it.
|
|
*/
|
|
pwszCaption = (PWSTR)MessageEntry->Text;
|
|
if (!wcsncmp(pwszCaption, wszException, ARRAY_SIZE(wszException) - 1)) {
|
|
pwszCaption += ARRAY_SIZE(wszException) - 1;
|
|
|
|
/*
|
|
* Skip non-printable stuff (\r\n).
|
|
*/
|
|
while (*pwszCaption != (WCHAR)0 && *pwszCaption <= L' ') {
|
|
pwszCaption++;
|
|
}
|
|
} else {
|
|
pwszCaption = wszUnkownSoftwareException;
|
|
}
|
|
|
|
wsprintfW(wszErrorMessage, pwszFormatString, pwszCaption,
|
|
adwParameterVector[0], adwParameterVector[1]);
|
|
}
|
|
|
|
UserAssert(wcslen(wszErrorMessage) < ARRAY_SIZE(wszErrorMessage));
|
|
|
|
/*
|
|
* Add button(s) explanation text.
|
|
*/
|
|
pwszResBuffer = ServerLoadString(ghModuleWin, STR_OK_TO_TERMINATE,
|
|
L"Click on OK to terminate the application",
|
|
&fResAllocated);
|
|
|
|
|
|
if (phemsg->ValidResponseOptions == OptionOkCancel ) {
|
|
pwszResBuffer1 = ServerLoadString(ghModuleWin,
|
|
STR_CANCEL_TO_DEBUG, L"Click on CANCEL xx to debug the application",
|
|
&fResAllocated1);
|
|
} else {
|
|
pwszResBuffer1 = NULL;
|
|
fResAllocated1 = FALSE;
|
|
}
|
|
|
|
/*
|
|
* Conncatenate all strings, one per line.
|
|
*/
|
|
uMsgLen = wcslen(wszErrorMessage)
|
|
+ wcslen(pwszResBuffer) + 1
|
|
+ (pwszResBuffer1 == NULL ? 0 : wcslen(pwszResBuffer1) + 1)
|
|
+ 1;
|
|
|
|
pwszMsg = (PWSTR) LocalAlloc(LPTR, uMsgLen * sizeof(WCHAR));
|
|
if (pwszMsg != NULL) {
|
|
#if DBG
|
|
int iLen =
|
|
#endif
|
|
wsprintfW(pwszMsg, L"%s\n%s%s%s", wszErrorMessage, pwszResBuffer,
|
|
(pwszResBuffer1 == NULL ? L"" : L"\n"),
|
|
(pwszResBuffer1 == NULL ? L"" : pwszResBuffer1));
|
|
|
|
UserAssert((UINT)iLen < uMsgLen);
|
|
|
|
RtlCreateUnicodeString(&usMessage, pwszMsg);
|
|
LocalFree(pwszMsg);
|
|
}
|
|
|
|
/*
|
|
* Free ServerLoadString allocations.
|
|
*/
|
|
if (fResAllocated) {
|
|
LocalFree(pwszResBuffer);
|
|
}
|
|
|
|
if (fResAllocated1) {
|
|
LocalFree(pwszResBuffer1);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
} else {
|
|
/*
|
|
* Default message text generation for all other status codes.
|
|
*/
|
|
try {
|
|
#if DBG
|
|
int iLen =
|
|
#endif
|
|
wsprintfW(wszErrorMessage, pwszFormatString, adwParameterVector[0],
|
|
adwParameterVector[1],
|
|
adwParameterVector[2],
|
|
adwParameterVector[3]);
|
|
UserAssert((UINT)iLen < ARRAY_SIZE(wszErrorMessage));
|
|
|
|
/*
|
|
* Remove \r\n.
|
|
*/
|
|
pwszFormatString = wszErrorMessage;
|
|
while (*pwszFormatString != (WCHAR)0) {
|
|
if (*pwszFormatString == (WCHAR)0xd) {
|
|
*pwszFormatString = L' ';
|
|
|
|
/*
|
|
* Move everything up if a CR LF sequence is found.
|
|
*/
|
|
if (*(pwszFormatString+1) == (WCHAR)0xa) {
|
|
UINT uSize = (wcslen(pwszFormatString+1) + 1) * sizeof(WCHAR);
|
|
RtlMoveMemory(pwszFormatString, pwszFormatString+1, uSize);
|
|
}
|
|
}
|
|
|
|
if (*pwszFormatString == (WCHAR)0xa) {
|
|
*pwszFormatString = L' ';
|
|
}
|
|
|
|
pwszFormatString++;
|
|
}
|
|
|
|
RtlCreateUnicodeString(&usMessage, wszErrorMessage);
|
|
UserAssert(usMessage.MaximumLength <= sizeof(wszErrorMessage));
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
wsprintfW(wszErrorMessage, L"Exception Processing Message %lx Parameters %lx %lx %lx %lx",
|
|
phemsg->Status, adwParameterVector[0], adwParameterVector[1],
|
|
adwParameterVector[2], adwParameterVector[3]);
|
|
|
|
RtlFreeUnicodeString(&usMessage);
|
|
RtlCreateUnicodeString(&usMessage, wszErrorMessage);
|
|
UserAssert(usMessage.MaximumLength <= sizeof(wszErrorMessage));
|
|
|
|
}
|
|
}
|
|
|
|
|
|
CleanUpAndSaveParams:
|
|
if (hClientProcess != NULL) {
|
|
NtClose(hClientProcess);
|
|
}
|
|
|
|
/*
|
|
* Free string parameters. Note that we're supposed to call
|
|
* RtlFreeAnsiString since these were allocated by
|
|
* RtlUnicodeStringToAnsiString, but we only saved the buffers.
|
|
*/
|
|
if (dwStringsToFreeMask != 0) {
|
|
for (dwCounter = 0; dwCounter < phemsg->NumberOfParameters; dwCounter++) {
|
|
if (dwStringsToFreeMask & (1 << dwCounter)) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, (PVOID)adwParameterVector[dwCounter]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Save MessageBox Parameters in phi to be used and freed later.
|
|
*/
|
|
if (fErrorIsFromSystem) {
|
|
phi->dwHEIFFlags |= HEIF_SYSTEMERROR;
|
|
}
|
|
|
|
phi->usText = usMessage;
|
|
phi->usCaption = usCaption;
|
|
phi->dwMBFlags = dwMBFlags;
|
|
phi->dwTimeout = dwTimeout;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* CheckShellHardError
|
|
*
|
|
* This function tries to send the hard error to the HardErrorHandler window
|
|
* to see if we can avoid handling it here. If so, we avoid handling it.
|
|
*
|
|
* History:
|
|
* 03-29-01 BobDay Added
|
|
\***************************************************************************/
|
|
BOOL CheckShellHardError(
|
|
PHARDERRORINFO phi,
|
|
int *pidResponse)
|
|
{
|
|
HANDLE hKey;
|
|
UNICODE_STRING UnicodeString;
|
|
OBJECT_ATTRIBUTES OA;
|
|
LONG Status;
|
|
BYTE Buf[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(DWORD)];
|
|
DWORD cbSize;
|
|
DWORD dwShellErrorMode = 0;
|
|
BOOL fHandledThisMessage = FALSE;
|
|
HWND hwndTaskman;
|
|
|
|
//
|
|
// Shell can handle only the non-waiting case.
|
|
//
|
|
if (!(phi->dwHEIFFlags & HEIF_NOWAIT)) {
|
|
return FALSE;
|
|
}
|
|
|
|
RtlInitUnicodeString(&UnicodeString,
|
|
L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Windows");
|
|
InitializeObjectAttributes(&OA,
|
|
&UnicodeString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = NtOpenKey(&hKey, KEY_READ, &OA);
|
|
if (NT_SUCCESS(Status)) {
|
|
RtlInitUnicodeString(&UnicodeString, L"ShellErrorMode");
|
|
Status = NtQueryValueKey(hKey,
|
|
&UnicodeString,
|
|
KeyValuePartialInformation,
|
|
(PKEY_VALUE_PARTIAL_INFORMATION)Buf,
|
|
sizeof(Buf),
|
|
&cbSize);
|
|
if (NT_SUCCESS(Status)) {
|
|
dwShellErrorMode = *((PDWORD)((PKEY_VALUE_PARTIAL_INFORMATION)Buf)->Data);
|
|
}
|
|
|
|
NtClose(hKey);
|
|
}
|
|
|
|
//
|
|
// 1 = try shell ("HardErrorHandler" window).
|
|
//
|
|
if (dwShellErrorMode != 1) {
|
|
return FALSE;
|
|
}
|
|
|
|
hwndTaskman = GetTaskmanWindow();
|
|
if (hwndTaskman != NULL) {
|
|
BYTE *lpData;
|
|
UINT cbTitle = 0;
|
|
UINT cbText = 0;
|
|
UINT cbData;
|
|
|
|
//
|
|
// Build up a copy data buffer to send to the hard error handler
|
|
// window.
|
|
//
|
|
cbTitle = phi->usCaption.Length + sizeof(WCHAR);
|
|
cbText = phi->usText.Length + sizeof(WCHAR);
|
|
cbData = sizeof(HARDERRORDATA) + cbTitle + cbText;
|
|
lpData = (BYTE *)LocalAlloc(LPTR,cbData);
|
|
if (lpData) {
|
|
COPYDATASTRUCT cd;
|
|
PHARDERRORDATA phed = (PHARDERRORDATA)lpData;
|
|
PWSTR pwszTitle = (PWSTR)(phed + 1);
|
|
PWSTR pwszText = (WCHAR *)((BYTE *)pwszTitle + cbTitle);
|
|
LRESULT lResult;
|
|
BOOL fSentMessage;
|
|
|
|
cd.dwData = RegisterWindowMessage(TEXT(COPYDATA_HARDERROR));
|
|
cd.cbData = cbData;
|
|
cd.lpData = lpData;
|
|
|
|
phed->dwSize = sizeof(HARDERRORDATA);
|
|
phed->dwError = phi->pmsg->Status;
|
|
phed->dwFlags = phi->dwMBFlags;
|
|
phed->uOffsetTitleW = 0;
|
|
phed->uOffsetTextW = 0;
|
|
|
|
if (cbTitle != 0) {
|
|
phed->uOffsetTitleW = (UINT)((BYTE *)pwszTitle - (BYTE *)phed);
|
|
RtlCopyMemory(pwszTitle, phi->usCaption.Buffer, cbTitle-sizeof(WCHAR));
|
|
pwszTitle[cbTitle/sizeof(WCHAR)-1] = (WCHAR)0;
|
|
}
|
|
if (cbText != 0) {
|
|
phed->uOffsetTextW = (UINT)((BYTE *)pwszText - (BYTE *)phed);
|
|
RtlCopyMemory(pwszText, phi->usText.Buffer, cbText-sizeof(WCHAR));
|
|
pwszText[cbText/sizeof(WCHAR)-1] = (WCHAR)0;
|
|
}
|
|
|
|
//
|
|
// Send the message. If the app doesn't respond in a short
|
|
// amount of time, blow it off and assume it's unhandled.
|
|
//
|
|
lResult = 0;
|
|
fSentMessage = (BOOL)SendMessageTimeout(hwndTaskman, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cd, SMTO_ABORTIFHUNG, 3000, &lResult);
|
|
|
|
//
|
|
// Has to both be handled and return non-zero.
|
|
//
|
|
if (fSentMessage && lResult != 0) {
|
|
fHandledThisMessage = TRUE;
|
|
*pidResponse = IDOK;
|
|
}
|
|
|
|
LocalFree(lpData);
|
|
}
|
|
}
|
|
|
|
return fHandledThisMessage;
|
|
}
|
|
|
|
#if DBG
|
|
VOID DBGCheckForHardError(
|
|
PHARDERRORINFO phi)
|
|
{
|
|
PHARDERRORINFO *pphi;
|
|
|
|
/*
|
|
* Let's make sure it was unlinked.
|
|
*/
|
|
pphi = &gphiList;
|
|
while (*pphi != phi && *pphi != NULL) {
|
|
UserAssert(!((*pphi)->dwHEIFFlags & (HEIF_ACTIVE | HEIF_NUKED)));
|
|
pphi = &(*pphi)->phiNext;
|
|
}
|
|
|
|
UserAssert(*pphi == NULL);
|
|
}
|
|
#else
|
|
#define DBGCheckForHardError(phi)
|
|
#endif
|
|
|
|
/***************************************************************************\
|
|
* HardErrorHandler
|
|
*
|
|
* This routine processes hard error requests from the CSR exception port.
|
|
*
|
|
* History:
|
|
* 07-03-91 JimA Created.
|
|
\***************************************************************************/
|
|
VOID HardErrorHandler(
|
|
VOID)
|
|
{
|
|
UINT idResponse = 0;
|
|
PHARDERRORINFO phi, *pphi;
|
|
DWORD dwResponse;
|
|
DESKRESTOREDATA drdRestore;
|
|
BOOL fNuked;
|
|
UINT uHECRet;
|
|
DWORD dwCmd;
|
|
HANDLE hThread;
|
|
int aidButton[3], cButtons;
|
|
LPWSTR apstrButton[3];
|
|
MSGBOXDATA mbd;
|
|
BOOL bDoBlock;
|
|
PCTXHARDERRORINFO pCtxHEInfo = NULL;
|
|
MSG msg;
|
|
|
|
#if DBG
|
|
/*
|
|
* We should have only one error handler at the time.
|
|
*/
|
|
static long glReentered = -1;
|
|
UserAssert(InterlockedIncrement(&glReentered) == 0);
|
|
#endif
|
|
|
|
if (ISTS()) {
|
|
bDoBlock = (gbExitInProgress || (HEC_ERROR == NtUserHardErrorControl(HardErrorSetup, NULL, NULL)));
|
|
} else {
|
|
bDoBlock = (HEC_ERROR == NtUserHardErrorControl(HardErrorSetup, NULL, NULL));
|
|
}
|
|
|
|
drdRestore.pdeskRestore = NULL;
|
|
|
|
if (bDoBlock) {
|
|
/*
|
|
* We failed to set up to process hard errors. Acknowledge all
|
|
* pending errors as NotHandled.
|
|
*/
|
|
EnterCrit();
|
|
while (gphiList != NULL) {
|
|
phi = gphiList;
|
|
#ifdef PRERELEASE
|
|
hiLastProcessed = *phi;
|
|
#endif
|
|
gphiList = phi->phiNext;
|
|
LeaveCrit();
|
|
ReplyHardError(phi, ResponseNotHandled);
|
|
EnterCrit();
|
|
}
|
|
UserAssert(InterlockedDecrement(&glReentered) < 0);
|
|
UserAssert(gdwHardErrorThreadId == HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread));
|
|
gdwHardErrorThreadId = 0;
|
|
LeaveCrit();
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Process all hard error requests.
|
|
*/
|
|
|
|
for (;;) {
|
|
/*
|
|
* Grab the next request (for the current desktop)
|
|
* If we're done, reset gdwHardErrorThreadId so any request
|
|
* after this point will be handled by someone else
|
|
*/
|
|
EnterCrit();
|
|
phi = gphiList;
|
|
if (phi == NULL) {
|
|
UserAssert(InterlockedDecrement(&glReentered) < 0);
|
|
UserAssert(gdwHardErrorThreadId == HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread));
|
|
gdwHardErrorThreadId = 0;
|
|
} else {
|
|
while ((phi != NULL) && (phi->dwHEIFFlags & HEIF_WRONGDESKTOP)) {
|
|
phi = phi->phiNext;
|
|
}
|
|
if (phi != NULL) {
|
|
#ifdef PRERELEASE
|
|
hiLastProcessed = *phi;
|
|
#endif
|
|
/*
|
|
* We're going to show this one.
|
|
*/
|
|
phi->dwHEIFFlags |= HEIF_ACTIVE;
|
|
} else {
|
|
/*
|
|
* We have some requests pending but they are not
|
|
* for the current desktop. Let's wait for another
|
|
* request (WM_NULL posted) or a desktop switch (PostQuitMessage)
|
|
*/
|
|
LeaveCrit();
|
|
MsgWaitForMultipleObjects(0, NULL, FALSE, INFINITE, QS_POSTMESSAGE);
|
|
PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
|
|
CheckDefaultDesktop();
|
|
continue;
|
|
}
|
|
}
|
|
LeaveCrit();
|
|
/*
|
|
* If no messages are pending, we're done.
|
|
*/
|
|
if (phi == NULL) {
|
|
NtUserHardErrorControl(HardErrorCleanup, NULL, NULL);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* The Boost routine can mess with the list, so must get citrix info now.
|
|
*/
|
|
if (ISTS()) {
|
|
pCtxHEInfo = phi->pCtxHEInfo;
|
|
if (gbExitInProgress) {
|
|
dwResponse = ResponseOk;
|
|
goto Reply;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get win32k attach parameters.
|
|
*/
|
|
dwCmd = (phi->dwMBFlags & MB_DEFAULT_DESKTOP_ONLY) ? HardErrorAttachUser : HardErrorAttach;
|
|
hThread = (phi->pthread != NULL) ? phi->pthread->ThreadHandle : NULL;
|
|
/*
|
|
* We have already handled the MB_SERVICE_NOTIFICATION flags, so
|
|
* clear it to prevent recursion. Also, don't let hard error boxes
|
|
* steal the foreground.
|
|
*/
|
|
phi->dwMBFlags &= ~(MB_SERVICE_NOTIFICATION | MB_SETFOREGROUND | MB_SYSTEMMODAL);
|
|
/*
|
|
* If this is a VDM error, figure out buttons, default id, style, etc.
|
|
*/
|
|
if (phi->dwHEIFFlags & HEIF_VDMERROR) {
|
|
int i;
|
|
WORD rgwBtn[3], wBtn;
|
|
|
|
/*
|
|
* Initialize MSGBOXDATA with the information we have already
|
|
* figured out.
|
|
*/
|
|
RtlZeroMemory(&mbd, sizeof(MSGBOXDATA));
|
|
mbd.cbSize = sizeof(MSGBOXPARAMS);
|
|
mbd.lpszText = phi->usText.Buffer;
|
|
mbd.lpszCaption = phi->usCaption.Buffer;
|
|
mbd.dwTimeout = INFINITE;
|
|
|
|
/*
|
|
* phi->dwVDMParam0 = (fForWOW << 16) | wBtn1;
|
|
* phi->dwVDMParam1 = (wBtn2 << 16) | wBtn3;
|
|
* Right now, only WOW does this. If NTVDM does it, fForWOW
|
|
* will be false.
|
|
*/
|
|
rgwBtn[0] = LOWORD(phi->dwVDMParam0);
|
|
rgwBtn[1] = HIWORD(phi->dwVDMParam1);
|
|
rgwBtn[2] = LOWORD(phi->dwVDMParam1);
|
|
cButtons = 0;
|
|
for (i = 0; i < 3; i++) {
|
|
wBtn = rgwBtn[i] & ~SEB_DEFBUTTON;
|
|
if (wBtn && wBtn <= MAX_SEB_STYLES) {
|
|
apstrButton[cButtons] = MB_GetString(wBtn-1);
|
|
aidButton[cButtons] = i + 1;
|
|
if (rgwBtn[i] & SEB_DEFBUTTON) {
|
|
mbd.DefButton = cButtons;
|
|
}
|
|
if (wBtn == SEB_CANCEL) {
|
|
mbd.CancelId = cButtons;
|
|
}
|
|
cButtons++;
|
|
}
|
|
}
|
|
mbd.dwStyle = MB_TOPMOST;
|
|
if ((cButtons != 1) || (aidButton[0] != 1)) {
|
|
mbd.dwStyle |= MB_OKCANCEL;
|
|
}
|
|
mbd.ppszButtonText = apstrButton;
|
|
mbd.pidButton = aidButton;
|
|
mbd.cButtons = cButtons;
|
|
}
|
|
|
|
/*
|
|
* Attach to win32k and show the dialog. If we switch desktops,
|
|
* (loop and) show it on the new desktop (if applicable).
|
|
*/
|
|
do {
|
|
phi->pmsg->Response = ResponseNotHandled;
|
|
|
|
uHECRet = NtUserHardErrorControl(dwCmd, hThread, &drdRestore);
|
|
|
|
if (uHECRet == HEC_SUCCESS) {
|
|
if (phi->dwHEIFFlags & HEIF_VDMERROR) {
|
|
idResponse = SoftModalMessageBox(&mbd);
|
|
} else {
|
|
/*
|
|
* Bring up the message box. Or in MB_TOPMOST so it
|
|
* comes up on top. We want to preserve the
|
|
* MB_DEFAULT_DESKTOP_ONLY flag but don't want to pass
|
|
* it to MessageBox or we'll recurse due to a
|
|
* compatibility hack.
|
|
*/
|
|
if (CheckShellHardError(phi, &idResponse) == FALSE) {
|
|
DWORD dwTimeout;
|
|
if (pCtxHEInfo && pCtxHEInfo->Timeout != 0 && pCtxHEInfo->Timeout != -1) {
|
|
dwTimeout = pCtxHEInfo->Timeout * 1000;
|
|
} else {
|
|
dwTimeout = phi->dwTimeout;
|
|
}
|
|
idResponse = MessageBoxTimeout(NULL, phi->usText.Buffer, phi->usCaption.Buffer,
|
|
(phi->dwMBFlags | MB_TOPMOST) & ~MB_DEFAULT_DESKTOP_ONLY, 0, dwTimeout);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Restore hard error handler desktop; this will also tell
|
|
* us if the input desktop has changed. If so, we want to
|
|
* bring the error box again on the new desktop.
|
|
*/
|
|
uHECRet = NtUserHardErrorControl(HardErrorDetach, NULL, &drdRestore);
|
|
|
|
if (ISTS()) {
|
|
/*
|
|
* Really a citrix message.
|
|
*/
|
|
if (uHECRet != HEC_DESKTOPSWITCH && pCtxHEInfo != NULL) {
|
|
pCtxHEInfo->Response = idResponse;
|
|
|
|
/*
|
|
* Check for message box timeout.
|
|
*/
|
|
if (idResponse == IDTIMEOUT) {
|
|
uHECRet = HEC_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if (idResponse == IDTIMEOUT) {
|
|
idResponse = ResponseNotHandled;
|
|
}
|
|
|
|
if (idResponse >= ARRAY_SIZE(dwResponses)) {
|
|
RIPMSGF1(RIP_WARNING, "Index idResponse: %d is out of range.", idResponse);
|
|
idResponse = 0;
|
|
}
|
|
if (dwResponses[idResponse] == ResponseNotHandled &&
|
|
uHECRet == HEC_DESKTOPSWITCH && gSessionId == 0) {
|
|
|
|
RIPMSGF2(RIP_WARNING,
|
|
"Abort harderror, idResponse 0x%x, uHECRet 0x%x",
|
|
idResponse,
|
|
uHECRet);
|
|
|
|
break;
|
|
}
|
|
} else if (idResponse == IDTIMEOUT) {
|
|
idResponse = ResponseNotHandled;
|
|
}
|
|
} else {
|
|
idResponse = ResponseNotHandled;
|
|
}
|
|
|
|
if (idResponse >= ARRAY_SIZE(dwResponses)) {
|
|
RIPMSGF1(RIP_WARNING, "Index idResponse: %d is out of range.", idResponse);
|
|
idResponse = 0;
|
|
}
|
|
dwResponse = dwResponses[idResponse];
|
|
|
|
/*
|
|
* If we don't want to reshow this box, we're done.
|
|
*/
|
|
if (uHECRet != HEC_DESKTOPSWITCH) {
|
|
break;
|
|
} else {
|
|
/*
|
|
* We've switched desktops; if we're in the default one now,
|
|
* then we can show all MB_DEFAULT_DESKTOP_ONLY requests.
|
|
*/
|
|
CheckDefaultDesktop();
|
|
}
|
|
|
|
/*
|
|
* If BoostHardError nuked it, don't re-show it.
|
|
*/
|
|
EnterCrit();
|
|
fNuked = (phi->dwHEIFFlags & HEIF_NUKED);
|
|
LeaveCrit();
|
|
} while (!fNuked);
|
|
|
|
/*
|
|
* If we didn't show this box because we're not in the default
|
|
* desktop, mark this phi and continue.
|
|
*/
|
|
if (uHECRet == HEC_WRONGDESKTOP) {
|
|
UserAssert(phi->dwMBFlags & MB_DEFAULT_DESKTOP_ONLY);
|
|
if (ISTS() && phi->pCtxHEInfo) {
|
|
if (phi->pCtxHEInfo->DoNotWaitForCorrectDesktop) {
|
|
phi->pCtxHEInfo->Response = IDTIMEOUT;
|
|
dwResponse = ResponseNotHandled;
|
|
goto Reply;
|
|
}
|
|
}
|
|
EnterCrit();
|
|
COPY_FLAG(phi->dwHEIFFlags, HEIF_WRONGDESKTOP, HEIF_ACTIVE | HEIF_WRONGDESKTOP);
|
|
LeaveCrit();
|
|
continue;
|
|
}
|
|
|
|
Reply:
|
|
/*
|
|
* We're done with this phi. Unlink it if BoostHardError hasn't done
|
|
* so already. If unlinked, it is marked as nuked.
|
|
*/
|
|
EnterCrit();
|
|
UserAssert(phi->dwHEIFFlags & HEIF_ACTIVE);
|
|
fNuked = (phi->dwHEIFFlags & HEIF_NUKED);
|
|
if (!fNuked) {
|
|
pphi = &gphiList;
|
|
while ((*pphi != phi) && (*pphi != NULL)) {
|
|
pphi = &(*pphi)->phiNext;
|
|
}
|
|
UserAssert(*pphi != NULL);
|
|
*pphi = phi->phiNext;
|
|
} else {
|
|
DBGCheckForHardError(phi);
|
|
}
|
|
|
|
if (phi->pCtxHEInfo) {
|
|
|
|
/*
|
|
* Clean up
|
|
*/
|
|
HardErrorRemove(phi->pCtxHEInfo);
|
|
|
|
/*
|
|
* Done
|
|
*/
|
|
phi->pCtxHEInfo = NULL;
|
|
}
|
|
|
|
LeaveCrit();
|
|
|
|
/*
|
|
* Save the response, reply and free phi
|
|
*/
|
|
ReplyHardError(phi, (fNuked ? ResponseNotHandled : dwResponse));
|
|
}
|
|
|
|
/*
|
|
* Nobody should break out of the loop.
|
|
*/
|
|
UserAssert(FALSE);
|
|
}
|
|
|
|
|
|
LPWSTR RtlLoadStringOrError(
|
|
HANDLE hModule,
|
|
UINT wID,
|
|
LPWSTR lpDefault,
|
|
PBOOL pAllocated,
|
|
BOOL bAnsi
|
|
)
|
|
{
|
|
LPTSTR lpsz;
|
|
int cch;
|
|
LPWSTR lpw;
|
|
PMESSAGE_RESOURCE_ENTRY MessageEntry;
|
|
NTSTATUS Status;
|
|
|
|
cch = 0;
|
|
lpw = NULL;
|
|
|
|
Status = RtlFindMessage((PVOID)hModule, (ULONG_PTR)RT_MESSAGETABLE,
|
|
0, wID, &MessageEntry);
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
/*
|
|
* Return two fewer chars so the crlf in the message will be
|
|
* stripped out.
|
|
*/
|
|
cch = wcslen((PWCHAR)MessageEntry->Text) - 2;
|
|
lpsz = (LPWSTR)MessageEntry->Text;
|
|
|
|
if (bAnsi) {
|
|
int ich;
|
|
|
|
/*
|
|
* Add one to zero terminate then force the termination.
|
|
*/
|
|
ich = WCSToMB(lpsz, cch+1, (CHAR **)&lpw, -1, TRUE);
|
|
if (lpw) {
|
|
((LPSTR)lpw)[ich - 1] = 0;
|
|
}
|
|
} else {
|
|
lpw = (LPWSTR)LocalAlloc(LMEM_ZEROINIT, (cch + 1) * sizeof(WCHAR));
|
|
if (lpw) {
|
|
/*
|
|
* Copy the string into the buffer.
|
|
*/
|
|
RtlCopyMemory(lpw, lpsz, cch * sizeof(WCHAR));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!lpw) {
|
|
lpw = lpDefault;
|
|
*pAllocated = FALSE;
|
|
} else {
|
|
*pAllocated = TRUE;
|
|
}
|
|
|
|
return lpw;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* HardErrorWorkerThread
|
|
*
|
|
* Worker thread to handle hard error requests.
|
|
*
|
|
* History:
|
|
* 05-01-98 JerrySh Created.
|
|
\***************************************************************************/
|
|
NTSTATUS HardErrorWorkerThread(
|
|
PVOID ThreadParameter)
|
|
{
|
|
PCSR_THREAD pt;
|
|
UNREFERENCED_PARAMETER(ThreadParameter);
|
|
|
|
pt = CsrConnectToUser();
|
|
ProcessHardErrorRequest(FALSE);
|
|
if (pt) {
|
|
CsrDereferenceThread(pt);
|
|
}
|
|
|
|
#ifdef PRERELEASE
|
|
NtUserHardErrorControl(HardErrorCheckOnDesktop, NULL, NULL);
|
|
#endif
|
|
|
|
UserExitWorkerThread(STATUS_SUCCESS);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* ProcessHardErrorRequest
|
|
*
|
|
* Figures out who should process the hard error. There are 3 possible cases.
|
|
* - If there is already a hard error thread, hand it off to him.
|
|
* - If not and we don't want to wait, create a worker thread to deal with it.
|
|
* - If we want to wait or thread creation fails, deal with it ourselves.
|
|
*
|
|
* History:
|
|
* 05-01-98 JerrySh Created.
|
|
\***************************************************************************/
|
|
VOID ProcessHardErrorRequest(
|
|
BOOL fNewThread)
|
|
{
|
|
NTSTATUS Status;
|
|
CLIENT_ID ClientId;
|
|
HANDLE hThread;
|
|
|
|
EnterCrit();
|
|
|
|
/*
|
|
* If there's already a hard error handler, make sure he's awake.
|
|
*/
|
|
if (gdwHardErrorThreadId) {
|
|
DWORD dwHardErrorHandler = gdwHardErrorThreadId;
|
|
LeaveCrit();
|
|
PostThreadMessage(dwHardErrorHandler, WM_NULL, 0, 0);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Create a worker thread to handle the hard error.
|
|
*/
|
|
if (fNewThread) {
|
|
LeaveCrit();
|
|
Status = RtlCreateUserThread(NtCurrentProcess(),
|
|
NULL,
|
|
TRUE,
|
|
0,
|
|
0,
|
|
0,
|
|
HardErrorWorkerThread,
|
|
NULL,
|
|
&hThread,
|
|
&ClientId);
|
|
if (NT_SUCCESS(Status)) {
|
|
CsrAddStaticServerThread(hThread, &ClientId, 0);
|
|
NtResumeThread(hThread, NULL);
|
|
return;
|
|
}
|
|
EnterCrit();
|
|
}
|
|
|
|
/*
|
|
* Let this thread handle the hard error.
|
|
*/
|
|
gdwHardErrorThreadId = HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread);
|
|
LeaveCrit();
|
|
HardErrorHandler();
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* UserHardError
|
|
*
|
|
* Called from CSR to pop up hard error messages.
|
|
*
|
|
* History:
|
|
* 03/12/97 GerardoB Rewritten to support OptionOkNoWait
|
|
* 07-03-91 JimA Created.
|
|
\***************************************************************************/
|
|
VOID UserHardError(
|
|
PCSR_THREAD pt,
|
|
PHARDERROR_MSG pmsg)
|
|
{
|
|
UserHardErrorEx(pt, pmsg, NULL);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* UserHardErrorEx
|
|
*
|
|
* Called from CSR to pop up hard error messages.
|
|
*
|
|
* History:
|
|
* 07-03-91 JimA Created.
|
|
\***************************************************************************/
|
|
VOID UserHardErrorEx(
|
|
PCSR_THREAD pt,
|
|
PHARDERROR_MSG pmsg,
|
|
PCTXHARDERRORINFO pCtxHEInfo)
|
|
{
|
|
BOOL fClientPort, fNoWait, fMsgBox, fLogEvent;
|
|
PHARDERRORINFO phi, *pphiLast;
|
|
HANDLE hEvent;
|
|
DWORD dwReportMode, dwResponse;
|
|
|
|
UserAssert((ULONG)pmsg->NumberOfParameters <= MAXIMUM_HARDERROR_PARAMETERS);
|
|
|
|
/*
|
|
* Allocate memory to queue request.
|
|
*/
|
|
phi = (PHARDERRORINFO)LocalAlloc(LPTR, sizeof(HARDERRORINFO));
|
|
if (phi == NULL) {
|
|
goto ErrorExit;
|
|
}
|
|
phi->pthread = pt;
|
|
|
|
/*
|
|
* Set up citrix specific stuff.
|
|
*/
|
|
if (ISTS()) {
|
|
phi->pCtxHEInfo = pCtxHEInfo;
|
|
}
|
|
|
|
/*
|
|
* Determine reply type.
|
|
*/
|
|
fClientPort = ((pt != NULL) && (pt->Process->ClientPort != NULL));
|
|
fNoWait = (pmsg->ValidResponseOptions == OptionOkNoWait);
|
|
|
|
/*
|
|
* Capture HARDERROR_MSG data or create wait event as needed
|
|
*/
|
|
if (fClientPort || fNoWait) {
|
|
phi->pmsg = (PHARDERROR_MSG)LocalAlloc(LPTR, pmsg->h.u1.s1.TotalLength);
|
|
if (phi->pmsg == NULL) {
|
|
goto ErrorExit;
|
|
}
|
|
|
|
phi->dwHEIFFlags |= HEIF_ALLOCATEDMSG;
|
|
RtlCopyMemory(phi->pmsg, pmsg, pmsg->h.u1.s1.TotalLength);
|
|
hEvent = NULL;
|
|
/*
|
|
* Set the magic response value (-1) to let CsrApiRequestThread know
|
|
* that we'll take care of dereferencing and replying.
|
|
*/
|
|
if (pt != NULL) {
|
|
phi->dwHEIFFlags |= HEIF_DEREFTHREAD;
|
|
}
|
|
pmsg->Response = (ULONG)-1;
|
|
if (fNoWait) {
|
|
phi->dwHEIFFlags |= HEIF_NOWAIT;
|
|
phi->pmsg->ValidResponseOptions = OptionOk;
|
|
}
|
|
} else {
|
|
phi->pmsg = pmsg;
|
|
/*
|
|
* There is no reply port but we need to wait; create an event.
|
|
*/
|
|
hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if (hEvent == NULL) {
|
|
goto ErrorExit;
|
|
}
|
|
phi->hEventHardError = hEvent;
|
|
}
|
|
|
|
/*
|
|
* Build hard error title, message and flags. Then log the event.
|
|
*/
|
|
GetHardErrorText(phi);
|
|
|
|
/*
|
|
* Reply now if we're not going to wait.
|
|
*/
|
|
if (fNoWait) {
|
|
phi->dwHEIFFlags |= HEIF_REPLIED;
|
|
phi->pmsg->Response = ResponseOk;
|
|
if (fClientPort) {
|
|
NtReplyPort(pt->Process->ClientPort, (PPORT_MESSAGE)phi->pmsg);
|
|
} else {
|
|
/*
|
|
* Must let CsrApiRequestThread reply since we don't have a port
|
|
*/
|
|
pmsg->Response = ResponseOk;
|
|
/*
|
|
* If we have a thread, reference it because we're telling
|
|
* CsrApiRequestThread that we're done with it.
|
|
*/
|
|
if (pt != NULL) {
|
|
/*
|
|
* Later5.0 GerardoB. Let's stop to see how this happens.
|
|
*/
|
|
UserAssert(pt == NULL);
|
|
CsrReferenceThread(pt);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Register the event if we haven't done so already. Since
|
|
* RegisterEventSource is supported by a service, we must not hold any
|
|
* locks while making this call. Hence we might have several threads
|
|
* registering the event simultaneously.
|
|
*/
|
|
fLogEvent = (gEventSource != NULL);
|
|
if (!fLogEvent) {
|
|
HANDLE hEventSource = RegisterEventSourceW(NULL, L"Application Popup");
|
|
|
|
/*
|
|
* Save the first handle, deregister all others.
|
|
*/
|
|
if (InterlockedCompareExchangePointer(&gEventSource, hEventSource, NULL) == NULL) {
|
|
/*
|
|
* This is the first handle. If valid, we can log events.
|
|
*/
|
|
fLogEvent = (hEventSource != NULL);
|
|
} else {
|
|
/*
|
|
* We had already saved another handle (so we can log events).
|
|
* Deregister this one.
|
|
*/
|
|
if (hEventSource != NULL) {
|
|
UserVerify(DeregisterEventSource(hEventSource));
|
|
}
|
|
fLogEvent = TRUE;
|
|
}
|
|
}
|
|
|
|
dwReportMode = fLogEvent ? GetErrorMode() : 0;
|
|
if (fLogEvent) {
|
|
LogErrorPopup(phi->usCaption.Buffer, phi->usText.Buffer);
|
|
}
|
|
|
|
/*
|
|
* Determine if we need to display a message box.
|
|
*/
|
|
if ((phi->pmsg->Status == STATUS_SERVICE_NOTIFICATION) || (dwReportMode == 0)) {
|
|
fMsgBox = TRUE;
|
|
} else if (phi->pmsg->Status == STATUS_VDM_HARD_ERROR) {
|
|
fMsgBox = (dwReportMode == 1);
|
|
if (!fMsgBox) {
|
|
dwResponse = ResponseOk;
|
|
}
|
|
} else {
|
|
fMsgBox = ((dwReportMode == 1) && !(phi->dwHEIFFlags & HEIF_SYSTEMERROR));
|
|
if (!fMsgBox) {
|
|
UserAssert((UINT)phi->pmsg->ValidResponseOptions < ARRAY_SIZE(dwResponseDefault));
|
|
dwResponse = dwResponseDefault[phi->pmsg->ValidResponseOptions];
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we don't have to display a message box, we're done.
|
|
*/
|
|
if (!fMsgBox) {
|
|
goto DontNeedErrorHandler;
|
|
}
|
|
|
|
/*
|
|
* We want to display a message box. Queue the request and go for it.
|
|
*
|
|
* Never mind if the process has terminated. Got to check this holding the
|
|
* critical section to make sure that no other thread makes it to BoostHardError
|
|
* before we add this phi to the list.
|
|
*/
|
|
EnterCrit();
|
|
if ((pt != NULL) && (pt->Process->Flags & CSR_PROCESS_TERMINATED)) {
|
|
LeaveCrit();
|
|
DontNeedErrorHandler:
|
|
ReplyHardError(phi, dwResponse);
|
|
if (hEvent != NULL) {
|
|
NtClose(hEvent);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Add it to the end of the list.
|
|
*/
|
|
pphiLast = &gphiList;
|
|
while (*pphiLast != NULL) {
|
|
pphiLast = &(*pphiLast)->phiNext;
|
|
}
|
|
*pphiLast = phi;
|
|
LeaveCrit();
|
|
|
|
/*
|
|
* Process the hard error request. If this is a NoWait request and there
|
|
* is no reply port, then we'll try to launch a new worker thread so this
|
|
* one can return.
|
|
*/
|
|
ProcessHardErrorRequest(fNoWait && !fClientPort);
|
|
|
|
/*
|
|
* If there is an event handle, wait for it.
|
|
*/
|
|
if (hEvent != NULL) {
|
|
NtWaitForSingleObject(hEvent, FALSE, NULL);
|
|
NtClose(hEvent);
|
|
}
|
|
return;
|
|
|
|
ErrorExit:
|
|
if (phi != NULL) {
|
|
FreePhi(phi);
|
|
}
|
|
pmsg->Response = ResponseNotHandled;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* BoostHardError
|
|
*
|
|
* If one or more hard errors exist for the specified process, remove them
|
|
* from the list if forced, otherwise bring the first one to the top of the
|
|
* hard error list and display it. Return TRUE if there is a hard error.
|
|
*
|
|
* History:
|
|
* 11-02-91 JimA Created.
|
|
\***************************************************************************/
|
|
BOOL BoostHardError(
|
|
ULONG_PTR dwProcessId,
|
|
DWORD dwCode)
|
|
{
|
|
DESKRESTOREDATA drdRestore;
|
|
PHARDERRORINFO phi, *pphi;
|
|
BOOL fHasError = FALSE;
|
|
|
|
EnterCrit();
|
|
/*
|
|
* If the list is empty, nothing do to here.
|
|
*/
|
|
if (gphiList == NULL) {
|
|
LeaveCrit();
|
|
return FALSE;
|
|
}
|
|
|
|
drdRestore.pdeskRestore = NULL;
|
|
/*
|
|
* Walk the hard error list.
|
|
*/
|
|
pphi = &gphiList;
|
|
while (*pphi != NULL) {
|
|
/*
|
|
* If not not nuking all and not owned by dwProcessId, continue
|
|
* walking.
|
|
*/
|
|
if (dwProcessId != (ULONG_PTR)-1) {
|
|
if (((*pphi)->pthread == NULL)
|
|
|| ((ULONG_PTR)((*pphi)->pthread->ClientId.UniqueProcess) != dwProcessId)) {
|
|
|
|
pphi = &(*pphi)->phiNext;
|
|
continue;
|
|
}
|
|
} else {
|
|
UserAssert(dwCode == BHE_FORCE);
|
|
}
|
|
|
|
/*
|
|
* Got one so we want to return TRUE.
|
|
*/
|
|
fHasError = TRUE;
|
|
|
|
/*
|
|
* If nuking the request ...
|
|
*/
|
|
if (dwCode == BHE_FORCE) {
|
|
/*
|
|
* Unlink it from the list.
|
|
*/
|
|
phi = *pphi;
|
|
*pphi = phi->phiNext;
|
|
|
|
/*
|
|
* If this box is being shown right now, signal it to go away.
|
|
* Otherwise, nuke it.
|
|
*/
|
|
if (phi->dwHEIFFlags & HEIF_ACTIVE) {
|
|
DWORD dwHardErrorHandler = gdwHardErrorThreadId;
|
|
phi->dwHEIFFlags |= HEIF_NUKED;
|
|
LeaveCrit();
|
|
PostThreadMessage(dwHardErrorHandler, WM_QUIT, 0, 0);
|
|
} else {
|
|
/*
|
|
* Acknowledge the error as not handled, reply and free.
|
|
*/
|
|
LeaveCrit();
|
|
ReplyHardError(phi, ResponseNotHandled);
|
|
}
|
|
|
|
/*
|
|
* Restart the search because we left the crit sect.
|
|
*/
|
|
EnterCrit();
|
|
pphi = &gphiList;
|
|
} else if (dwCode == BHE_ACTIVATE) {
|
|
/*
|
|
* If it's active, find it and show it.
|
|
*/
|
|
phi = *pphi;
|
|
if (phi->dwHEIFFlags & HEIF_ACTIVE) {
|
|
HWND hwndError = NULL;
|
|
DWORD dwHardErrorHandler = gdwHardErrorThreadId;
|
|
|
|
LeaveCrit();
|
|
EnumThreadWindows(dwHardErrorHandler, FindWindowFromThread, (LPARAM)&hwndError);
|
|
|
|
if (hwndError != NULL &&
|
|
HEC_SUCCESS == NtUserHardErrorControl(HardErrorAttachNoQueue, NULL, &drdRestore)) {
|
|
|
|
SetForegroundWindow(hwndError);
|
|
|
|
NtUserHardErrorControl(HardErrorDetachNoQueue, NULL, &drdRestore);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* It's not active so move it to the head of the list to make it
|
|
* show up next.
|
|
*/
|
|
*pphi = phi->phiNext;
|
|
phi->phiNext = gphiList;
|
|
gphiList = phi;
|
|
break;
|
|
|
|
} else {
|
|
/*
|
|
* The caller just want to know if this process owns a hard error.
|
|
*/
|
|
break;
|
|
}
|
|
}
|
|
|
|
LeaveCrit();
|
|
|
|
/*
|
|
* Bug 284468. Wake up the hard error handler.
|
|
*/
|
|
if (dwCode == BHE_FORCE && gdwHardErrorThreadId != 0) {
|
|
PostThreadMessage(gdwHardErrorThreadId, WM_NULL, 0, 0);
|
|
}
|
|
|
|
return fHasError;
|
|
}
|