- clarify many error messages
[wine] / win32 / except.c
1 /*
2  * Win32 exception functions
3  *
4  * Copyright (c) 1996 Onno Hovers, (onno@stack.urc.tue.nl)
5  * Copyright (c) 1999 Alexandre Julliard
6  *
7  * Notes:
8  *  What really happens behind the scenes of those new
9  *  __try{...}__except(..){....}  and
10  *  __try{...}__finally{...}
11  *  statements is simply not documented by Microsoft. There could be different
12  *  reasons for this: 
13  *  One reason could be that they try to hide the fact that exception 
14  *  handling in Win32 looks almost the same as in OS/2 2.x.  
15  *  Another reason could be that Microsoft does not want others to write
16  *  binary compatible implementations of the Win32 API (like us).  
17  *
18  *  Whatever the reason, THIS SUCKS!! Ensuring portability or future 
19  *  compatibility may be valid reasons to keep some things undocumented. 
20  *  But exception handling is so basic to Win32 that it should be 
21  *  documented!
22  *
23  */
24
25 #include <stdio.h>
26 #include "windef.h"
27 #include "winerror.h"
28 #include "ntddk.h"
29 #include "wine/exception.h"
30 #include "callback.h"
31 #include "thread.h"
32 #include "stackframe.h"
33 #include "server.h"
34 #include "debugtools.h"
35
36 DEFAULT_DEBUG_CHANNEL(seh);
37
38 static PTOP_LEVEL_EXCEPTION_FILTER top_filter;
39
40
41 /*******************************************************************
42  *         RaiseException  (KERNEL32.418)
43  */
44 void WINAPI RaiseException( DWORD code, DWORD flags, DWORD nbargs, const LPDWORD args )
45 {
46     EXCEPTION_RECORD record;
47
48     /* Compose an exception record */ 
49     
50     record.ExceptionCode    = code;
51     record.ExceptionFlags   = flags & EH_NONCONTINUABLE;
52     record.ExceptionRecord  = NULL;
53     record.ExceptionAddress = RaiseException;
54     if (nbargs && args)
55     {
56         if (nbargs > EXCEPTION_MAXIMUM_PARAMETERS) nbargs = EXCEPTION_MAXIMUM_PARAMETERS;
57         record.NumberParameters = nbargs;
58         memcpy( record.ExceptionInformation, args, nbargs * sizeof(*args) );
59     }
60     else record.NumberParameters = 0;
61
62     RtlRaiseException( &record );
63 }
64
65
66 /*******************************************************************
67  *         format_exception_msg
68  */
69 static void format_exception_msg( const EXCEPTION_POINTERS *ptr, char *buffer )
70 {
71     const EXCEPTION_RECORD *rec = ptr->ExceptionRecord;
72
73     switch(rec->ExceptionCode)
74     {
75     case EXCEPTION_INT_DIVIDE_BY_ZERO:
76         sprintf( buffer, "Unhandled division by zero" );
77         break;
78     case EXCEPTION_INT_OVERFLOW:
79         sprintf( buffer, "Unhandled overflow" );
80         break;
81     case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
82         sprintf( buffer, "Unhandled array bounds" );
83         break;
84     case EXCEPTION_ILLEGAL_INSTRUCTION:
85         sprintf( buffer, "Unhandled illegal instruction" );
86         break;
87     case EXCEPTION_STACK_OVERFLOW:
88         sprintf( buffer, "Unhandled stack overflow" );
89         break;
90     case EXCEPTION_PRIV_INSTRUCTION:
91         sprintf( buffer, "Unhandled priviledged instruction" );
92         break;
93     case EXCEPTION_ACCESS_VIOLATION:
94         if (rec->NumberParameters == 2)
95             sprintf( buffer, "Unhandled page fault on %s access to 0x%08lx",
96                      rec->ExceptionInformation[0] ? "write" : "read",
97                      rec->ExceptionInformation[1]);
98         else
99             sprintf( buffer, "Unhandled page fault");
100         break;
101     case EXCEPTION_DATATYPE_MISALIGNMENT:
102         sprintf( buffer, "Unhandled alignment" );
103         break;
104     case CONTROL_C_EXIT:
105         sprintf( buffer, "Unhandled ^C");
106         break;
107     case EXCEPTION_CRITICAL_SECTION_WAIT:
108         sprintf( buffer, "Critical section %08lx wait failed",
109                  rec->ExceptionInformation[0]);
110         break;
111     case EXCEPTION_WINE_STUB:
112         sprintf( buffer, "Unimplemented function %s.%s called",
113                  (char *)rec->ExceptionInformation[0], (char *)rec->ExceptionInformation[1] );
114         break;
115     case EXCEPTION_VM86_INTx:
116         sprintf( buffer, "Unhandled interrupt %02lx in vm86 mode",
117                  rec->ExceptionInformation[0]);
118         break;
119     case EXCEPTION_VM86_STI:
120         sprintf( buffer, "Unhandled sti in vm86 mode");
121         break;
122     case EXCEPTION_VM86_PICRETURN:
123         sprintf( buffer, "Unhandled PIC return in vm86 mode");
124         break;
125     default:
126         sprintf( buffer, "Unhandled exception 0x%08lx", rec->ExceptionCode);
127         break;
128     }
129 #ifdef __i386__
130     if (ptr->ContextRecord->SegCs != __get_cs())
131         sprintf( buffer+strlen(buffer), " at address 0x%04lx:0x%08lx.\n",
132                  ptr->ContextRecord->SegCs, (DWORD)ptr->ExceptionRecord->ExceptionAddress );
133     else
134 #endif
135     sprintf( buffer+strlen(buffer), " at address 0x%08lx.\n",
136              (DWORD)ptr->ExceptionRecord->ExceptionAddress );
137     strcat( buffer, "Do you wish to debug it ?" );
138 }
139
140
141 /*******************************************************************
142  *         UnhandledExceptionFilter   (KERNEL32.537)
143  */
144 DWORD WINAPI UnhandledExceptionFilter(PEXCEPTION_POINTERS epointers)
145 {
146     char                format[256];
147     char                buffer[256];
148     HKEY                hDbgConf;
149     DWORD               bAuto = FALSE;
150     DWORD               ret = EXCEPTION_EXECUTE_HANDLER;
151     int status;
152
153     /* send a last chance event to the debugger */
154     SERVER_START_REQ
155     {
156         struct exception_event_request *req = server_alloc_req( sizeof(*req),
157                                                   sizeof(EXCEPTION_RECORD)+sizeof(CONTEXT) );
158         CONTEXT *context_ptr = server_data_ptr(req);
159         EXCEPTION_RECORD *rec_ptr = (EXCEPTION_RECORD *)(context_ptr + 1);
160         req->first   = 0;
161         *rec_ptr     = *epointers->ExceptionRecord;
162         *context_ptr = *epointers->ContextRecord;
163         if (!server_call_noerr( REQ_EXCEPTION_EVENT )) *epointers->ContextRecord = *context_ptr;
164         status = req->status;
165     }
166     SERVER_END_REQ;
167
168     switch (status)
169     {
170     case DBG_CONTINUE: 
171         return EXCEPTION_CONTINUE_EXECUTION;
172     case DBG_EXCEPTION_NOT_HANDLED: 
173         TerminateProcess( GetCurrentProcess(), epointers->ExceptionRecord->ExceptionCode );
174         break; /* not reached */
175     case 0: /* no debugger is present */
176         break;
177     default:    
178         FIXME("Unsupported yet debug continue value %d (please report)\n", status);
179     }
180
181     if (top_filter)
182     {
183         DWORD ret = top_filter( epointers );
184         if (ret != EXCEPTION_CONTINUE_SEARCH) return ret;
185     }
186
187     /* FIXME: Should check the current error mode */
188
189     if (!RegOpenKeyA(HKEY_LOCAL_MACHINE, 
190                      "Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug", 
191                      &hDbgConf)) {
192        DWORD    type;
193        DWORD    count;
194
195        count = sizeof(format);
196        if (RegQueryValueExA(hDbgConf, "Debugger", 0, &type, format, &count))
197           format[0] = 0;
198
199        count = sizeof(bAuto);
200        if (RegQueryValueExA(hDbgConf, "Auto", 0, &type, (char*)&bAuto, &count))
201           bAuto = TRUE;
202        else if (type == REG_SZ)
203        {
204            char autostr[10];
205            count = sizeof(autostr);
206            if (!RegQueryValueExA(hDbgConf, "Auto", 0, &type, autostr, &count))
207                bAuto = atoi(autostr);
208        }
209        RegCloseKey(hDbgConf);
210     } else {
211        /* format[0] = 0; */
212        strcpy(format, "debugger/winedbg %ld %ld");
213     }
214
215     if (!bAuto && Callout.MessageBoxA) {
216         format_exception_msg( epointers, buffer );
217        if (Callout.MessageBoxA( 0, buffer, "Error", MB_YESNO | MB_ICONHAND ) == IDNO) {
218           TRACE("Killing process\n");
219           return EXCEPTION_EXECUTE_HANDLER;
220        }
221     }
222     
223     if (format[0]) {
224        HANDLE                   hEvent;
225        PROCESS_INFORMATION      info;
226        STARTUPINFOA             startup;
227        OBJECT_ATTRIBUTES        attr;
228
229        attr.Length                   = sizeof(attr);
230        attr.RootDirectory            = 0;
231        attr.Attributes               = OBJ_INHERIT;
232        attr.ObjectName               = NULL;
233        attr.SecurityDescriptor       = NULL;
234        attr.SecurityQualityOfService = NULL;
235
236        TRACE("Starting debugger (fmt=%s)\n", format);
237        NtCreateEvent( &hEvent, EVENT_ALL_ACCESS, &attr, FALSE, FALSE );
238        sprintf(buffer, format, GetCurrentProcessId(), hEvent);
239        memset(&startup, 0, sizeof(startup));
240        startup.cb = sizeof(startup);
241        startup.dwFlags = STARTF_USESHOWWINDOW;
242        startup.wShowWindow = SW_SHOWNORMAL;
243        if (CreateProcessA(NULL, buffer, NULL, NULL, 
244                           TRUE, 0, NULL, NULL, &startup, &info)) {
245           WaitForSingleObject(hEvent, INFINITE);
246           ret = EXCEPTION_CONTINUE_SEARCH;
247        } else {
248            ERR("Couldn't start debugger (%s) (%ld)\n"
249                "Read the Wine Developers Guide on how to set up winedbg or another debugger\n",
250                buffer, GetLastError());
251        }
252        CloseHandle(hEvent);
253     } else {
254        ERR("No standard debugger defined in the registry => no debugging session\n");
255     }
256     
257     return ret;
258 }
259
260
261 /***********************************************************************
262  *            SetUnhandledExceptionFilter   (KERNEL32.516)
263  */
264 LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter(
265                                           LPTOP_LEVEL_EXCEPTION_FILTER filter )
266 {
267     LPTOP_LEVEL_EXCEPTION_FILTER old = top_filter;
268     top_filter = filter;
269     return old;
270 }
271
272
273 /**************************************************************************
274  *           FatalAppExitA   (KERNEL32.108)
275  */
276 void WINAPI FatalAppExitA( UINT action, LPCSTR str )
277 {
278     WARN("AppExit\n");
279     if (Callout.MessageBoxA)
280         Callout.MessageBoxA( 0, str, NULL, MB_SYSTEMMODAL | MB_OK );
281     else
282         ERR( "%s\n", debugstr_a(str) );
283     ExitProcess(0);
284 }
285
286
287 /**************************************************************************
288  *           FatalAppExitW   (KERNEL32.109)
289  */
290 void WINAPI FatalAppExitW( UINT action, LPCWSTR str )
291 {
292     WARN("AppExit\n");
293     if (Callout.MessageBoxW)
294         Callout.MessageBoxW( 0, str, NULL, MB_SYSTEMMODAL | MB_OK );
295     else
296         ERR( "%s\n", debugstr_w(str) );
297     ExitProcess(0);
298 }