Leaked source code of windows server 2003
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

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