/*** 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 extern BOOLEAN gbExitInProgress; BOOL gfTimedOut = FALSE; VOID UserHardErrorEx( PCSR_THREAD pt, PHARDERROR_MSG pmsg, PCTXHARDERRORINFO pCtxHEInfo); VOID UserExitWorkerThread(VOID); VOID ProcessHardErrorRequest( BOOL fNewThread); 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 }; /* * Global timer id */ static UINT_PTR gTimerId = 0; /* * Citrix SendMessage entry point to harderror handler and cleanup routine */ VOID HardErrorInsert(PCSR_THREAD, PHARDERROR_MSG, PCTXHARDERRORINFO); VOID HardErrorRemove(PCTXHARDERRORINFO); FARPROC gfnRegisterEventSource; FARPROC gfnDeregisterEventSource; FARPROC gfnReportEvent; /* * UserRegisterEventSource * Dynamically link to the event functions in advapi32.dll and register the * event source. * History: * 04-13-98 JerrySh Created. */ HANDLE UserRegisterEventSource( PCWSTR pwszSourceName) { /* * If we haven't already dynamically linked to advadpi32.dll, do it now. */ if (gfnRegisterEventSource == NULL) { HINSTANCE hAdvApiDll; FARPROC fnRegisterEventSource; FARPROC fnDeregisterEventSource; FARPROC fnReportEvent; /* * Try to load the DLL and function pointers. */ if ((hAdvApiDll = LoadLibrary(L"advapi32.dll")) == NULL) { return NULL; } fnRegisterEventSource = GetProcAddress(hAdvApiDll, "RegisterEventSourceW"); fnDeregisterEventSource = GetProcAddress(hAdvApiDll, "DeregisterEventSource"); fnReportEvent = GetProcAddress(hAdvApiDll, "ReportEventW"); if (!fnRegisterEventSource || !fnDeregisterEventSource || !fnReportEvent) { FreeLibrary(hAdvApiDll); return NULL; } /* * Update the global function pointers if they're not set already. */ EnterCrit(); if (gfnRegisterEventSource == NULL) { gfnReportEvent = fnReportEvent; gfnDeregisterEventSource = fnDeregisterEventSource; // This must be last since we test it above gfnRegisterEventSource = fnRegisterEventSource; hAdvApiDll = NULL; } LeaveCrit(); /* * If another thread beat us to it, free the library. */ if (hAdvApiDll) { FreeLibrary(hAdvApiDll); } } /* * Let's be paranoid and verify we loaded everything correctly. */ UserAssert(gfnRegisterEventSource != NULL); UserAssert(gfnDeregisterEventSource != NULL); UserAssert(gfnReportEvent != NULL); /* * Call the real function. */ return (HANDLE)gfnRegisterEventSource(NULL, pwszSourceName); } /* * 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); gfnReportEvent(gEventSource, EVENTLOG_INFORMATION_TYPE, 0, STATUS_LOG_HARD_ERROR, NULL, sizeof(lps) / sizeof(*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; } } } } } DWORD GetErrorMode(VOID) /* * History: * 09-22-97 GerardoB Added Header */ { 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(); } /* * MsgBoxTimerFunc * This function gets called when a timer fires on a GUI message box * is up that has a timeout set for it. * This function cancels the message box by posting a WM_QUIT into the * message queue of the Citrix Message box thread. * ENTRY: * EXIT: */ VOID MsgBoxTimerFunc( HWND hWnd, UINT MessageType, UINT_PTR idEvent, DWORD TimeWhenCalled ) { BOOL RetVal; RIPMSG0(RIP_WARNING, "MsgBoxTimerFunc: timer poped"); // Post a WM_QUIT message into the messagebox threads queue if ( gdwHardErrorThreadId != 0 ) { gfTimedOut = TRUE; RetVal = PostThreadMessage(gdwHardErrorThreadId, WM_QUIT, 0, 0); } if(- !RetVal ) { RIPMSG0(RIP_WARNING, "MsgBoxTimerFunc: PostMessageFailed"); } UNREFERENCED_PARAMETER(hWnd); UNREFERENCED_PARAMETER(MessageType); UNREFERENCED_PARAMETER(idEvent); UNREFERENCED_PARAMETER(TimeWhenCalled); } /* * 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; 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); /* * 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 -- 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; } /* for (dwCounter... */ } /* if (phemsg->UnicodeStringParameterMask) */ /* * 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]); } else { RtlInitAnsiString(&asMessage, (PSTR)adwParameterVector[0]); RtlAnsiStringToUnicodeString(&usMessage, &asMessage, TRUE); } if (phemsg->UnicodeStringParameterMask & 0x2) { RtlInitUnicodeString(&usCaption, (PWSTR)adwParameterVector[1]); } else { RtlInitAnsiString(&asMessage, (PSTR)adwParameterVector[1]); RtlAnsiStringToUnicodeString(&usCaption, &asMessage, TRUE); } dwMBFlags = (DWORD)adwParameterVector[2] & ~MB_SERVICE_NOTIFICATION; 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]); } else { MBToWCS((LPSTR)adwParameterVector[2], -1, &pwszTitle, -1, TRUE); RtlCreateUnicodeString(&usCaption, pwszTitle); RtlFreeHeap(RtlProcessHeap(), 0, pwszTitle); } if (phemsg->UnicodeStringParameterMask & 0x8) { RtlInitUnicodeString(&usMessage, (PWSTR)adwParameterVector[3]); } else { MBToWCS((LPSTR)adwParameterVector[3], -1, &pwszMsg, -1, TRUE); 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 it's 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 = (PWSTR)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 (!fErrorIsFromSystem) */ 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 (*pszParsedCaption == '{') */ if (*pwszFormatString == (WCHAR)0) { pwszFormatString = wszUnknownHardError; } } /* if (!NT_SUCCESS(Status)) (Failed to read caption/format string) */ /* * 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 (uTitleLen != 0) */ } /* else if (hwndOwner == NULL) */ /* * 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 not 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)); } /* try */ } /* else if (phemsg->Status == STATUS_UNHANDLED_EXCEPTION) */ 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; return; } /* * HardErrorHandler * This routine processes hard error requests from the CSR exception port * History: * 07-03-91 JimA Created. */ VOID HardErrorHandler(void) { int idResponse; 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; 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) { /* * 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. * 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; /* * 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 */ /* * Really a Terminal Server message */ if (pCtxHEInfo != NULL) { /* * If timeout is not (-1), or 0, then we must queue * a system timer to call our timer callback function * to cancel the message box. */ if ((pCtxHEInfo->Timeout != (-1)) && (pCtxHEInfo->Timeout != 0)) { DWORD Timeout; if (((DWORD)pCtxHEInfo->Timeout) < (MAXLONG/1000)) { Timeout = (DWORD)pCtxHEInfo->Timeout * 1000L; } else { Timeout = MAXLONG; } gfTimedOut = FALSE; gTimerId = SetTimer((HWND)0, (UINT)0, Timeout, MsgBoxTimerFunc); if (gTimerId == 0) { RIPMSG0(RIP_WARNING, "HardErrorHandler: Could not set system timer"); } } RIPMSG2(RIP_WARNING, "HardErrorHandler: MessageBoxEx( %S, %S )", phi->usText.Buffer, phi->usCaption.Buffer); } else { RIPMSG2(RIP_WARNING, "HardErrorHandler: MessageBoxEx( %S, %S )", phi->usText.Buffer, phi->usCaption.Buffer); } idResponse = MessageBoxEx(NULL, phi->usText.Buffer, phi->usCaption.Buffer, (phi->dwMBFlags | MB_TOPMOST) & ~MB_DEFAULT_DESKTOP_ONLY, 0); } /* * 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()) { /* * Kill the timer if set */ if (gTimerId) { KillTimer((HWND)0, gTimerId); gTimerId = 0; } /* * Really a citrix message */ if (uHECRet != HEC_DESKTOPSWITCH && pCtxHEInfo != NULL) { /* * Check for message box timeout */ if (gfTimedOut) { uHECRet = HEC_SUCCESS; gfTimedOut = FALSE; pCtxHEInfo->Response = IDTIMEOUT; } else { pCtxHEInfo->Response = idResponse; } } if (dwResponses[idResponse] == ResponseNotHandled && uHECRet == HEC_DESKTOPSWITCH && gSessionId == 0) { RIPMSG2(RIP_WARNING, "HardErrorHandler: abort harderror, idResponse %u, uHECRet %u", idResponse, uHECRet); break; } } } else { idResponse = 0; } UserAssert((UINT)idResponse < ARRAY_SIZE(dwResponses)); dwResponse = dwResponses[idResponse]; /* * If we don't want to reshow this box, 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); EnterCrit(); COPY_FLAG(phi->dwHEIFFlags, HEIF_WRONGDESKTOP, HEIF_ACTIVE | HEIF_WRONGDESKTOP); LeaveCrit(); continue; } Reply: /* * We're done with this phi. * Unlink it if BoostHardError haven'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; } #if DBG else { /* * 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); } #endif 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)); } /* for (;;) */ /* * 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); ((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); } UserExitWorkerThread(); return STATUS_SUCCESS; } VOID ProcessHardErrorRequest(BOOL fNewThread) /* * 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. */ { 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(); } VOID UserHardError(PCSR_THREAD pt, PHARDERROR_MSG pmsg) /* * Called from CSR to pop up hard error messages * History: * 03/12/97 GerardoB Rewritten to support OptionOkNoWait * 07-03-91 JimA Created. */ { UserHardErrorEx(pt, pmsg, NULL); } VOID HardErrorInsert(PCSR_THREAD pt, PHARDERROR_MSG pmsg, PCTXHARDERRORINFO pCtxHEInfo) { UserHardErrorEx(pt, pmsg, pCtxHEInfo); } VOID UserHardErrorEx(PCSR_THREAD pt, PHARDERROR_MSG pmsg, PCTXHARDERRORINFO pCtxHEInfo) /* * Called from CSR to pop up hard error messages * History: * 07-03-91 JimA Created. */ { BOOL fClientPort, fNoWait, fMsgBox, fLogEvent; PHARDERRORINFO phi = NULL, *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->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; hEventSource = UserRegisterEventSource(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(gfnDeregisterEventSource(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. */ EnterCrit(); /* * 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. */ 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; /* continue */ } 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 { /* BHE_TEST */ /* * The caller just want to know if this process owns a hard error */ break; } } /* while (*pphi != NULL) */ LeaveCrit(); /* * Bug 284468. Wake up the hard error handler */ if (dwCode == BHE_FORCE && gdwHardErrorThreadId != 0) { PostThreadMessage(gdwHardErrorThreadId, WM_NULL, 0, 0); } return fHasError; }