|
|
/**************************** 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; }
|