- Reimplementation of the CommDlg ExtendedError mechanism using TLS.
[wine] / win32 / except.c
1 /*
2  * Win32 exception functions
3  *
4  * Copyright (c) 1996 Onno Hovers, (onno@stack.urc.tue.nl)
5  *
6  * Notes:
7  *  What really happens behind the scenes of those new
8  *  __try{...}__except(..){....}  and
9  *  __try{...}__finally{...}
10  *  statements is simply not documented by Microsoft. There could be different
11  *  reasons for this: 
12  *  One reason could be that they try to hide the fact that exception 
13  *  handling in Win32 looks almost the same as in OS/2 2.x.  
14  *  Another reason could be that Microsoft does not want others to write
15  *  binary compatible implementations of the Win32 API (like us).  
16  *
17  *  Whatever the reason, THIS SUCKS!! Ensuring portabilty or future 
18  *  compatability may be valid reasons to keep some things undocumented. 
19  *  But exception handling is so basic to Win32 that it should be 
20  *  documented!
21  *
22  * Fixmes:
23  *  -Most functions need better parameter checking.
24  *  -I do not know how to handle exceptions within an exception handler.
25  *   or what is done when ExceptionNestedException is returned from an 
26  *   exception handler
27  *  -Real exceptions are not yet implemented. only the exception functions
28  *   are implemented. A real implementation needs some new code in
29  *   loader/signal.c. There would also be a need for showing debugging
30  *   information in UnhandledExceptionFilter.
31  *
32  */
33
34 #include <assert.h>
35 #include "winuser.h"
36 #include "winerror.h"
37 #include "ldt.h"
38 #include "process.h"
39 #include "thread.h"
40 #include "debug.h"
41 #include "except.h"
42 #include "stackframe.h"
43
44 #define TEB_EXCEPTION_FRAME(pcontext) \
45     ((PEXCEPTION_FRAME)((TEB *)GET_SEL_BASE((pcontext)->SegFs))->except)
46
47 /*******************************************************************
48  *         RtlUnwind  (KERNEL32.443)
49  *
50  *  This function is undocumented. This is the general idea of 
51  *  RtlUnwind, though. Note that error handling is not yet implemented.
52  *
53  * The real prototype is:
54  * void WINAPI EXC_RtlUnwind( PEXCEPTION_FRAME pEndFrame, LPVOID unusedEip, 
55  *                            PEXCEPTION_RECORD pRecord, DWORD returnEax );
56  */
57 REGS_ENTRYPOINT(RtlUnwind)
58 {
59     EXCEPTION_RECORD record;
60     DWORD            dispatch;
61     int              retval;
62     PEXCEPTION_FRAME pEndFrame;
63     PEXCEPTION_RECORD pRecord;
64
65     /* get the arguments from the stack */
66
67     DWORD ret        = STACK32_POP(context);  /* return addr */
68     pEndFrame        = (PEXCEPTION_FRAME)STACK32_POP(context);
69     (void)STACK32_POP(context);  /* unused arg */
70     pRecord          = (PEXCEPTION_RECORD)STACK32_POP(context);
71     EAX_reg(context) = STACK32_POP(context);
72     STACK32_PUSH(context,ret);  /* restore return addr */
73    
74    /* build an exception record, if we do not have one */
75    if(!pRecord)
76    {
77      record.ExceptionCode    = STATUS_INVALID_DISPOSITION;
78      record.ExceptionFlags   = 0;
79      record.ExceptionRecord  = NULL;
80      record.ExceptionAddress = (LPVOID)EIP_reg(context); 
81      record.NumberParameters = 0;
82      pRecord = &record;
83    }
84
85    if(pEndFrame)
86      pRecord->ExceptionFlags|=EH_UNWINDING;
87    else
88      pRecord->ExceptionFlags|=EH_UNWINDING | EH_EXIT_UNWIND;
89   
90    /* get chain of exception frames */      
91    while ((TEB_EXCEPTION_FRAME(context) != NULL) &&
92           (TEB_EXCEPTION_FRAME(context) != ((void *)0xffffffff)) &&
93           (TEB_EXCEPTION_FRAME(context) != pEndFrame))
94    {
95        TRACE(win32, "calling exception handler at 0x%x\n",
96                       (int)TEB_EXCEPTION_FRAME(context)->Handler );
97
98        dispatch=0;       
99        retval = TEB_EXCEPTION_FRAME(context)->Handler( pRecord,
100                                                 TEB_EXCEPTION_FRAME(context),
101                                                 context, &dispatch);
102                                          
103        TRACE(win32,"exception handler returns 0x%x, dispatch=0x%x\n",
104                               retval, (int) dispatch);
105   
106        if (     (retval == ExceptionCollidedUnwind) &&
107                 (TEB_EXCEPTION_FRAME(context) != (LPVOID)dispatch)
108        )
109            TEB_EXCEPTION_FRAME(context) = (LPVOID)dispatch;
110        else if (        (TEB_EXCEPTION_FRAME(context) != pEndFrame) &&
111                         (TEB_EXCEPTION_FRAME(context) != TEB_EXCEPTION_FRAME(context)->Prev)
112        )
113            TEB_EXCEPTION_FRAME(context) = TEB_EXCEPTION_FRAME(context)->Prev;
114        else
115           break;  
116    }
117 }
118
119
120 /*******************************************************************
121  *         RaiseException  (KERNEL32.418)
122  *
123  * The real prototype is:
124  * void WINAPI EXC_RaiseException(DWORD dwExceptionCode,
125  *                                DWORD dwExceptionFlags,
126  *                                DWORD cArguments,
127  *                                const LPDWORD lpArguments );
128  */
129 REGS_ENTRYPOINT(RaiseException)
130 {
131     PEXCEPTION_FRAME    pframe; 
132     EXCEPTION_RECORD    record;
133     DWORD               dispatch; /* is this used in raising exceptions ?? */
134     int                 retval;
135     int                 i;
136
137     /* Get the arguments from the stack */
138
139     DWORD ret                 = STACK32_POP(context);  /* return addr */
140     DWORD dwExceptionCode     = STACK32_POP(context);
141     DWORD dwExceptionFlags    = STACK32_POP(context);
142     DWORD cArguments          = STACK32_POP(context);
143     const LPDWORD lpArguments = (LPDWORD)STACK32_POP(context);
144     STACK32_PUSH(context,ret);  /* Restore the return address */
145
146     /* compose an exception record */ 
147     
148     record.ExceptionCode       = dwExceptionCode;   
149     record.ExceptionFlags      = dwExceptionFlags;
150     record.ExceptionRecord     = NULL;
151     record.NumberParameters    = cArguments;
152     record.ExceptionAddress    = (LPVOID)EIP_reg(context);
153     
154     if (lpArguments) for( i = 0; i < cArguments; i++)
155         record.ExceptionInformation[i] = lpArguments[i];
156     
157     /* get chain of exception frames */    
158     
159     retval = ExceptionContinueSearch;    
160     pframe = TEB_EXCEPTION_FRAME( context );
161     
162     while((pframe!=NULL)&&(pframe!=((void *)0xFFFFFFFF)))
163     {
164        PEXCEPTION_FRAME    prevframe; 
165        TRACE(win32,"calling exception handler at 0x%x\n",
166                                                 (int) pframe->Handler);
167        dispatch=0;  
168        TRACE(relay,"(except=%p,record=%p,frame=%p,context=%p,dispatch=%p)\n",
169                      pframe->Handler, &record, pframe, context, &dispatch );
170        prevframe = pframe->Prev;
171        retval=pframe->Handler(&record,pframe,context,&dispatch);
172  
173        TRACE(win32,"exception handler returns 0x%x, dispatch=0x%x\n",
174                               retval, (int) dispatch);
175                               
176        if(retval==ExceptionContinueExecution)
177           break;
178        pframe=prevframe;
179    }
180
181    if (retval!=ExceptionContinueExecution)
182    {    
183        /* FIXME: what should we do here? */
184        TRACE(win32,"no handler wanted to handle the exception, exiting\n");
185        ExitProcess(dwExceptionCode); /* what status should be used here ? */
186    }
187 }
188
189
190 /*******************************************************************
191  *         UnhandledExceptionFilter   (KERNEL32.537)
192  */
193 DWORD WINAPI UnhandledExceptionFilter(PEXCEPTION_POINTERS epointers)
194 {
195     char message[80];
196     PDB *pdb = PROCESS_Current();
197
198     /* FIXME: Should check if the process is being debugged */
199
200     if (pdb->top_filter)
201     {
202         DWORD ret = pdb->top_filter( epointers );
203         if (ret != EXCEPTION_CONTINUE_SEARCH) return ret;
204     }
205
206     /* FIXME: Should check the current error mode */
207
208     sprintf( message, "Unhandled exception 0x%08lx at address 0x%08lx.",
209              epointers->ExceptionRecord->ExceptionCode,
210              (DWORD)epointers->ExceptionRecord->ExceptionAddress );
211     MessageBoxA( 0, message, "Error", MB_OK | MB_ICONHAND );
212     return EXCEPTION_EXECUTE_HANDLER;
213 }
214
215
216 /*************************************************************
217  *            SetUnhandledExceptionFilter   (KERNEL32.516)
218  */
219 LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter(
220                                           LPTOP_LEVEL_EXCEPTION_FILTER filter )
221 {
222     PDB *pdb = PROCESS_Current();
223     LPTOP_LEVEL_EXCEPTION_FILTER old = pdb->top_filter;
224     pdb->top_filter = filter;
225     return old;
226 }