mirror of https://github.com/lianthony/NT4.0
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.
1608 lines
51 KiB
1608 lines
51 KiB
/**************************** Module Header ********************************\
|
|
* Module Name: harderr.c
|
|
*
|
|
* Copyright 1985-91, Microsoft Corporation
|
|
*
|
|
* Hard error handler
|
|
*
|
|
* History:
|
|
* 07-03-91 JimA Created scaffolding.
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
#include "ntlpcapi.h"
|
|
|
|
HANDLE NtDllHandle = NULL;
|
|
HANDLE EventSource;
|
|
DWORD HardErrorReportMode;
|
|
|
|
UINT wIcons[] = {
|
|
0,
|
|
MB_ICONINFORMATION,
|
|
MB_ICONEXCLAMATION,
|
|
MB_ICONSTOP };
|
|
UINT wOptions[] = {
|
|
MB_ABORTRETRYIGNORE,
|
|
MB_OK,
|
|
MB_OKCANCEL,
|
|
MB_RETRYCANCEL,
|
|
MB_YESNO,
|
|
MB_YESNOCANCEL
|
|
};
|
|
DWORD dwResponses[] = {
|
|
ResponseNotHandled, // MessageBox error
|
|
ResponseOk, // IDOK
|
|
ResponseCancel, // IDCANCEL
|
|
ResponseAbort, // IDABORT
|
|
ResponseRetry, // IDRETRY
|
|
ResponseIgnore, // IDIGNORE
|
|
ResponseYes, // IDYES
|
|
ResponseNo // IDNO
|
|
};
|
|
DWORD dwResponseDefault[] = {
|
|
ResponseAbort, // OptionAbortRetryIgnore
|
|
ResponseOk, // OptionOK
|
|
ResponseOk, // OptionOKCancel
|
|
ResponseCancel, // OptionRetryCancel
|
|
ResponseYes, // OptionYes
|
|
ResponseYes, // OptionYesNoCancel
|
|
ResponseOk, // OptionShutdownSystem
|
|
};
|
|
WCHAR HardErrorachStatus[4096];
|
|
CHAR HEAnsiBuf[3072];
|
|
|
|
DWORD DisplayVDMHardError(LPDWORD ParameterVector);
|
|
LONG WOWSysErrorBoxDlgProc(PWND, UINT, DWORD, LONG);
|
|
|
|
/*
|
|
* SEB_CREATEPARMS structure is passed to WM_DIALOGINIT of
|
|
* WOWSysErrorBoxDlgProc.
|
|
*
|
|
*/
|
|
|
|
typedef struct _SEB_CREATEPARMS {
|
|
LPWSTR szTitle;
|
|
LPWSTR szMessage;
|
|
LPWSTR rgszBtn[3];
|
|
BOOL rgfDefButton[3];
|
|
WORD wBtnCancel;
|
|
} SEB_CREATEPARMS, *PSEB_CREATEPARMS;
|
|
|
|
VOID
|
|
LogErrorPopup(
|
|
IN LPWSTR Caption,
|
|
IN LPWSTR Message
|
|
)
|
|
{
|
|
|
|
LPWSTR lps[2];
|
|
|
|
lps[0] = Caption;
|
|
lps[1] = Message;
|
|
|
|
if ( EventSource ) {
|
|
LeaveCrit();
|
|
ReportEvent(
|
|
EventSource,
|
|
EVENTLOG_INFORMATION_TYPE,
|
|
0,
|
|
STATUS_LOG_HARD_ERROR,
|
|
NULL,
|
|
2,
|
|
0,
|
|
&lps[0],
|
|
NULL
|
|
);
|
|
EnterCrit();
|
|
}
|
|
}
|
|
|
|
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 = (PWCHAR)LinkName.Buffer;
|
|
p = p+12;
|
|
for(i=0;i<26;i++){
|
|
*p = (WCHAR)'A' + (WCHAR)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]='A'+(WCHAR)i;
|
|
OutputDriveLetter[1]=':';
|
|
OutputDriveLetter[2]='\0';
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
BOOL CALLBACK WindowEnumProc(
|
|
HWND hwnd,
|
|
LPARAM lParam)
|
|
{
|
|
#ifdef FE_IME // WindowEnumProc()
|
|
/*
|
|
* LATER: We need check IME-UI/IME class.
|
|
* if we get IME-UI/IME class, we should skip it.
|
|
*/
|
|
*(HWND *)lParam = hwnd;
|
|
#else
|
|
*(HWND *)lParam = hwnd;
|
|
#endif // FE_IME
|
|
return FALSE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* HardErrorHandler
|
|
*
|
|
* This routine processes hard error requests from the CSR exception port
|
|
*
|
|
* History:
|
|
* 07-03-91 JimA Created.
|
|
\***************************************************************************/
|
|
|
|
VOID HardErrorHandler()
|
|
{
|
|
NTSTATUS Status;
|
|
int idResponse;
|
|
LPSTR lpCaption, alpCaption;
|
|
LPWSTR lpMessage, lpFullCaption;
|
|
PHARDERROR_MSG phemsg;
|
|
DWORD cbCaption;
|
|
HANDLE ClientProcess;
|
|
DWORD ParameterVector[MAXIMUM_HARDERROR_PARAMETERS];
|
|
DWORD Counter;
|
|
DWORD StringsToFreeMask;
|
|
UNICODE_STRING ScratchU;
|
|
UNICODE_STRING LocalU;
|
|
ANSI_STRING LocalA;
|
|
LPSTR formatstring;
|
|
PMESSAGE_RESOURCE_ENTRY MessageEntry;
|
|
LPWSTR ApplicationNameString;
|
|
BOOL ApplicationNameAllocated;
|
|
LPWSTR ApplicationName;
|
|
BOOL ApplicationNameIsStatic;
|
|
LONG ApplicationNameLength;
|
|
BOOL fFreeCaption;
|
|
PHARDERRORINFO phi, *pphi;
|
|
PCSR_THREAD DerefThread;
|
|
DWORD dwMBFlags;
|
|
WCHAR *pResBuffer;
|
|
BOOL ResAllocated;
|
|
DWORD dwResponse;
|
|
BOOLEAN ErrorIsFromSystem;
|
|
|
|
if (!NtUserHardErrorControl(HardErrorSetup, NULL)) {
|
|
|
|
/*
|
|
* We failed to set up to process hard errors. Acknowledge all
|
|
* pending errors as NotHandled.
|
|
*/
|
|
pphi = &gphiList;
|
|
while (gphiList != NULL) {
|
|
phi = gphiList;
|
|
gphiList = phi->phiNext;
|
|
|
|
/*
|
|
* Set the response as not handled
|
|
*/
|
|
phi->pmsg->Response = ResponseNotHandled;
|
|
|
|
/*
|
|
* Signal HardError() that we're done.
|
|
*/
|
|
if (phi->hEventHardError == NULL) {
|
|
Status = NtReplyPort(((PCSR_THREAD)phi->pthread)->Process->ClientPort,
|
|
(PPORT_MESSAGE)phi->pmsg);
|
|
LeaveCrit();
|
|
CsrDereferenceThread(phi->pthread);
|
|
EnterCrit();
|
|
LocalFree(phi->pmsg);
|
|
} else {
|
|
NtSetEvent(phi->hEventHardError, NULL);
|
|
}
|
|
LocalFree(phi);
|
|
}
|
|
return;
|
|
}
|
|
gdwHardErrorThreadId = (DWORD)NtCurrentTeb()->ClientId.UniqueThread;
|
|
|
|
if (NtDllHandle == NULL) {
|
|
LeaveCrit();
|
|
NtDllHandle = GetModuleHandle(TEXT("ntdll"));
|
|
EnterCrit();
|
|
}
|
|
UserAssert(NtDllHandle != NULL);
|
|
|
|
DerefThread = NULL;
|
|
|
|
if ( !EventSource ) {
|
|
LeaveCrit();
|
|
EventSource = RegisterEventSourceW(NULL,L"Application Popup");
|
|
EnterCrit();
|
|
|
|
if ( EventSource ) {
|
|
HardErrorReportMode = GetErrorMode();
|
|
} else {
|
|
HardErrorReportMode = 0;
|
|
}
|
|
} else {
|
|
HardErrorReportMode = GetErrorMode();
|
|
}
|
|
|
|
for (;;) {
|
|
|
|
/*
|
|
* If no messages are pending, we're done.
|
|
*/
|
|
if ( DerefThread ) {
|
|
LeaveCrit();
|
|
CsrDereferenceThread(DerefThread);
|
|
DerefThread = NULL;
|
|
EnterCrit();
|
|
}
|
|
if (gphiList == NULL) {
|
|
|
|
NtUserHardErrorControl(HardErrorCleanup, NULL);
|
|
gdwHardErrorThreadId = 0;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Process the error from the tail of the list.
|
|
*/
|
|
for (phi = gphiList; phi->phiNext != NULL; phi = phi->phiNext)
|
|
;
|
|
|
|
phemsg = phi->pmsg;
|
|
phemsg->Response = ResponseNotHandled;
|
|
|
|
//
|
|
// Compute the hard error parameter and store them in
|
|
// the parameter vector
|
|
//
|
|
|
|
StringsToFreeMask = 0;
|
|
ClientProcess = NULL;
|
|
ClientProcess = OpenProcess(
|
|
PROCESS_VM_READ | PROCESS_QUERY_INFORMATION,
|
|
FALSE,
|
|
(DWORD)phemsg->h.ClientId.UniqueProcess
|
|
);
|
|
|
|
for(Counter = 0;Counter < phemsg->NumberOfParameters;Counter++) {
|
|
ParameterVector[Counter] = phemsg->Parameters[Counter];
|
|
}
|
|
while(Counter < MAXIMUM_HARDERROR_PARAMETERS)
|
|
ParameterVector[Counter++] = (DWORD)L"";
|
|
|
|
//
|
|
// If there are unicode strings, then we need to get them
|
|
// convert them to ansi and then store them in the
|
|
// parameter vector
|
|
//
|
|
|
|
if ( phemsg->UnicodeStringParameterMask ) {
|
|
for(Counter = 0;Counter < phemsg->NumberOfParameters;Counter++) {
|
|
|
|
//
|
|
// if there is a string in this position,
|
|
// then grab it
|
|
if ( phemsg->UnicodeStringParameterMask & 1 << Counter ) {
|
|
|
|
//
|
|
// Point to an empty string in case we don't have
|
|
// a client to read from or something fails later on.
|
|
//
|
|
|
|
ParameterVector[Counter] = (DWORD)"";
|
|
|
|
if ( ClientProcess ) {
|
|
|
|
Status = NtReadVirtualMemory(
|
|
ClientProcess,
|
|
(PVOID)phemsg->Parameters[Counter],
|
|
(PVOID)&ScratchU,
|
|
sizeof(ScratchU),
|
|
NULL
|
|
);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
KdPrint(("Failed to read error string struct!\n"));
|
|
continue;
|
|
}
|
|
|
|
LocalU = ScratchU;
|
|
LocalU.Buffer = (PWSTR)LocalAlloc(
|
|
LMEM_ZEROINIT,
|
|
LocalU.MaximumLength
|
|
);
|
|
if ( !LocalU.Buffer ) {
|
|
KdPrint(("Failed to alloc string buffer!\n"));
|
|
continue;
|
|
}
|
|
Status = NtReadVirtualMemory(
|
|
ClientProcess,
|
|
(PVOID)ScratchU.Buffer,
|
|
(PVOID)LocalU.Buffer,
|
|
LocalU.MaximumLength,
|
|
NULL
|
|
);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
LocalFree(LocalU.Buffer);
|
|
KdPrint(("Failed to read error string!\n"));
|
|
continue;
|
|
}
|
|
RtlUnicodeStringToAnsiString(&LocalA, &LocalU, TRUE);
|
|
|
|
//
|
|
// check to see if string contains an NT
|
|
// device name. If so, then attempt a
|
|
// drive letter substitution
|
|
//
|
|
|
|
if ( strstr(LocalA.Buffer,"\\Device") == LocalA.Buffer ) {
|
|
SubstituteDeviceName(&LocalU,LocalA.Buffer);
|
|
}
|
|
else
|
|
if ( LocalA.Length > 4 && !_strnicmp(LocalA.Buffer, "\\??\\", 4) ) {
|
|
strcpy( LocalA.Buffer, LocalA.Buffer+4 );
|
|
LocalA.Length -= 4;
|
|
}
|
|
|
|
LocalFree(LocalU.Buffer);
|
|
|
|
StringsToFreeMask |= (1 << Counter);
|
|
ParameterVector[Counter] = (DWORD)LocalA.Buffer;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Special-case STATUS_VDM_HARD_ERROR, which is raised by the VDM process
|
|
// when a 16-bit fault must be handled without any reentrancy.
|
|
//
|
|
|
|
if (phemsg->Status == STATUS_VDM_HARD_ERROR) {
|
|
dwResponse = DisplayVDMHardError(ParameterVector);
|
|
goto Reply;
|
|
}
|
|
|
|
|
|
ApplicationNameString = ServerLoadString(
|
|
hModuleWin,
|
|
STR_UNKNOWN_APPLICATION,
|
|
L"System Process",
|
|
&ApplicationNameAllocated
|
|
);
|
|
|
|
ApplicationName = ApplicationNameString;
|
|
ApplicationNameIsStatic = TRUE;
|
|
|
|
dwMBFlags = wIcons[(ULONG)(phemsg->Status) >> 30] |
|
|
wOptions[phemsg->ValidResponseOptions];
|
|
|
|
if ( ClientProcess ) {
|
|
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;
|
|
#ifndef UNICODE
|
|
ANSI_STRING AnsiString;
|
|
UNICODE_STRING UnicodeString;
|
|
#endif
|
|
|
|
ErrorIsFromSystem = FALSE;
|
|
|
|
//
|
|
// Pick up the application name.
|
|
//
|
|
|
|
//
|
|
// 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(
|
|
ClientProcess,
|
|
ProcessBasicInformation,
|
|
&BasicInfo,
|
|
sizeof(BasicInfo),
|
|
NULL
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ErrorIsFromSystem = TRUE;
|
|
goto noname;
|
|
}
|
|
|
|
Peb = BasicInfo.PebBaseAddress;
|
|
|
|
if ( !Peb ) {
|
|
ErrorIsFromSystem = TRUE;
|
|
goto noname;
|
|
}
|
|
|
|
//
|
|
// Ldr = Peb->Ldr
|
|
//
|
|
|
|
Status = NtReadVirtualMemory(
|
|
ClientProcess,
|
|
&Peb->Ldr,
|
|
&Ldr,
|
|
sizeof(Ldr),
|
|
NULL
|
|
);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto noname;
|
|
}
|
|
|
|
LdrHead = &Ldr->InLoadOrderModuleList;
|
|
|
|
//
|
|
// LdrNext = Head->Flink;
|
|
//
|
|
|
|
Status = NtReadVirtualMemory(
|
|
ClientProcess,
|
|
&LdrHead->Flink,
|
|
&LdrNext,
|
|
sizeof(LdrNext),
|
|
NULL
|
|
);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto noname;
|
|
}
|
|
|
|
if ( LdrNext != LdrHead ) {
|
|
|
|
//
|
|
// This is the entry data for the image.
|
|
//
|
|
|
|
LdrEntry = CONTAINING_RECORD(LdrNext, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
|
|
Status = NtReadVirtualMemory(
|
|
ClientProcess,
|
|
LdrEntry,
|
|
&LdrEntryData,
|
|
sizeof(LdrEntryData),
|
|
NULL
|
|
);
|
|
if ( !NT_SUCCESS(Status)) {
|
|
goto noname;
|
|
}
|
|
Status = NtReadVirtualMemory(
|
|
ClientProcess,
|
|
&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 ) {
|
|
goto noname;
|
|
}
|
|
|
|
Status = NtReadVirtualMemory(
|
|
ClientProcess,
|
|
LdrEntryData.BaseDllName.Buffer,
|
|
ClientApplicationName,
|
|
LdrEntryData.BaseDllName.MaximumLength,
|
|
NULL
|
|
);
|
|
if ( !NT_SUCCESS(Status)) {
|
|
LocalFree(ClientApplicationName);
|
|
goto noname;
|
|
}
|
|
|
|
#ifndef UNICODE
|
|
//
|
|
// Now we have a unicode Application Name. Convert to ANSI
|
|
//
|
|
|
|
RtlInitUnicodeString(&UnicodeString, ClientApplicationName);
|
|
Status = RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, TRUE);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
LocalFree(ClientApplicationName);
|
|
goto noname;
|
|
}
|
|
|
|
ApplicationName = AnsiString.Buffer;
|
|
#else
|
|
ApplicationName = ClientApplicationName;
|
|
#endif
|
|
ApplicationNameIsStatic = FALSE;
|
|
|
|
}
|
|
noname:
|
|
NtClose(ClientProcess);
|
|
ClientProcess = NULL;
|
|
} else {
|
|
ErrorIsFromSystem = TRUE;
|
|
}
|
|
Status = RtlFindMessage( (PVOID)NtDllHandle,
|
|
(ULONG)RT_MESSAGETABLE,
|
|
LANG_NEUTRAL,
|
|
phemsg->Status,
|
|
&MessageEntry
|
|
);
|
|
|
|
alpCaption = lpCaption = NULL;
|
|
fFreeCaption = FALSE;
|
|
if (!NT_SUCCESS( Status )) {
|
|
formatstring = "Unknown Hard Error";
|
|
} else {
|
|
lpCaption = MessageEntry->Text;
|
|
formatstring = lpCaption;
|
|
|
|
/*
|
|
* If the message starts with a '{', it has a caption.
|
|
*/
|
|
if (*lpCaption == '{') {
|
|
cbCaption = 0;
|
|
lpCaption++;
|
|
formatstring++;
|
|
#ifdef FE_SB // HardErrorHandler()
|
|
while (*formatstring) {
|
|
if (IsDBCSLeadByte(*formatstring)) {
|
|
formatstring++;
|
|
cbCaption += 1;
|
|
if(*formatstring) {
|
|
formatstring++;
|
|
cbCaption += 1;
|
|
}
|
|
} else if(*formatstring != '}' ) {
|
|
formatstring++;
|
|
cbCaption += 1;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
#else
|
|
while ( *formatstring && *formatstring != '}' ) {
|
|
formatstring++;
|
|
cbCaption += 1;
|
|
}
|
|
#endif // FE_SB
|
|
if (*formatstring)
|
|
formatstring++;
|
|
|
|
/*
|
|
* Eat any non-printable stuff, up to the NULL
|
|
*/
|
|
while ( *formatstring && (unsigned char)*formatstring <= ' ') {
|
|
*formatstring++;
|
|
}
|
|
if (cbCaption++ > 0 && (alpCaption =
|
|
(LPSTR)LocalAlloc(LPTR, cbCaption)) != NULL) {
|
|
RtlMoveMemory(alpCaption, lpCaption, cbCaption - 1);
|
|
fFreeCaption = TRUE;
|
|
}
|
|
}
|
|
if (*formatstring == 0) {
|
|
formatstring = "Unknown Hard Error";
|
|
}
|
|
}
|
|
|
|
if (alpCaption == NULL) {
|
|
switch (phemsg->Status & ERROR_SEVERITY_ERROR) {
|
|
case ERROR_SEVERITY_SUCCESS:
|
|
alpCaption = pszaSUCCESS;
|
|
break;
|
|
case ERROR_SEVERITY_INFORMATIONAL:
|
|
alpCaption = pszaSYSTEM_INFORMATION;
|
|
break;
|
|
case ERROR_SEVERITY_WARNING:
|
|
alpCaption = pszaSYSTEM_WARNING;
|
|
break;
|
|
case ERROR_SEVERITY_ERROR:
|
|
alpCaption = pszaSYSTEM_ERROR;
|
|
break;
|
|
}
|
|
}
|
|
cbCaption = strlen(alpCaption) + 1;
|
|
|
|
//
|
|
// Special case UAE
|
|
//
|
|
if ( phemsg->Status == STATUS_UNHANDLED_EXCEPTION ) {
|
|
Status = RtlFindMessage( (PVOID)NtDllHandle,
|
|
(ULONG)RT_MESSAGETABLE,
|
|
LANG_NEUTRAL,
|
|
ParameterVector[0],
|
|
&MessageEntry
|
|
);
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
UNICODE_STRING us1;
|
|
ANSI_STRING as1;
|
|
NTSTATUS trstatus;
|
|
|
|
/*
|
|
* The format specifier %s appears in some message strings.
|
|
* This was meant to be ANSI so we need to call wsprintfA
|
|
* first so %s is interpreted correctly.
|
|
*/
|
|
|
|
pResBuffer = ServerLoadString(
|
|
hModuleWin,
|
|
STR_UNKNOWN_EXCEPTION,
|
|
L"Unknown software exception",
|
|
&ResAllocated
|
|
);
|
|
|
|
RtlInitUnicodeString(&us1,pResBuffer);
|
|
trstatus = RtlUnicodeStringToAnsiString(&as1,&us1,TRUE);
|
|
if ( NT_SUCCESS(trstatus) ) {
|
|
wsprintfA(HEAnsiBuf, formatstring, as1.Buffer,
|
|
ParameterVector[0],
|
|
ParameterVector[1]
|
|
);
|
|
RtlFreeAnsiString(&as1);
|
|
} else {
|
|
wsprintfA(HEAnsiBuf, formatstring, "unknown software exception",
|
|
ParameterVector[0],
|
|
ParameterVector[1]
|
|
);
|
|
}
|
|
if ( ResAllocated ) {
|
|
LocalFree(pResBuffer);
|
|
}
|
|
wsprintfW(HardErrorachStatus, L"%hs", HEAnsiBuf);
|
|
} else {
|
|
|
|
//
|
|
// Access Violations are handled a bit differently
|
|
//
|
|
|
|
if ( ParameterVector[0] == STATUS_ACCESS_VIOLATION ) {
|
|
|
|
wsprintfA(HEAnsiBuf, MessageEntry->Text, ParameterVector[1],
|
|
ParameterVector[3],
|
|
ParameterVector[2] ? "written" : "read"
|
|
);
|
|
wsprintfW(HardErrorachStatus, L"%hs", HEAnsiBuf);
|
|
|
|
} else if ( ParameterVector[0] == STATUS_IN_PAGE_ERROR ) {
|
|
wsprintfA(HEAnsiBuf, MessageEntry->Text, ParameterVector[1],
|
|
ParameterVector[3],
|
|
ParameterVector[2]
|
|
);
|
|
wsprintfW(HardErrorachStatus, L"%hs", HEAnsiBuf);
|
|
|
|
} else {
|
|
lpCaption = MessageEntry->Text;
|
|
if ( !strncmp(lpCaption, "{EXCEPTION}", strlen("{EXCEPTION}")) ) {
|
|
while ( *lpCaption >= ' ' ) {
|
|
lpCaption++;
|
|
}
|
|
while ( *lpCaption && *lpCaption <= ' ') {
|
|
*lpCaption++;
|
|
}
|
|
|
|
//
|
|
// This is a marked exception. The lpCaption pointer
|
|
// points at the exception-name.
|
|
//
|
|
} else {
|
|
lpCaption = "unknown software exception";
|
|
}
|
|
|
|
wsprintfA(HEAnsiBuf, formatstring, lpCaption,
|
|
ParameterVector[0],
|
|
ParameterVector[1]
|
|
);
|
|
wsprintfW(HardErrorachStatus, L"%hs", HEAnsiBuf);
|
|
}
|
|
|
|
pResBuffer = ServerLoadString(
|
|
hModuleWin,
|
|
STR_OK_TO_TERMINATE,
|
|
L"Click on OK to terminate the application",
|
|
&ResAllocated
|
|
);
|
|
|
|
wcscat(
|
|
HardErrorachStatus,
|
|
TEXT("\n")
|
|
);
|
|
wcscat(
|
|
HardErrorachStatus,
|
|
pResBuffer
|
|
);
|
|
if ( ResAllocated ) {
|
|
LocalFree(pResBuffer);
|
|
}
|
|
#if DEVL
|
|
if ( phemsg->ValidResponseOptions == OptionOkCancel ) {
|
|
pResBuffer = ServerLoadString(
|
|
hModuleWin,
|
|
STR_CANCEL_TO_DEBUG,
|
|
L"Click on CANCEL xx to debug the application",
|
|
&ResAllocated
|
|
);
|
|
|
|
wcscat(
|
|
HardErrorachStatus,
|
|
TEXT("\n")
|
|
);
|
|
wcscat(
|
|
HardErrorachStatus,
|
|
pResBuffer
|
|
);
|
|
if ( ResAllocated ) {
|
|
LocalFree(pResBuffer);
|
|
}
|
|
}
|
|
#endif // DEVL
|
|
}
|
|
} else if (phemsg->Status == STATUS_SERVICE_NOTIFICATION) {
|
|
wsprintfW(HardErrorachStatus, L"%hs", ParameterVector[0]);
|
|
|
|
lpFullCaption = LocalAlloc(LPTR,
|
|
(strlen((LPSTR)ParameterVector[1]) + 1) * sizeof(WCHAR));
|
|
wsprintfW(lpFullCaption, L"%hs", ParameterVector[1]);
|
|
|
|
dwMBFlags = ParameterVector[2] & ~MB_SERVICE_NOTIFICATION;
|
|
} else {
|
|
|
|
try {
|
|
wsprintfA(HEAnsiBuf, formatstring, ParameterVector[0],
|
|
ParameterVector[1],
|
|
ParameterVector[2],
|
|
ParameterVector[3]);
|
|
|
|
#ifdef FE_SB // HardErrorHandler()
|
|
{
|
|
//
|
|
// Won't replace 0xd 0xa with space.
|
|
// Eliminate it, if the character is between DBCS characters.
|
|
//
|
|
// [Rules]
|
|
// + {DBC}0xd0xa{DBC} -> {DBC}{DBC}
|
|
// + {DBC}0xd0xa{SBC} -> {DBC} {SBC}
|
|
// + {SBC}0xd0xa{DBC} -> {SBC} {DBC}
|
|
// + {SBC}0xd0xa{SBC} -> {SBC} {SBC}
|
|
//
|
|
BOOL bLastCharIsDbcs = FALSE;
|
|
UINT cSkip = 0;
|
|
LPSTR startSkip = NULL;
|
|
|
|
formatstring = HEAnsiBuf;
|
|
|
|
while (*formatstring) {
|
|
switch (*formatstring) {
|
|
case 0xa:
|
|
case 0xd:
|
|
if (cSkip == 0) {
|
|
startSkip = formatstring;
|
|
}
|
|
cSkip++;
|
|
break;
|
|
default:
|
|
if (cSkip) {
|
|
UserAssert(startSkip != NULL);
|
|
if(bLastCharIsDbcs && IsDBCSLeadByte(*formatstring)) {
|
|
//
|
|
// Overwrite skipped character.
|
|
//
|
|
strcpy(startSkip,formatstring);
|
|
//
|
|
// Adjust current pointer.
|
|
//
|
|
formatstring = startSkip;
|
|
} else {
|
|
//
|
|
// Fill skipped character with ' '.
|
|
//
|
|
*startSkip = ' ';
|
|
//
|
|
// Overwrite skiped charcter..
|
|
//
|
|
strcpy(startSkip+1,formatstring);
|
|
//
|
|
// Adjust pointer...
|
|
//
|
|
formatstring = startSkip+1;
|
|
}
|
|
cSkip = 0;
|
|
startSkip = NULL;
|
|
}
|
|
|
|
if (IsDBCSLeadByte(*formatstring)) {
|
|
//
|
|
// This is DBCS character.
|
|
//
|
|
formatstring++;
|
|
if (*formatstring) {
|
|
//
|
|
// Mark as DBCS character.
|
|
//
|
|
bLastCharIsDbcs = TRUE;
|
|
} else {
|
|
//
|
|
// Reach end-of-string.
|
|
// DBCS char will be displayed as garbage....
|
|
//
|
|
goto EndOfString;
|
|
}
|
|
} else {
|
|
bLastCharIsDbcs = FALSE;
|
|
}
|
|
break;
|
|
}
|
|
formatstring++;
|
|
}
|
|
|
|
//
|
|
// This is for following case...
|
|
//
|
|
// +) 0xa0xd0x0.
|
|
//
|
|
// We will trim unnessesary 0xa 0xd character.
|
|
//
|
|
if (cSkip) {
|
|
UserAssert(startSkip != NULL);
|
|
startSkip = '\0';
|
|
}
|
|
EndOfString: ;
|
|
}
|
|
#else
|
|
formatstring = HEAnsiBuf;
|
|
while ( *formatstring ) {
|
|
if ( *formatstring == 0xd ) {
|
|
*formatstring = ' ';
|
|
|
|
//
|
|
// Move everything up if a CR LF sequence is found
|
|
//
|
|
if ( *(formatstring+1) == 0xa ) {
|
|
strcpy(formatstring, formatstring+1);
|
|
}
|
|
}
|
|
|
|
if ( *formatstring == 0xa ) {
|
|
*formatstring = ' ';
|
|
}
|
|
formatstring++;
|
|
}
|
|
#endif // FE_SB
|
|
|
|
wsprintfW(HardErrorachStatus, L"%hs", HEAnsiBuf);
|
|
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER) {
|
|
wsprintfW(HardErrorachStatus, L"Exception Processing Message %lx Parameters %lx %lx %lx %lx", phemsg->Status,
|
|
ParameterVector[0],
|
|
ParameterVector[1],
|
|
ParameterVector[2],
|
|
ParameterVector[3]);
|
|
}
|
|
}
|
|
|
|
lpMessage = HardErrorachStatus;
|
|
for(Counter = 0;Counter < phemsg->NumberOfParameters;Counter++) {
|
|
|
|
//
|
|
// if there is a string in this position,
|
|
// then free it
|
|
if ( StringsToFreeMask & (1 << Counter) ) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, (PVOID)ParameterVector[Counter]);
|
|
}
|
|
}
|
|
|
|
if (phemsg->Status != STATUS_SERVICE_NOTIFICATION) {
|
|
HWND hwndOwner = NULL;
|
|
DWORD cbTitle;
|
|
LPWSTR lpTitle;
|
|
|
|
ApplicationNameLength = (wcslen(ApplicationName) +
|
|
wcslen(TEXT(" - ")) + 2)*sizeof(WCHAR);
|
|
|
|
/*
|
|
* if UNICODE :
|
|
* need count of bytes when ANSI Caption is output as Unicode
|
|
* LATER IanJa: eventually the caption will be Unicode too, so
|
|
* cbCaption will already be correct and won't need doubling.
|
|
*/
|
|
cbCaption *= sizeof(WCHAR);
|
|
|
|
|
|
/*
|
|
* If the client is a Windows thread, find a top-level window
|
|
* belonging to the thread to act as the owner
|
|
*/
|
|
EnumThreadWindows((DWORD)phemsg->h.ClientId.UniqueThread,
|
|
WindowEnumProc, (LPARAM)&hwndOwner);
|
|
|
|
/*
|
|
* Add a window title, if possible
|
|
*/
|
|
if (hwndOwner != NULL) {
|
|
|
|
/*
|
|
* Get the window title, if it has one.
|
|
*/
|
|
cbTitle = GetWindowTextLengthW(hwndOwner);
|
|
if (cbTitle != 0) {
|
|
|
|
/*
|
|
* Alloc a buffer and query the text
|
|
*/
|
|
lpTitle = LocalAlloc(LPTR, (cbTitle + 1) * sizeof(WCHAR));
|
|
if (lpTitle != NULL)
|
|
GetWindowText(hwndOwner, lpTitle, cbTitle + 1);
|
|
else
|
|
cbTitle = 0; // allocation failed
|
|
}
|
|
}
|
|
if (hwndOwner != NULL && cbTitle != 0) {
|
|
cbCaption += (cbTitle * sizeof(WCHAR)) + sizeof(TEXT(": "));
|
|
lpFullCaption = (LPWSTR)LocalAlloc(LPTR, cbCaption+ApplicationNameLength);
|
|
if (lpFullCaption != NULL)
|
|
wsprintfW(
|
|
lpFullCaption,
|
|
L"%ls: %ls - %hs",
|
|
lpTitle,
|
|
ApplicationName,
|
|
alpCaption
|
|
);
|
|
LocalFree(lpTitle);
|
|
} else {
|
|
lpFullCaption = (LPWSTR)LocalAlloc(LPTR, cbCaption+ApplicationNameLength);
|
|
if (lpFullCaption != NULL)
|
|
wsprintfW(
|
|
lpFullCaption,
|
|
L"%ls - %hs",
|
|
ApplicationName,
|
|
alpCaption
|
|
);
|
|
}
|
|
}
|
|
if (fFreeCaption)
|
|
LocalFree(alpCaption);
|
|
|
|
//
|
|
// Add the application name to the caption
|
|
//
|
|
|
|
if ( phemsg->Status == STATUS_SERVICE_NOTIFICATION ||
|
|
HardErrorReportMode == 0 ||
|
|
HardErrorReportMode == 1 && ErrorIsFromSystem == FALSE ) {
|
|
|
|
do
|
|
{
|
|
gfHardError = HEF_NORMAL;
|
|
phemsg->Response = ResponseNotHandled;
|
|
if (NtUserHardErrorControl((dwMBFlags & MB_DEFAULT_DESKTOP_ONLY) ?
|
|
HardErrorAttachUser : HardErrorAttach, NULL)) {
|
|
|
|
/*
|
|
* We have already handled the MB_DEFAULT_DESKTOP_ONLY and
|
|
* MB_SERVICE_NOTIFICATION flags. Clear them to prevent
|
|
* recursion.
|
|
*/
|
|
dwMBFlags &= ~(MB_DEFAULT_DESKTOP_ONLY | MB_SERVICE_NOTIFICATION);
|
|
/*
|
|
* Bring up the message box. OR in MB_SETFOREGROUND so the
|
|
* it comes up on top.
|
|
*
|
|
* NOTE; This only works with a single visible windowstation.
|
|
*/
|
|
LeaveCrit();
|
|
idResponse = MessageBoxEx(NULL, lpMessage, lpFullCaption,
|
|
dwMBFlags | MB_SYSTEMMODAL | MB_SETFOREGROUND, 0);
|
|
EnterCrit();
|
|
|
|
if (NtUserHardErrorControl(HardErrorDetach, NULL))
|
|
gfHardError = HEF_SWITCH;
|
|
} else {
|
|
idResponse = 0;
|
|
}
|
|
dwResponse = dwResponses[idResponse];
|
|
|
|
} while (gfHardError == HEF_SWITCH);
|
|
|
|
if ( ErrorIsFromSystem ) {
|
|
LogErrorPopup(lpFullCaption,lpMessage);
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// We have selected mode 1 and the error is from within the system.
|
|
// log the message and continue
|
|
// Or, We selected mode 2 which says to log all hard errors
|
|
//
|
|
|
|
LogErrorPopup(lpFullCaption,lpMessage);
|
|
dwResponse = dwResponseDefault[phemsg->ValidResponseOptions];
|
|
}
|
|
|
|
/*
|
|
* Free all allocated stuff
|
|
*/
|
|
if (lpFullCaption != NULL)
|
|
LocalFree(lpFullCaption);
|
|
if ( ApplicationNameIsStatic == FALSE ) {
|
|
LocalFree(ApplicationName);
|
|
}
|
|
|
|
if ( ApplicationNameAllocated ) {
|
|
LocalFree(ApplicationNameString);
|
|
}
|
|
|
|
if (gfHardError != HEF_RESTART) {
|
|
Reply:
|
|
/*
|
|
* Unlink the error from the list.
|
|
*/
|
|
for (
|
|
pphi = &gphiList;
|
|
(*pphi != NULL) && (*pphi != phi);
|
|
pphi = &(*pphi)->phiNext)
|
|
;
|
|
if (*pphi != NULL) {
|
|
*pphi = phi->phiNext;
|
|
|
|
/*
|
|
* Save the response
|
|
*/
|
|
phemsg->Response = dwResponse;
|
|
|
|
/*
|
|
* Signal HardError() that we're done.
|
|
*/
|
|
if (phi->hEventHardError == NULL) {
|
|
Status = NtReplyPort(((PCSR_THREAD)phi->pthread)->Process->ClientPort,
|
|
(PPORT_MESSAGE)phi->pmsg);
|
|
DerefThread = (PCSR_THREAD)phi->pthread;
|
|
LocalFree(phi->pmsg);
|
|
} else {
|
|
NtSetEvent(phi->hEventHardError, NULL);
|
|
}
|
|
LocalFree(phi);
|
|
}
|
|
} else {
|
|
|
|
/*
|
|
* Don't dereference yet.
|
|
*/
|
|
DerefThread = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
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)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;
|
|
}
|
|
|
|
|
|
DWORD DisplayVDMHardError(
|
|
LPDWORD Parameters
|
|
)
|
|
{
|
|
SEB_CREATEPARMS sebcp;
|
|
WORD rgwBtn[3];
|
|
WORD wBtn;
|
|
int i;
|
|
DWORD dwResponse;
|
|
LPWSTR apstrButton[3];
|
|
int aidButton[3];
|
|
int defbutton;
|
|
int cButtons = 0;
|
|
BOOL fAlloc = TRUE;
|
|
MSGBOXDATA mbd;
|
|
|
|
//
|
|
// STATUS_VDM_HARD_ERROR was raised as a hard error.
|
|
//
|
|
// Right now, only WOW does this. If NTVDM does it,
|
|
// fForWOW will be false.
|
|
//
|
|
// The 4 parameters are as follows:
|
|
//
|
|
// Parameters[0] = (fForWOW << 16) | wBtn1;
|
|
// Parameters[1] = (wBtn2 << 16) | wBtn3;
|
|
// Parameters[2] = (DWORD) szTitle;
|
|
// Parameters[3] = (DWORD) szMessage;
|
|
//
|
|
|
|
rgwBtn[0] = LOWORD(Parameters[0]);
|
|
rgwBtn[1] = HIWORD(Parameters[1]);
|
|
rgwBtn[2] = LOWORD(Parameters[1]);
|
|
|
|
/*
|
|
* Get the error text and convert it to unicode. Note that the
|
|
* buffers are allocated from the process heap, so we can't
|
|
* use LocalFree when we free them.
|
|
*/
|
|
try {
|
|
|
|
MBToWCS((LPSTR)Parameters[2], -1, &sebcp.szTitle, -1, TRUE);
|
|
MBToWCS((LPSTR)Parameters[3], -1, &sebcp.szMessage, -1, TRUE);
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
sebcp.szTitle = TEXT("VDM Internal Error");
|
|
sebcp.szMessage = TEXT("Exception retrieving error text.");
|
|
fAlloc = FALSE;
|
|
|
|
}
|
|
|
|
/*
|
|
* Setup rgszBtn[x] and rgfDefButton[x] for each button.
|
|
* wBtnCancel is the button # to return when IDCANCEL
|
|
* is received (because the user hit Esc).
|
|
*/
|
|
|
|
sebcp.wBtnCancel = 0;
|
|
|
|
defbutton = 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) {
|
|
defbutton = cButtons;
|
|
}
|
|
if (wBtn == SEB_CANCEL) {
|
|
sebcp.wBtnCancel = cButtons;
|
|
}
|
|
cButtons++;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Pop the dialog. Code copied from HardErrorThread above.
|
|
*/
|
|
if ( HardErrorReportMode == 0 || HardErrorReportMode == 1 ) {
|
|
|
|
do {
|
|
gfHardError = HEF_NORMAL;
|
|
dwResponse = 0;
|
|
if (NtUserHardErrorControl(HardErrorAttach, NULL)) {
|
|
/*
|
|
* Bring up the dialog box.
|
|
*/
|
|
memset(&mbd, 0, sizeof(MSGBOXDATA));
|
|
mbd.cbSize = sizeof(MSGBOXPARAMS);
|
|
mbd.lpszText = sebcp.szMessage;
|
|
mbd.lpszCaption = sebcp.szTitle;
|
|
mbd.dwStyle = MB_SYSTEMMODAL | MB_SETFOREGROUND;
|
|
if ((cButtons != 1) || (aidButton[0] != 1))
|
|
mbd.dwStyle |= MB_OKCANCEL;
|
|
mbd.pidButton = aidButton;
|
|
mbd.ppszButtonText = apstrButton;
|
|
mbd.cButtons = cButtons;
|
|
mbd.DefButton = defbutton;
|
|
mbd.CancelId = sebcp.wBtnCancel;
|
|
LeaveCrit();
|
|
dwResponse = SoftModalMessageBox( &mbd );
|
|
EnterCrit();
|
|
|
|
if (NtUserHardErrorControl(HardErrorDetach, NULL))
|
|
gfHardError = HEF_SWITCH;
|
|
}
|
|
|
|
} while (gfHardError == HEF_SWITCH);
|
|
} else {
|
|
|
|
//
|
|
// We have selected mode 1 and the error is from within the system.
|
|
// log the message and continue
|
|
// Or, We selected mode 2 which says to log all hard errors
|
|
//
|
|
|
|
LogErrorPopup(sebcp.szTitle,sebcp.szMessage);
|
|
dwResponse = ResponseOk;
|
|
}
|
|
if (fAlloc) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, (PVOID)sebcp.szTitle);
|
|
RtlFreeHeap(RtlProcessHeap(), 0, (PVOID)sebcp.szMessage);
|
|
}
|
|
|
|
return dwResponse;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* UserHardError
|
|
*
|
|
* Called from CSR to pop up hard error messages
|
|
*
|
|
* History:
|
|
* 07-03-91 JimA Created.
|
|
\***************************************************************************/
|
|
|
|
VOID UserHardError(
|
|
PCSR_THREAD pt,
|
|
PHARDERROR_MSG pmsg)
|
|
{
|
|
PHARDERRORINFO phi;
|
|
HANDLE hEvent;
|
|
|
|
/*
|
|
* Set up error return in case of failure.
|
|
*/
|
|
pmsg->Response = ResponseNotHandled;
|
|
|
|
/*
|
|
* Return if the process has terminated.
|
|
*/
|
|
if (pt != NULL && pt->Process->Flags & CSR_PROCESS_TERMINATED)
|
|
return;
|
|
|
|
EnterCrit(); // to synchronize heap calls
|
|
|
|
phi = (PHARDERRORINFO)LocalAlloc(LPTR, sizeof(HARDERRORINFO));
|
|
if (phi == NULL) {
|
|
LeaveCrit();
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Set up how the handler will acknowledge the error.
|
|
*/
|
|
phi->pthread = pt;
|
|
if ( pt && pt->Process->ClientPort ) {
|
|
phi->pmsg = (PHARDERROR_MSG)LocalAlloc(LPTR,
|
|
pmsg->h.u1.s1.TotalLength);
|
|
if (phi->pmsg == NULL) {
|
|
LocalFree(phi);
|
|
LeaveCrit();
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Do a wait reply for csr clients
|
|
*/
|
|
RtlCopyMemory(phi->pmsg, pmsg, pmsg->h.u1.s1.TotalLength);
|
|
pmsg->Response = (ULONG)-1;
|
|
hEvent = NULL;
|
|
} else {
|
|
hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if (hEvent == NULL) {
|
|
LocalFree(phi);
|
|
LeaveCrit();
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Wait for the user to acknowledge the error if it's coming from a
|
|
* non-csr source
|
|
*/
|
|
phi->pmsg = pmsg;
|
|
phi->hEventHardError = hEvent;
|
|
}
|
|
|
|
/*
|
|
* Queue the error message.
|
|
*/
|
|
phi->phiNext = gphiList;
|
|
gphiList = phi;
|
|
|
|
/*
|
|
* If no other thread is currently handling the errors, this
|
|
* thread will do it.
|
|
*/
|
|
if (gdwHardErrorThreadId == 0) {
|
|
HardErrorHandler();
|
|
}
|
|
|
|
/*
|
|
* If there is an event handle, wait for it.
|
|
*/
|
|
LeaveCrit();
|
|
if (hEvent != NULL) {
|
|
NtWaitForSingleObject(hEvent, FALSE, NULL);
|
|
NtClose(hEvent);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* 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(
|
|
DWORD dwProcessId,
|
|
BOOL fForce)
|
|
{
|
|
HDESK hdeskSave;
|
|
PHARDERRORINFO phi, *pphi;
|
|
BOOL fHasError = FALSE;
|
|
BOOL fWasActive = FALSE;
|
|
BOOL fWasInCrit;
|
|
HANDLE hThreadId;
|
|
int cTries = 0;
|
|
|
|
if (gphiList == NULL)
|
|
return FALSE;
|
|
|
|
/*
|
|
* Save the state of the critical section. If we are in it,
|
|
* we must leave it prior to calling GetThreadDesktop.
|
|
*/
|
|
hThreadId = NtCurrentTeb()->ClientId.UniqueThread;
|
|
fWasInCrit = (gcsUserSrv.OwningThread == hThreadId);
|
|
if (fWasInCrit)
|
|
LeaveCrit();
|
|
|
|
/*
|
|
* Save current desktop in case we need to change desktops
|
|
* to activate windows.
|
|
*/
|
|
hdeskSave = GetThreadDesktop(GetCurrentThreadId());
|
|
|
|
EnterCrit();
|
|
|
|
for (pphi = &gphiList; *pphi != NULL; ) {
|
|
if ((*pphi)->pthread != NULL &&
|
|
(*pphi)->pthread->ClientId.UniqueProcess == (HANDLE)dwProcessId) {
|
|
|
|
/*
|
|
* Found a hard error message.
|
|
*/
|
|
fHasError = TRUE;
|
|
if (fForce) {
|
|
|
|
/*
|
|
* Unlink it from the list.
|
|
*/
|
|
phi = *pphi;
|
|
*pphi = phi->phiNext;
|
|
fWasActive = (phi->phiNext == NULL);
|
|
|
|
/*
|
|
* Acknowledge the error as not handled.
|
|
*/
|
|
phi->pmsg->Response = ResponseNotHandled;
|
|
if (phi->hEventHardError == NULL) {
|
|
NtReplyPort(phi->pthread->Process->ClientPort,
|
|
(PPORT_MESSAGE)phi->pmsg);
|
|
LeaveCrit();
|
|
CsrDereferenceThread(phi->pthread);
|
|
EnterCrit();
|
|
LocalFree(phi->pmsg);
|
|
/*
|
|
* The list might have changed while we were outside the
|
|
* critical section, so start over.
|
|
*/
|
|
pphi = &gphiList;
|
|
cTries++;
|
|
} else {
|
|
NtSetEvent(phi->hEventHardError, NULL);
|
|
}
|
|
LocalFree(phi);
|
|
/*
|
|
* Bail out if we've already started over a bunch of times.
|
|
*/
|
|
if (cTries > 16) {
|
|
KdPrint(("BoostHardError exceeded max loop count\n"));
|
|
return TRUE;
|
|
}
|
|
} else {
|
|
|
|
/*
|
|
* If this is the last one in the list, we don't
|
|
* need to do anything to bring it up, just
|
|
* need to activate the popup.
|
|
*/
|
|
if ((*pphi)->phiNext == NULL) {
|
|
HWND hwndError = NULL;
|
|
|
|
/*
|
|
* Make the hard error foreground.
|
|
*/
|
|
EnumThreadWindows((DWORD)hThreadId,
|
|
WindowEnumProc, (LPARAM)&hwndError);
|
|
if (hwndError != NULL &&
|
|
NtUserHardErrorControl(HardErrorAttachNoQueue, NULL)) {
|
|
|
|
SetForegroundWindow(hwndError);
|
|
|
|
/*
|
|
* Release the desktop that was used.
|
|
*/
|
|
NtUserHardErrorControl(HardErrorDetachNoQueue, hdeskSave);
|
|
}
|
|
if (!fWasInCrit)
|
|
LeaveCrit();
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Unlink it from the list.
|
|
*/
|
|
phi = *pphi;
|
|
*pphi = phi->phiNext;
|
|
|
|
/*
|
|
* Put it on the tail of the list so it will come up first.
|
|
*/
|
|
while (*pphi != NULL)
|
|
pphi = &(*pphi)->phiNext;
|
|
*pphi = phi;
|
|
phi->phiNext = NULL;
|
|
break;
|
|
}
|
|
} else {
|
|
|
|
/*
|
|
* Step to the next error in the list.
|
|
*/
|
|
pphi = &(*pphi)->phiNext;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If the diplayed error was cleared and there is still
|
|
* a handler thread, restart the popup.
|
|
*/
|
|
if (fWasActive && gdwHardErrorThreadId != 0) {
|
|
gfHardError = HEF_RESTART;
|
|
PostThreadMessage(gdwHardErrorThreadId, WM_QUIT, 0, 0);
|
|
}
|
|
|
|
if (!fWasInCrit)
|
|
LeaveCrit();
|
|
|
|
return fHasError;
|
|
}
|