- Use PeekMessage loop around GetMessage.
[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  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  * Notes:
22  *  What really happens behind the scenes of those new
23  *  __try{...}__except(..){....}  and
24  *  __try{...}__finally{...}
25  *  statements is simply not documented by Microsoft. There could be different
26  *  reasons for this:
27  *  One reason could be that they try to hide the fact that exception
28  *  handling in Win32 looks almost the same as in OS/2 2.x.
29  *  Another reason could be that Microsoft does not want others to write
30  *  binary compatible implementations of the Win32 API (like us).
31  *
32  *  Whatever the reason, THIS SUCKS!! Ensuring portability or future
33  *  compatibility may be valid reasons to keep some things undocumented.
34  *  But exception handling is so basic to Win32 that it should be
35  *  documented!
36  *
37  */
38
39 #include <stdio.h>
40 #include "windef.h"
41 #include "winerror.h"
42 #include "ntddk.h"
43 #include "wingdi.h"
44 #include "winuser.h"
45 #include "wine/exception.h"
46 #include "wine/library.h"
47 #include "thread.h"
48 #include "stackframe.h"
49 #include "wine/server.h"
50 #include "wine/debug.h"
51 #include "msvcrt/excpt.h"
52
53 WINE_DEFAULT_DEBUG_CHANNEL(seh);
54
55 static PTOP_LEVEL_EXCEPTION_FILTER top_filter;
56
57 typedef INT (WINAPI *MessageBoxA_funcptr)(HWND,LPCSTR,LPCSTR,UINT);
58 typedef INT (WINAPI *MessageBoxW_funcptr)(HWND,LPCWSTR,LPCWSTR,UINT);
59
60 /*******************************************************************
61  *         RaiseException  (KERNEL32.@)
62  */
63 void WINAPI RaiseException( DWORD code, DWORD flags, DWORD nbargs, const LPDWORD args )
64 {
65     EXCEPTION_RECORD record;
66
67     /* Compose an exception record */
68
69     record.ExceptionCode    = code;
70     record.ExceptionFlags   = flags & EH_NONCONTINUABLE;
71     record.ExceptionRecord  = NULL;
72     record.ExceptionAddress = RaiseException;
73     if (nbargs && args)
74     {
75         if (nbargs > EXCEPTION_MAXIMUM_PARAMETERS) nbargs = EXCEPTION_MAXIMUM_PARAMETERS;
76         record.NumberParameters = nbargs;
77         memcpy( record.ExceptionInformation, args, nbargs * sizeof(*args) );
78     }
79     else record.NumberParameters = 0;
80
81     RtlRaiseException( &record );
82 }
83
84
85 /*******************************************************************
86  *         format_exception_msg
87  */
88 static int format_exception_msg( const EXCEPTION_POINTERS *ptr, char *buffer, int size )
89 {
90     const EXCEPTION_RECORD *rec = ptr->ExceptionRecord;
91     int len,len2;
92
93     switch(rec->ExceptionCode)
94     {
95     case EXCEPTION_INT_DIVIDE_BY_ZERO:
96         len = snprintf( buffer, size, "Unhandled division by zero" );
97         break;
98     case EXCEPTION_INT_OVERFLOW:
99         len = snprintf( buffer, size, "Unhandled overflow" );
100         break;
101     case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
102         len = snprintf( buffer, size, "Unhandled array bounds" );
103         break;
104     case EXCEPTION_ILLEGAL_INSTRUCTION:
105         len = snprintf( buffer, size, "Unhandled illegal instruction" );
106         break;
107     case EXCEPTION_STACK_OVERFLOW:
108         len = snprintf( buffer, size, "Unhandled stack overflow" );
109         break;
110     case EXCEPTION_PRIV_INSTRUCTION:
111         len = snprintf( buffer, size, "Unhandled privileged instruction" );
112         break;
113     case EXCEPTION_ACCESS_VIOLATION:
114         if (rec->NumberParameters == 2)
115             len = snprintf( buffer, size, "Unhandled page fault on %s access to 0x%08lx",
116                      rec->ExceptionInformation[0] ? "write" : "read",
117                      rec->ExceptionInformation[1]);
118         else
119             len = snprintf( buffer, size, "Unhandled page fault");
120         break;
121     case EXCEPTION_DATATYPE_MISALIGNMENT:
122         len = snprintf( buffer, size, "Unhandled alignment" );
123         break;
124     case CONTROL_C_EXIT:
125         len = snprintf( buffer, size, "Unhandled ^C");
126         break;
127     case EXCEPTION_CRITICAL_SECTION_WAIT:
128         len = snprintf( buffer, size, "Critical section %08lx wait failed",
129                  rec->ExceptionInformation[0]);
130         break;
131     case EXCEPTION_WINE_STUB:
132         len = snprintf( buffer, size, "Unimplemented function %s.%s called",
133                  (char *)rec->ExceptionInformation[0], (char *)rec->ExceptionInformation[1] );
134         break;
135     case EXCEPTION_VM86_INTx:
136         len = snprintf( buffer, size, "Unhandled interrupt %02lx in vm86 mode",
137                  rec->ExceptionInformation[0]);
138         break;
139     case EXCEPTION_VM86_STI:
140         len = snprintf( buffer, size, "Unhandled sti in vm86 mode");
141         break;
142     case EXCEPTION_VM86_PICRETURN:
143         len = snprintf( buffer, size, "Unhandled PIC return in vm86 mode");
144         break;
145     default:
146         len = snprintf( buffer, size, "Unhandled exception 0x%08lx", rec->ExceptionCode);
147         break;
148     }
149     if ((len<0) || (len>=size))
150         return -1;
151 #ifdef __i386__
152     if (ptr->ContextRecord->SegCs != wine_get_cs())
153         len2 = snprintf(buffer+len, size-len,
154                         " at address 0x%04lx:0x%08lx.\nDo you wish to debug it ?",
155                         ptr->ContextRecord->SegCs,
156                         (DWORD)ptr->ExceptionRecord->ExceptionAddress);
157     else
158 #endif
159         len2 = snprintf(buffer+len, size-len,
160                         " at address 0x%08lx.\nDo you wish to debug it ?",
161                         (DWORD)ptr->ExceptionRecord->ExceptionAddress);
162     if ((len2<0) || (len>=size-len))
163         return -1;
164     return len+len2;
165 }
166
167
168 /**********************************************************************
169  *           send_debug_event
170  *
171  * Send an EXCEPTION_DEBUG_EVENT event to the debugger.
172  */
173 static int send_debug_event( EXCEPTION_RECORD *rec, int first_chance, CONTEXT *context )
174 {
175     int ret;
176     HANDLE handle = 0;
177
178     SERVER_START_REQ( queue_exception_event )
179     {
180         req->first   = first_chance;
181         wine_server_add_data( req, context, sizeof(*context) );
182         wine_server_add_data( req, rec, sizeof(*rec) );
183         if (!wine_server_call(req)) handle = reply->handle;
184     }
185     SERVER_END_REQ;
186     if (!handle) return 0;  /* no debugger present or other error */
187
188     /* No need to wait on the handle since the process gets suspended
189      * once the event is passed to the debugger, so when we get back
190      * here the event has been continued already.
191      */
192     SERVER_START_REQ( get_exception_status )
193     {
194         req->handle = handle;
195         wine_server_set_reply( req, context, sizeof(*context) );
196         wine_server_call( req );
197         ret = reply->status;
198     }
199     SERVER_END_REQ;
200     NtClose( handle );
201     return ret;
202 }
203
204 /******************************************************************
205  *              start_debugger
206  *
207  * Does the effective debugger startup according to 'format'
208  */
209 static BOOL     start_debugger(PEXCEPTION_POINTERS epointers, HANDLE hEvent)
210 {
211     HKEY                hDbgConf;
212     DWORD               bAuto = FALSE;
213     PROCESS_INFORMATION info;
214     STARTUPINFOA        startup;
215     char*               cmdline = NULL;
216     char*               format = NULL;
217     DWORD               format_size;
218     BOOL                ret = FALSE;
219
220     MESSAGE("wine: Unhandled exception, starting debugger...\n");
221
222     if (!RegOpenKeyA(HKEY_LOCAL_MACHINE,
223                      "Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug", &hDbgConf)) {
224        DWORD    type;
225        DWORD    count;
226
227        format_size = 0;
228        if (!RegQueryValueExA(hDbgConf, "Debugger", 0, &type, NULL, &format_size)) {
229            format = HeapAlloc(GetProcessHeap(), 0, format_size);
230            RegQueryValueExA(hDbgConf, "Debugger", 0, &type, format, &format_size);
231            if (type==REG_EXPAND_SZ) {
232                char* tmp;
233
234                /* Expand environment variable references */
235                format_size=ExpandEnvironmentStringsA(format,NULL,0);
236                tmp=HeapAlloc(GetProcessHeap(), 0, format_size);
237                ExpandEnvironmentStringsA(format,tmp,format_size);
238                HeapFree(GetProcessHeap(), 0, format);
239                format=tmp;
240            }
241        }
242
243        count = sizeof(bAuto);
244        if (RegQueryValueExA(hDbgConf, "Auto", 0, &type, (char*)&bAuto, &count))
245           bAuto = TRUE;
246        else if (type == REG_SZ)
247        {
248            char autostr[10];
249            count = sizeof(autostr);
250            if (!RegQueryValueExA(hDbgConf, "Auto", 0, &type, autostr, &count))
251                bAuto = atoi(autostr);
252        }
253        RegCloseKey(hDbgConf);
254     } else {
255         /* try a default setup... */
256         strcpy( format, "winedbg --debugmsg -all --auto %ld %ld" );
257     }
258
259     if (!bAuto)
260     {
261         HMODULE                 mod = GetModuleHandleA( "user32.dll" );
262         MessageBoxA_funcptr     pMessageBoxA = NULL;
263
264         if (mod) pMessageBoxA = (MessageBoxA_funcptr)GetProcAddress( mod, "MessageBoxA" );
265         if (pMessageBoxA)
266         {
267             char buffer[256];
268             format_exception_msg( epointers, buffer, sizeof(buffer) );
269             if (pMessageBoxA( 0, buffer, "Exception raised", MB_YESNO | MB_ICONHAND ) == IDNO)
270             {
271                 TRACE("Killing process\n");
272                 goto EXIT;
273             }
274         }
275     }
276
277     if (format) {
278         TRACE("Starting debugger (fmt=%s)\n", format);
279         cmdline=HeapAlloc(GetProcessHeap(), 0, format_size+2*20);
280         sprintf(cmdline, format, GetCurrentProcessId(), hEvent);
281         memset(&startup, 0, sizeof(startup));
282         startup.cb = sizeof(startup);
283         startup.dwFlags = STARTF_USESHOWWINDOW;
284         startup.wShowWindow = SW_SHOWNORMAL;
285         if (CreateProcessA(NULL, cmdline, NULL, NULL, TRUE, 0, NULL, NULL, &startup, &info)) {
286             /* wait for debugger to come up... */
287             WaitForSingleObject(hEvent, INFINITE);
288             ret = TRUE;
289             goto EXIT;
290         }
291     } else {
292         cmdline = NULL;
293     }
294     ERR("Couldn't start debugger (%s) (%ld)\n"
295         "Read the Wine Developers Guide on how to set up winedbg or another debugger\n",
296         debugstr_a(cmdline), GetLastError());
297
298 EXIT:
299     if (cmdline)
300         HeapFree(GetProcessHeap(), 0, cmdline);
301     if (format)
302         HeapFree(GetProcessHeap(), 0, format);
303     return ret;
304 }
305
306 /******************************************************************
307  *              start_debugger_atomic
308  *
309  * starts the debugger in an atomic way:
310  *      - either the debugger is not started and it is started
311  *      - or the debugger has already been started by another thread
312  *      - or the debugger couldn't be started
313  *
314  * returns TRUE for the two first conditions, FALSE for the last
315  */
316 static  int     start_debugger_atomic(PEXCEPTION_POINTERS epointers)
317 {
318     static HANDLE       hRunOnce /* = 0 */;
319
320     if (hRunOnce == 0)
321     {
322         OBJECT_ATTRIBUTES       attr;
323         HANDLE                  hEvent;
324
325         attr.Length                   = sizeof(attr);
326         attr.RootDirectory            = 0;
327         attr.Attributes               = OBJ_INHERIT;
328         attr.ObjectName               = NULL;
329         attr.SecurityDescriptor       = NULL;
330         attr.SecurityQualityOfService = NULL;
331
332         /* ask for manual reset, so that once the debugger is started,
333          * every thread will know it */
334         NtCreateEvent( &hEvent, EVENT_ALL_ACCESS, &attr, TRUE, FALSE );
335         if (InterlockedCompareExchange( (LPLONG)&hRunOnce, hEvent, 0 ) == 0)
336         {
337             /* ok, our event has been set... we're the winning thread */
338             BOOL        ret = start_debugger( epointers, hRunOnce );
339             DWORD       tmp;
340
341             if (!ret)
342             {
343                 /* so that the other threads won't be stuck */
344                 NtSetEvent( hRunOnce, &tmp );
345             }
346             return ret;
347         }
348
349         /* someone beat us here... */
350         CloseHandle( hEvent );
351     }
352
353     /* and wait for the winner to have actually created the debugger */
354     WaitForSingleObject( hRunOnce, INFINITE );
355     /* in fact, here, we only know that someone has tried to start the debugger,
356      * we'll know by reposting the exception if it has actually attached
357      * to the current process */
358     return TRUE;
359 }
360
361
362 /*******************************************************************
363  *         UnhandledExceptionFilter   (KERNEL32.@)
364  */
365 DWORD WINAPI UnhandledExceptionFilter(PEXCEPTION_POINTERS epointers)
366 {
367     int                 status;
368     int                 loop = 0;
369
370     for (loop = 0; loop <= 1; loop++)
371     {
372         /* send a last chance event to the debugger */
373         status = send_debug_event( epointers->ExceptionRecord, FALSE, epointers->ContextRecord );
374         switch (status)
375         {
376         case DBG_CONTINUE:
377             return EXCEPTION_CONTINUE_EXECUTION;
378         case DBG_EXCEPTION_NOT_HANDLED:
379             TerminateProcess( GetCurrentProcess(), epointers->ExceptionRecord->ExceptionCode );
380             break; /* not reached */
381         case 0: /* no debugger is present */
382             if (epointers->ExceptionRecord->ExceptionCode == CONTROL_C_EXIT)
383             {
384                 /* do not launch the debugger on ^C, simply terminate the process */
385                 TerminateProcess( GetCurrentProcess(), 1 );
386             }
387             /* second try, the debugger isn't present... */
388             if (loop == 1) return EXCEPTION_EXECUTE_HANDLER;
389             break;
390         default:
391             FIXME("Unsupported yet debug continue value %d (please report)\n", status);
392             return EXCEPTION_EXECUTE_HANDLER;
393         }
394
395         /* should only be there when loop == 0 */
396
397         if (top_filter)
398         {
399             DWORD ret = top_filter( epointers );
400             if (ret != EXCEPTION_CONTINUE_SEARCH) return ret;
401         }
402
403         /* FIXME: Should check the current error mode */
404
405         if (!start_debugger_atomic( epointers ))
406             return EXCEPTION_EXECUTE_HANDLER;
407         /* now that we should have a debugger attached, try to resend event */
408     }
409
410     return EXCEPTION_EXECUTE_HANDLER;
411 }
412
413
414 /***********************************************************************
415  *            SetUnhandledExceptionFilter   (KERNEL32.@)
416  */
417 LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter(
418                                           LPTOP_LEVEL_EXCEPTION_FILTER filter )
419 {
420     LPTOP_LEVEL_EXCEPTION_FILTER old = top_filter;
421     top_filter = filter;
422     return old;
423 }
424
425
426 /**************************************************************************
427  *           FatalAppExitA   (KERNEL32.@)
428  */
429 void WINAPI FatalAppExitA( UINT action, LPCSTR str )
430 {
431     HMODULE mod = GetModuleHandleA( "user32.dll" );
432     MessageBoxA_funcptr pMessageBoxA = NULL;
433
434     WARN("AppExit\n");
435
436     if (mod) pMessageBoxA = (MessageBoxA_funcptr)GetProcAddress( mod, "MessageBoxA" );
437     if (pMessageBoxA) pMessageBoxA( 0, str, NULL, MB_SYSTEMMODAL | MB_OK );
438     else ERR( "%s\n", debugstr_a(str) );
439     ExitProcess(0);
440 }
441
442
443 /**************************************************************************
444  *           FatalAppExitW   (KERNEL32.@)
445  */
446 void WINAPI FatalAppExitW( UINT action, LPCWSTR str )
447 {
448     HMODULE mod = GetModuleHandleA( "user32.dll" );
449     MessageBoxW_funcptr pMessageBoxW = NULL;
450
451     WARN("AppExit\n");
452
453     if (mod) pMessageBoxW = (MessageBoxW_funcptr)GetProcAddress( mod, "MessageBoxW" );
454     if (pMessageBoxW) pMessageBoxW( 0, str, NULL, MB_SYSTEMMODAL | MB_OK );
455     else ERR( "%s\n", debugstr_w(str) );
456     ExitProcess(0);
457 }