* Copyright (c) 1996 Onno Hovers, (onno@stack.urc.tue.nl)
* Copyright (c) 1999 Alexandre Julliard
*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
* Notes:
* What really happens behind the scenes of those new
* __try{...}__except(..){....} and
* __try{...}__finally{...}
* statements is simply not documented by Microsoft. There could be different
- * reasons for this:
- * One reason could be that they try to hide the fact that exception
- * handling in Win32 looks almost the same as in OS/2 2.x.
+ * reasons for this:
+ * One reason could be that they try to hide the fact that exception
+ * handling in Win32 looks almost the same as in OS/2 2.x.
* Another reason could be that Microsoft does not want others to write
- * binary compatible implementations of the Win32 API (like us).
+ * binary compatible implementations of the Win32 API (like us).
*
- * Whatever the reason, THIS SUCKS!! Ensuring portability or future
- * compatibility may be valid reasons to keep some things undocumented.
- * But exception handling is so basic to Win32 that it should be
+ * Whatever the reason, THIS SUCKS!! Ensuring portability or future
+ * compatibility may be valid reasons to keep some things undocumented.
+ * But exception handling is so basic to Win32 that it should be
* documented!
*
*/
+#include "config.h"
+#include "wine/port.h"
#include <stdio.h>
#include "windef.h"
#include "winerror.h"
-#include "ntddk.h"
+#include "winternl.h"
+#include "wingdi.h"
+#include "winuser.h"
#include "wine/exception.h"
-#include "callback.h"
+#include "wine/library.h"
#include "thread.h"
#include "stackframe.h"
-#include "server.h"
-#include "debugtools.h"
+#include "excpt.h"
+#include "wine/server.h"
+#include "wine/unicode.h"
+#include "wine/debug.h"
-DEFAULT_DEBUG_CHANNEL(seh);
+WINE_DEFAULT_DEBUG_CHANNEL(seh);
static PTOP_LEVEL_EXCEPTION_FILTER top_filter;
+typedef INT (WINAPI *MessageBoxA_funcptr)(HWND,LPCSTR,LPCSTR,UINT);
+typedef INT (WINAPI *MessageBoxW_funcptr)(HWND,LPCWSTR,LPCWSTR,UINT);
/*******************************************************************
- * RaiseException (KERNEL32.418)
+ * RaiseException (KERNEL32.@)
*/
void WINAPI RaiseException( DWORD code, DWORD flags, DWORD nbargs, const LPDWORD args )
{
EXCEPTION_RECORD record;
- /* Compose an exception record */
-
+ /* Compose an exception record */
+
record.ExceptionCode = code;
record.ExceptionFlags = flags & EH_NONCONTINUABLE;
record.ExceptionRecord = NULL;
/*******************************************************************
* format_exception_msg
*/
-static void format_exception_msg( const EXCEPTION_POINTERS *ptr, char *buffer )
+static int format_exception_msg( const EXCEPTION_POINTERS *ptr, char *buffer, int size )
{
const EXCEPTION_RECORD *rec = ptr->ExceptionRecord;
+ int len,len2;
switch(rec->ExceptionCode)
{
case EXCEPTION_INT_DIVIDE_BY_ZERO:
- sprintf( buffer, "Unhandled division by zero" );
+ len = snprintf( buffer, size, "Unhandled division by zero" );
break;
case EXCEPTION_INT_OVERFLOW:
- sprintf( buffer, "Unhandled overflow" );
+ len = snprintf( buffer, size, "Unhandled overflow" );
break;
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
- sprintf( buffer, "Unhandled array bounds" );
+ len = snprintf( buffer, size, "Unhandled array bounds" );
break;
case EXCEPTION_ILLEGAL_INSTRUCTION:
- sprintf( buffer, "Unhandled illegal instruction" );
+ len = snprintf( buffer, size, "Unhandled illegal instruction" );
break;
case EXCEPTION_STACK_OVERFLOW:
- sprintf( buffer, "Unhandled stack overflow" );
+ len = snprintf( buffer, size, "Unhandled stack overflow" );
break;
case EXCEPTION_PRIV_INSTRUCTION:
- sprintf( buffer, "Unhandled priviledged instruction" );
+ len = snprintf( buffer, size, "Unhandled privileged instruction" );
break;
case EXCEPTION_ACCESS_VIOLATION:
if (rec->NumberParameters == 2)
- sprintf( buffer, "Unhandled page fault on %s access to 0x%08lx",
+ len = snprintf( buffer, size, "Unhandled page fault on %s access to 0x%08lx",
rec->ExceptionInformation[0] ? "write" : "read",
rec->ExceptionInformation[1]);
else
- sprintf( buffer, "Unhandled page fault");
+ len = snprintf( buffer, size, "Unhandled page fault");
break;
case EXCEPTION_DATATYPE_MISALIGNMENT:
- sprintf( buffer, "Unhandled alignment" );
+ len = snprintf( buffer, size, "Unhandled alignment" );
break;
case CONTROL_C_EXIT:
- sprintf( buffer, "Unhandled ^C");
+ len = snprintf( buffer, size, "Unhandled ^C");
break;
case EXCEPTION_CRITICAL_SECTION_WAIT:
- sprintf( buffer, "Critical section %08lx wait failed",
+ len = snprintf( buffer, size, "Critical section %08lx wait failed",
rec->ExceptionInformation[0]);
break;
case EXCEPTION_WINE_STUB:
- sprintf( buffer, "Unimplemented function %s.%s called",
+ len = snprintf( buffer, size, "Unimplemented function %s.%s called",
(char *)rec->ExceptionInformation[0], (char *)rec->ExceptionInformation[1] );
break;
+ case EXCEPTION_WINE_ASSERTION:
+ len = snprintf( buffer, size, "Assertion failed" );
+ break;
case EXCEPTION_VM86_INTx:
- sprintf( buffer, "Unhandled interrupt %02lx in vm86 mode",
+ len = snprintf( buffer, size, "Unhandled interrupt %02lx in vm86 mode",
rec->ExceptionInformation[0]);
break;
case EXCEPTION_VM86_STI:
- sprintf( buffer, "Unhandled sti in vm86 mode");
+ len = snprintf( buffer, size, "Unhandled sti in vm86 mode");
break;
case EXCEPTION_VM86_PICRETURN:
- sprintf( buffer, "Unhandled PIC return in vm86 mode");
+ len = snprintf( buffer, size, "Unhandled PIC return in vm86 mode");
break;
default:
- sprintf( buffer, "Unhandled exception 0x%08lx", rec->ExceptionCode);
+ len = snprintf( buffer, size, "Unhandled exception 0x%08lx", rec->ExceptionCode);
break;
}
+ if ((len<0) || (len>=size))
+ return -1;
#ifdef __i386__
- if (ptr->ContextRecord->SegCs != __get_cs())
- sprintf( buffer+strlen(buffer), " at address 0x%04lx:0x%08lx.\n",
- ptr->ContextRecord->SegCs, (DWORD)ptr->ExceptionRecord->ExceptionAddress );
+ if (ptr->ContextRecord->SegCs != wine_get_cs())
+ len2 = snprintf(buffer+len, size-len,
+ " at address 0x%04lx:0x%08lx.\nDo you wish to debug it ?",
+ ptr->ContextRecord->SegCs,
+ (DWORD)ptr->ExceptionRecord->ExceptionAddress);
else
#endif
- sprintf( buffer+strlen(buffer), " at address 0x%08lx.\n",
- (DWORD)ptr->ExceptionRecord->ExceptionAddress );
- strcat( buffer, "Do you wish to debug it ?" );
+ len2 = snprintf(buffer+len, size-len,
+ " at address 0x%08lx.\nDo you wish to debug it ?",
+ (DWORD)ptr->ExceptionRecord->ExceptionAddress);
+ if ((len2<0) || (len>=size-len))
+ return -1;
+ return len+len2;
}
-/*******************************************************************
- * UnhandledExceptionFilter (KERNEL32.537)
+/**********************************************************************
+ * send_debug_event
+ *
+ * Send an EXCEPTION_DEBUG_EVENT event to the debugger.
*/
-DWORD WINAPI UnhandledExceptionFilter(PEXCEPTION_POINTERS epointers)
+static int send_debug_event( EXCEPTION_RECORD *rec, int first_chance, CONTEXT *context )
{
- char format[256];
- char buffer[256];
- HKEY hDbgConf;
- DWORD bAuto = FALSE;
- DWORD ret = EXCEPTION_EXECUTE_HANDLER;
- int status;
+ int ret;
+ HANDLE handle = 0;
- /* send a last chance event to the debugger */
- SERVER_START_REQ
+ SERVER_START_REQ( queue_exception_event )
{
- struct exception_event_request *req = server_alloc_req( sizeof(*req),
- sizeof(EXCEPTION_RECORD)+sizeof(CONTEXT) );
- CONTEXT *context_ptr = server_data_ptr(req);
- EXCEPTION_RECORD *rec_ptr = (EXCEPTION_RECORD *)(context_ptr + 1);
- req->first = 0;
- *rec_ptr = *epointers->ExceptionRecord;
- *context_ptr = *epointers->ContextRecord;
- if (!server_call_noerr( REQ_EXCEPTION_EVENT )) *epointers->ContextRecord = *context_ptr;
- status = req->status;
+ req->first = first_chance;
+ wine_server_add_data( req, context, sizeof(*context) );
+ wine_server_add_data( req, rec, sizeof(*rec) );
+ if (!wine_server_call(req)) handle = reply->handle;
}
SERVER_END_REQ;
+ if (!handle) return 0; /* no debugger present or other error */
- switch (status)
+ /* No need to wait on the handle since the process gets suspended
+ * once the event is passed to the debugger, so when we get back
+ * here the event has been continued already.
+ */
+ SERVER_START_REQ( get_exception_status )
{
- case DBG_CONTINUE:
- return EXCEPTION_CONTINUE_EXECUTION;
- case DBG_EXCEPTION_NOT_HANDLED:
- TerminateProcess( GetCurrentProcess(), epointers->ExceptionRecord->ExceptionCode );
- break; /* not reached */
- case 0: /* no debugger is present */
- break;
- default:
- FIXME("Unsupported yet debug continue value %d (please report)\n", status);
+ req->handle = handle;
+ wine_server_set_reply( req, context, sizeof(*context) );
+ wine_server_call( req );
+ ret = reply->status;
}
+ SERVER_END_REQ;
+ NtClose( handle );
+ return ret;
+}
+
+/******************************************************************
+ * start_debugger
+ *
+ * Does the effective debugger startup according to 'format'
+ */
+static BOOL start_debugger(PEXCEPTION_POINTERS epointers, HANDLE hEvent)
+{
+ OBJECT_ATTRIBUTES attr;
+ UNICODE_STRING nameW;
+ HKEY hDbgConf;
+ DWORD bAuto = FALSE;
+ PROCESS_INFORMATION info;
+ STARTUPINFOA startup;
+ char* cmdline;
+ char* format = NULL;
+ BOOL ret = FALSE;
- if (top_filter)
+ static const WCHAR AeDebugW[] = {'M','a','c','h','i','n','e','\\',
+ 'S','o','f','t','w','a','r','e','\\',
+ 'M','i','c','r','o','s','o','f','t','\\',
+ 'W','i','n','d','o','w','s',' ','N','T','\\',
+ 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
+ 'A','e','D','e','b','u','g',0};
+ static const WCHAR DebuggerW[] = {'D','e','b','u','g','g','e','r',0};
+ static const WCHAR AutoW[] = {'A','u','t','o',0};
+
+ MESSAGE("wine: Unhandled exception, starting debugger...\n");
+
+ attr.Length = sizeof(attr);
+ attr.RootDirectory = 0;
+ attr.ObjectName = &nameW;
+ attr.Attributes = 0;
+ attr.SecurityDescriptor = NULL;
+ attr.SecurityQualityOfService = NULL;
+ RtlInitUnicodeString( &nameW, AeDebugW );
+
+ if (!NtOpenKey( &hDbgConf, KEY_ALL_ACCESS, &attr ))
{
- DWORD ret = top_filter( epointers );
- if (ret != EXCEPTION_CONTINUE_SEARCH) return ret;
- }
+ char buffer[64];
+ KEY_VALUE_PARTIAL_INFORMATION *info;
+ DWORD format_size = 0;
- /* FIXME: Should check the current error mode */
+ RtlInitUnicodeString( &nameW, DebuggerW );
+ if (NtQueryValueKey( hDbgConf, &nameW, KeyValuePartialInformation,
+ NULL, 0, &format_size ) == STATUS_BUFFER_OVERFLOW)
+ {
+ char *data = HeapAlloc(GetProcessHeap(), 0, format_size);
+ NtQueryValueKey( hDbgConf, &nameW, KeyValuePartialInformation,
+ data, format_size, &format_size );
+ info = (KEY_VALUE_PARTIAL_INFORMATION *)data;
+ RtlUnicodeToMultiByteSize( &format_size, (WCHAR *)info->Data, info->DataLength );
+ format = HeapAlloc( GetProcessHeap(), 0, format_size+1 );
+ RtlUnicodeToMultiByteN( format, format_size, NULL,
+ (WCHAR *)info->Data, info->DataLength );
+ format[format_size] = 0;
- if (!RegOpenKeyA(HKEY_LOCAL_MACHINE,
- "Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug",
- &hDbgConf)) {
- DWORD type;
- DWORD count;
+ if (info->Type == REG_EXPAND_SZ)
+ {
+ char* tmp;
- count = sizeof(format);
- if (RegQueryValueExA(hDbgConf, "Debugger", 0, &type, format, &count))
- format[0] = 0;
+ /* Expand environment variable references */
+ format_size=ExpandEnvironmentStringsA(format,NULL,0);
+ tmp=HeapAlloc(GetProcessHeap(), 0, format_size);
+ ExpandEnvironmentStringsA(format,tmp,format_size);
+ HeapFree(GetProcessHeap(), 0, format);
+ format=tmp;
+ }
+ HeapFree( GetProcessHeap(), 0, data );
+ }
- count = sizeof(bAuto);
- if (RegQueryValueExA(hDbgConf, "Auto", 0, &type, (char*)&bAuto, &count))
- bAuto = TRUE;
- else if (type == REG_SZ)
+ RtlInitUnicodeString( &nameW, AutoW );
+ if (!NtQueryValueKey( hDbgConf, &nameW, KeyValuePartialInformation,
+ buffer, sizeof(buffer)-sizeof(WCHAR), &format_size ))
{
- char autostr[10];
- count = sizeof(autostr);
- if (!RegQueryValueExA(hDbgConf, "Auto", 0, &type, autostr, &count))
- bAuto = atoi(autostr);
+ info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
+ if (info->Type == REG_DWORD) memcpy( &bAuto, info->Data, sizeof(DWORD) );
+ else if (info->Type == REG_SZ)
+ {
+ WCHAR *str = (WCHAR *)info->Data;
+ str[info->DataLength/sizeof(WCHAR)] = 0;
+ bAuto = atoiW( str );
+ }
}
- RegCloseKey(hDbgConf);
- } else {
- /* format[0] = 0; */
- strcpy(format, "debugger/winedbg %ld %ld");
+ else bAuto = TRUE;
+
+ NtClose(hDbgConf);
}
- if (!bAuto && Callout.MessageBoxA) {
- format_exception_msg( epointers, buffer );
- if (Callout.MessageBoxA( 0, buffer, "Error", MB_YESNO | MB_ICONHAND ) == IDNO) {
- TRACE("Killing process\n");
- return EXCEPTION_EXECUTE_HANDLER;
- }
+ if (format)
+ {
+ cmdline = HeapAlloc(GetProcessHeap(), 0, strlen(format) + 2*20);
+ sprintf(cmdline, format, GetCurrentProcessId(), hEvent);
+ HeapFree(GetProcessHeap(), 0, format);
}
-
- if (format[0]) {
- HANDLE hEvent;
- PROCESS_INFORMATION info;
- STARTUPINFOA startup;
- OBJECT_ATTRIBUTES attr;
-
- attr.Length = sizeof(attr);
- attr.RootDirectory = 0;
- attr.Attributes = OBJ_INHERIT;
- attr.ObjectName = NULL;
- attr.SecurityDescriptor = NULL;
- attr.SecurityQualityOfService = NULL;
-
- TRACE("Starting debugger (fmt=%s)\n", format);
- NtCreateEvent( &hEvent, EVENT_ALL_ACCESS, &attr, FALSE, FALSE );
- sprintf(buffer, format, GetCurrentProcessId(), hEvent);
- memset(&startup, 0, sizeof(startup));
- startup.cb = sizeof(startup);
- startup.dwFlags = STARTF_USESHOWWINDOW;
- startup.wShowWindow = SW_SHOWNORMAL;
- if (CreateProcessA(NULL, buffer, NULL, NULL,
- TRUE, 0, NULL, NULL, &startup, &info)) {
- WaitForSingleObject(hEvent, INFINITE);
- ret = EXCEPTION_CONTINUE_SEARCH;
- } else {
- ERR("Couldn't start debugger (%s) (%ld)\n"
- "Read the documentation on how to set up winedbg or another debugger\n",
- buffer, GetLastError());
- }
- CloseHandle(hEvent);
- } else {
- ERR("No standard debugger defined in the registry => no debugging session\n");
+ else
+ {
+ cmdline = HeapAlloc(GetProcessHeap(), 0, 80);
+ sprintf(cmdline, "winedbg --debugmsg -all --auto %ld %p", GetCurrentProcessId(), hEvent);
}
-
+
+ if (!bAuto)
+ {
+ HMODULE mod = GetModuleHandleA( "user32.dll" );
+ MessageBoxA_funcptr pMessageBoxA = NULL;
+
+ if (mod) pMessageBoxA = (MessageBoxA_funcptr)GetProcAddress( mod, "MessageBoxA" );
+ if (pMessageBoxA)
+ {
+ char buffer[256];
+ format_exception_msg( epointers, buffer, sizeof(buffer) );
+ if (pMessageBoxA( 0, buffer, "Exception raised", MB_YESNO | MB_ICONHAND ) == IDNO)
+ {
+ TRACE("Killing process\n");
+ goto EXIT;
+ }
+ }
+ }
+
+ TRACE("Starting debugger %s\n", debugstr_a(cmdline));
+ memset(&startup, 0, sizeof(startup));
+ startup.cb = sizeof(startup);
+ startup.dwFlags = STARTF_USESHOWWINDOW;
+ startup.wShowWindow = SW_SHOWNORMAL;
+ ret = CreateProcessA(NULL, cmdline, NULL, NULL, TRUE, 0, NULL, NULL, &startup, &info);
+
+ if (ret) WaitForSingleObject(hEvent, INFINITE); /* wait for debugger to come up... */
+ else ERR("Couldn't start debugger (%s) (%ld)\n"
+ "Read the Wine Developers Guide on how to set up winedbg or another debugger\n",
+ debugstr_a(cmdline), GetLastError());
+EXIT:
+ HeapFree(GetProcessHeap(), 0, cmdline);
return ret;
}
+/******************************************************************
+ * start_debugger_atomic
+ *
+ * starts the debugger in an atomic way:
+ * - either the debugger is not started and it is started
+ * - or the debugger has already been started by another thread
+ * - or the debugger couldn't be started
+ *
+ * returns TRUE for the two first conditions, FALSE for the last
+ */
+static int start_debugger_atomic(PEXCEPTION_POINTERS epointers)
+{
+ static HANDLE hRunOnce /* = 0 */;
+
+ if (hRunOnce == 0)
+ {
+ OBJECT_ATTRIBUTES attr;
+ HANDLE hEvent;
+
+ attr.Length = sizeof(attr);
+ attr.RootDirectory = 0;
+ attr.Attributes = OBJ_INHERIT;
+ attr.ObjectName = NULL;
+ attr.SecurityDescriptor = NULL;
+ attr.SecurityQualityOfService = NULL;
+
+ /* ask for manual reset, so that once the debugger is started,
+ * every thread will know it */
+ NtCreateEvent( &hEvent, EVENT_ALL_ACCESS, &attr, TRUE, FALSE );
+ if (InterlockedCompareExchangePointer( (PVOID)&hRunOnce, hEvent, 0 ) == 0)
+ {
+ /* ok, our event has been set... we're the winning thread */
+ BOOL ret = start_debugger( epointers, hRunOnce );
+ DWORD tmp;
+
+ if (!ret)
+ {
+ /* so that the other threads won't be stuck */
+ NtSetEvent( hRunOnce, &tmp );
+ }
+ return ret;
+ }
+
+ /* someone beat us here... */
+ CloseHandle( hEvent );
+ }
+
+ /* and wait for the winner to have actually created the debugger */
+ WaitForSingleObject( hRunOnce, INFINITE );
+ /* in fact, here, we only know that someone has tried to start the debugger,
+ * we'll know by reposting the exception if it has actually attached
+ * to the current process */
+ return TRUE;
+}
+
+
+/*******************************************************************
+ * check_resource_write
+ *
+ * Check if the exception is a write attempt to the resource data.
+ * If yes, we unprotect the resources to let broken apps continue
+ * (Windows does this too).
+ */
+inline static BOOL check_resource_write( const EXCEPTION_RECORD *rec )
+{
+ void *addr, *rsrc;
+ DWORD size;
+ MEMORY_BASIC_INFORMATION info;
+
+ if (rec->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) return FALSE;
+ if (!rec->ExceptionInformation[0]) return FALSE; /* not a write access */
+ addr = (void *)rec->ExceptionInformation[1];
+ if (!VirtualQuery( addr, &info, sizeof(info) )) return FALSE;
+ if (!(rsrc = RtlImageDirectoryEntryToData( (HMODULE)info.AllocationBase, TRUE,
+ IMAGE_DIRECTORY_ENTRY_RESOURCE, &size )))
+ return FALSE;
+ if (addr < rsrc || (char *)addr >= (char *)rsrc + size) return FALSE;
+ FIXME( "Broken app is writing to the resource data, enabling work-around\n" );
+ VirtualProtect( rsrc, size, PAGE_WRITECOPY, NULL );
+ return TRUE;
+}
+
+
+/*******************************************************************
+ * UnhandledExceptionFilter (KERNEL32.@)
+ */
+DWORD WINAPI UnhandledExceptionFilter(PEXCEPTION_POINTERS epointers)
+{
+ int status;
+ int loop = 0;
+
+ if (check_resource_write( epointers->ExceptionRecord )) return EXCEPTION_CONTINUE_EXECUTION;
+
+ for (loop = 0; loop <= 1; loop++)
+ {
+ /* send a last chance event to the debugger */
+ status = send_debug_event( epointers->ExceptionRecord, FALSE, epointers->ContextRecord );
+ switch (status)
+ {
+ case DBG_CONTINUE:
+ return EXCEPTION_CONTINUE_EXECUTION;
+ case DBG_EXCEPTION_NOT_HANDLED:
+ TerminateProcess( GetCurrentProcess(), epointers->ExceptionRecord->ExceptionCode );
+ break; /* not reached */
+ case 0: /* no debugger is present */
+ if (epointers->ExceptionRecord->ExceptionCode == CONTROL_C_EXIT)
+ {
+ /* do not launch the debugger on ^C, simply terminate the process */
+ TerminateProcess( GetCurrentProcess(), 1 );
+ }
+ /* second try, the debugger isn't present... */
+ if (loop == 1) return EXCEPTION_EXECUTE_HANDLER;
+ break;
+ default:
+ FIXME("Unsupported yet debug continue value %d (please report)\n", status);
+ return EXCEPTION_EXECUTE_HANDLER;
+ }
+
+ /* should only be there when loop == 0 */
+
+ if (top_filter)
+ {
+ DWORD ret = top_filter( epointers );
+ if (ret != EXCEPTION_CONTINUE_SEARCH) return ret;
+ }
+
+ /* FIXME: Should check the current error mode */
+
+ if (!start_debugger_atomic( epointers ))
+ return EXCEPTION_EXECUTE_HANDLER;
+ /* now that we should have a debugger attached, try to resend event */
+ }
+
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+
/***********************************************************************
- * SetUnhandledExceptionFilter (KERNEL32.516)
+ * SetUnhandledExceptionFilter (KERNEL32.@)
*/
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter(
LPTOP_LEVEL_EXCEPTION_FILTER filter )
/**************************************************************************
- * FatalAppExitA (KERNEL32.108)
+ * FatalAppExitA (KERNEL32.@)
*/
void WINAPI FatalAppExitA( UINT action, LPCSTR str )
{
+ HMODULE mod = GetModuleHandleA( "user32.dll" );
+ MessageBoxA_funcptr pMessageBoxA = NULL;
+
WARN("AppExit\n");
- if (Callout.MessageBoxA)
- Callout.MessageBoxA( 0, str, NULL, MB_SYSTEMMODAL | MB_OK );
- else
- ERR( "%s\n", debugstr_a(str) );
+
+ if (mod) pMessageBoxA = (MessageBoxA_funcptr)GetProcAddress( mod, "MessageBoxA" );
+ if (pMessageBoxA) pMessageBoxA( 0, str, NULL, MB_SYSTEMMODAL | MB_OK );
+ else ERR( "%s\n", debugstr_a(str) );
ExitProcess(0);
}
/**************************************************************************
- * FatalAppExitW (KERNEL32.109)
+ * FatalAppExitW (KERNEL32.@)
*/
void WINAPI FatalAppExitW( UINT action, LPCWSTR str )
{
+ HMODULE mod = GetModuleHandleA( "user32.dll" );
+ MessageBoxW_funcptr pMessageBoxW = NULL;
+
WARN("AppExit\n");
- if (Callout.MessageBoxW)
- Callout.MessageBoxW( 0, str, NULL, MB_SYSTEMMODAL | MB_OK );
- else
- ERR( "%s\n", debugstr_w(str) );
+
+ if (mod) pMessageBoxW = (MessageBoxW_funcptr)GetProcAddress( mod, "MessageBoxW" );
+ if (pMessageBoxW) pMessageBoxW( 0, str, NULL, MB_SYSTEMMODAL | MB_OK );
+ else ERR( "%s\n", debugstr_w(str) );
ExitProcess(0);
}