Authors: Gavriel State <gavriels@corel.com>, Ulrich Czekalla <ulrichc@corel.com>
[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 portabilty or future 
19  *  compatability 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 <assert.h>
26 #include <stdio.h>
27 #include "windef.h"
28 #include "wingdi.h"
29 #include "winuser.h"
30 #include "winerror.h"
31 #include "ntddk.h"
32 #include "wine/exception.h"
33 #include "ldt.h"
34 #include "callback.h"
35 #include "process.h"
36 #include "thread.h"
37 #include "stackframe.h"
38 #include "server.h"
39 #include "debugtools.h"
40
41 DEFAULT_DEBUG_CHANNEL(seh);
42
43
44 /*******************************************************************
45  *         RaiseException  (KERNEL32.418)
46  */
47 void WINAPI RaiseException( DWORD code, DWORD flags, DWORD nbargs, const LPDWORD args )
48 {
49     EXCEPTION_RECORD record;
50
51     /* Compose an exception record */ 
52     
53     record.ExceptionCode    = code;
54     record.ExceptionFlags   = flags & EH_NONCONTINUABLE;
55     record.ExceptionRecord  = NULL;
56     record.ExceptionAddress = RaiseException;
57     if (nbargs && args)
58     {
59         if (nbargs > EXCEPTION_MAXIMUM_PARAMETERS) nbargs = EXCEPTION_MAXIMUM_PARAMETERS;
60         record.NumberParameters = nbargs;
61         memcpy( record.ExceptionInformation, args, nbargs * sizeof(*args) );
62     }
63     else record.NumberParameters = 0;
64
65     RtlRaiseException( &record );
66 }
67
68
69 /*******************************************************************
70  *         UnhandledExceptionFilter   (KERNEL32.537)
71  */
72 DWORD WINAPI UnhandledExceptionFilter(PEXCEPTION_POINTERS epointers)
73 {
74     struct exception_event_request *req = get_req_buffer();
75     PDB*                pdb = PROCESS_Current();
76     char                format[256];
77     char                buffer[256];
78     HKEY                hDbgConf;
79     DWORD               bAuto;
80     DWORD               ret = EXCEPTION_EXECUTE_HANDLER;
81
82     /* send a last chance event to the debugger */
83     req->record  = *epointers->ExceptionRecord;
84     req->first   = 0;
85     req->context = *epointers->ContextRecord;
86     if (!server_call_noerr( REQ_EXCEPTION_EVENT )) *epointers->ContextRecord = req->context;
87     if (req->status == DBG_CONTINUE) return EXCEPTION_CONTINUE_EXECUTION;
88
89     if (pdb->top_filter)
90     {
91         DWORD ret = pdb->top_filter( epointers );
92         if (ret != EXCEPTION_CONTINUE_SEARCH) return ret;
93     }
94
95     /* FIXME: Should check the current error mode */
96
97     if (!RegOpenKeyA(HKEY_LOCAL_MACHINE, 
98                      "Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug", 
99                      &hDbgConf)) {
100        DWORD    type;
101        DWORD    count;
102        
103        count = sizeof(format);
104        if (RegQueryValueExA(hDbgConf, "Debugger", 0, &type, format, &count))
105           format[0] = 0;
106
107        count = sizeof(bAuto);
108        if (RegQueryValueExA(hDbgConf, "Auto", 0, &type, (char*)&bAuto, &count))
109           bAuto = FALSE;
110        
111        RegCloseKey(hDbgConf);
112     } else {
113        format[0] = 0;
114     }
115
116     if (!bAuto) {
117        sprintf( buffer, "Unhandled exception 0x%08lx at address 0x%08lx.\n"
118                         "Do you wish to debug it ?",
119                 epointers->ExceptionRecord->ExceptionCode,
120                 (DWORD)epointers->ExceptionRecord->ExceptionAddress );
121        if (Callout.MessageBoxA( 0, buffer, "Error", MB_YESNO | MB_ICONHAND ) == IDNO) {
122           TRACE("Killing process\n");
123           return EXCEPTION_EXECUTE_HANDLER;
124        }
125     }
126     
127     if (format[0]) {
128        HANDLE                   hEvent;
129        PROCESS_INFORMATION      info;
130        STARTUPINFOA             startup;
131
132        TRACE("Starting debugger (fmt=%s)\n", format);
133        hEvent = ConvertToGlobalHandle(CreateEventA(NULL, FALSE, FALSE, NULL));
134        sprintf(buffer, format, (unsigned long)pdb->server_pid, hEvent);
135        memset(&startup, 0, sizeof(startup));
136        startup.cb = sizeof(startup);
137        startup.dwFlags = STARTF_USESHOWWINDOW;
138        startup.wShowWindow = SW_SHOWNORMAL;
139        if (CreateProcessA(NULL, buffer, NULL, NULL, 
140                           TRUE, 0, NULL, NULL, &startup, &info)) {
141           WaitForSingleObject(hEvent, INFINITE);
142           ret = EXCEPTION_CONTINUE_SEARCH;
143        } else {
144           ERR("Couldn't start debugger (%s)\n", buffer);
145        }
146        CloseHandle(hEvent);
147     } else {
148        ERR("No standard debugger defined in the registry => no debugging session\n");
149     }
150     
151     return ret;
152 }
153
154
155 /***********************************************************************
156  *            SetUnhandledExceptionFilter   (KERNEL32.516)
157  */
158 LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter(
159                                           LPTOP_LEVEL_EXCEPTION_FILTER filter )
160 {
161     PDB *pdb = PROCESS_Current();
162     LPTOP_LEVEL_EXCEPTION_FILTER old = pdb->top_filter;
163     pdb->top_filter = filter;
164     return old;
165 }
166
167
168 /**************************************************************************
169  *           FatalAppExit16   (KERNEL.137)
170  */
171 void WINAPI FatalAppExit16( UINT16 action, LPCSTR str )
172 {
173     WARN("AppExit\n");
174     FatalAppExitA( action, str );
175 }
176
177
178 /**************************************************************************
179  *           FatalAppExitA   (KERNEL32.108)
180  */
181 void WINAPI FatalAppExitA( UINT action, LPCSTR str )
182 {
183     WARN("AppExit\n");
184     Callout.MessageBoxA( 0, str, NULL, MB_SYSTEMMODAL | MB_OK );
185     ExitProcess(0);
186 }
187
188
189 /**************************************************************************
190  *           FatalAppExitW   (KERNEL32.109)
191  */
192 void WINAPI FatalAppExitW( UINT action, LPCWSTR str )
193 {
194     WARN("AppExit\n");
195     Callout.MessageBoxW( 0, str, NULL, MB_SYSTEMMODAL | MB_OK );
196     ExitProcess(0);
197 }
198
199
200 /*************************************************************
201  *            WINE_exception_handler
202  *
203  * Exception handler for exception blocks declared in Wine code.
204  */
205 DWORD WINE_exception_handler( EXCEPTION_RECORD *record, EXCEPTION_FRAME *frame,
206                               CONTEXT *context, LPVOID pdispatcher )
207 {
208     __WINE_FRAME *wine_frame = (__WINE_FRAME *)frame;
209
210     if (record->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND | EH_NESTED_CALL))
211         return ExceptionContinueSearch;
212     if (wine_frame->u.filter)
213     {
214         EXCEPTION_POINTERS ptrs;
215         ptrs.ExceptionRecord = record;
216         ptrs.ContextRecord = context;
217         switch(wine_frame->u.filter( &ptrs ))
218         {
219         case EXCEPTION_CONTINUE_SEARCH:
220             return ExceptionContinueSearch;
221         case EXCEPTION_CONTINUE_EXECUTION:
222             return ExceptionContinueExecution;
223         case EXCEPTION_EXECUTE_HANDLER:
224             break;
225         default:
226             MESSAGE( "Invalid return value from exception filter\n" );
227             assert( FALSE );
228         }
229     }
230     /* hack to make GetExceptionCode() work in handler */
231     wine_frame->ExceptionCode   = record->ExceptionCode;
232     wine_frame->ExceptionRecord = wine_frame;
233
234     RtlUnwind( frame, 0, record, 0 );
235     EXC_pop_frame( frame );
236     longjmp( wine_frame->jmp, 1 );
237 }
238
239
240 /*************************************************************
241  *            WINE_finally_handler
242  *
243  * Exception handler for try/finally blocks declared in Wine code.
244  */
245 DWORD WINE_finally_handler( EXCEPTION_RECORD *record, EXCEPTION_FRAME *frame,
246                             CONTEXT *context, LPVOID pdispatcher )
247 {
248     __WINE_FRAME *wine_frame = (__WINE_FRAME *)frame;
249
250     if (!(record->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND)))
251         return ExceptionContinueSearch;
252     wine_frame->u.finally_func( FALSE );
253     return ExceptionContinueSearch;
254 }