Windows2000/private/ntos/w32/ntuser/server/harderr.c

1967 lines
60 KiB
C
Raw Normal View History

2001-01-01 00:00:00 +01:00
/*** 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>
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;
}