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