Convert MsiEnumComponentQualifiers to use msi_strcpy_to_awstring.
[wine] / dlls / ntdll / exception.c
1 /*
2  * NT exception handling routines
3  *
4  * Copyright 1999 Turchanov Sergey
5  * Copyright 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
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include <assert.h>
26 #include <signal.h>
27 #include <stdarg.h>
28
29 #include "ntstatus.h"
30 #include "windef.h"
31 #include "winternl.h"
32 #include "wine/exception.h"
33 #include "wine/server.h"
34 #include "wine/list.h"
35 #include "wine/debug.h"
36 #include "excpt.h"
37 #include "ntdll_misc.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(seh);
40
41 /* Exception record for handling exceptions happening inside exception handlers */
42 typedef struct
43 {
44     EXCEPTION_REGISTRATION_RECORD frame;
45     EXCEPTION_REGISTRATION_RECORD *prevFrame;
46 } EXC_NESTED_FRAME;
47
48 typedef struct
49 {
50     struct list                 entry;
51     PVECTORED_EXCEPTION_HANDLER func;
52 } VECTORED_HANDLER;
53
54 static struct list vectored_handlers = LIST_INIT(vectored_handlers);
55
56 static RTL_CRITICAL_SECTION vectored_handlers_section;
57 static RTL_CRITICAL_SECTION_DEBUG critsect_debug =
58 {
59     0, 0, &vectored_handlers_section,
60     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
61       0, 0, { (DWORD_PTR)(__FILE__ ": vectored_handlers_section") }
62 };
63 static RTL_CRITICAL_SECTION vectored_handlers_section = { &critsect_debug, -1, 0, 0, 0, 0 };
64
65 #ifdef __i386__
66 # define GET_IP(context) ((LPVOID)(context)->Eip)
67 #elif defined(__sparc__)
68 # define GET_IP(context) ((LPVOID)(context)->pc)
69 #elif defined(__powerpc__)
70 # define GET_IP(context) ((LPVOID)(context)->Iar)
71 #elif defined(__ALPHA__)
72 # define GET_IP(context) ((LPVOID)(context)->Fir)
73 #elif defined(__x86_64__)
74 # define GET_IP(context) ((LPVOID)(context)->Rip)
75 #else
76 # error You must define GET_IP for this CPU
77 #endif
78
79
80 /*******************************************************************
81  *         EXC_RaiseHandler
82  *
83  * Handler for exceptions happening inside a handler.
84  */
85 static DWORD EXC_RaiseHandler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
86                                CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
87 {
88     if (rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))
89         return ExceptionContinueSearch;
90     /* We shouldn't get here so we store faulty frame in dispatcher */
91     *dispatcher = ((EXC_NESTED_FRAME*)frame)->prevFrame;
92     return ExceptionNestedException;
93 }
94
95
96 /*******************************************************************
97  *         EXC_UnwindHandler
98  *
99  * Handler for exceptions happening inside an unwind handler.
100  */
101 static DWORD EXC_UnwindHandler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
102                                 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
103 {
104     if (!(rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND)))
105         return ExceptionContinueSearch;
106     /* We shouldn't get here so we store faulty frame in dispatcher */
107     *dispatcher = ((EXC_NESTED_FRAME*)frame)->prevFrame;
108     return ExceptionCollidedUnwind;
109 }
110
111
112 /*******************************************************************
113  *         EXC_CallHandler
114  *
115  * Call an exception handler, setting up an exception frame to catch exceptions
116  * happening during the handler execution.
117  *
118  * For i386 this function is implemented in assembler in signal_i386.c.
119  */
120 #ifndef __i386__
121 static DWORD EXC_CallHandler( EXCEPTION_RECORD *record, EXCEPTION_REGISTRATION_RECORD *frame,
122                               CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher,
123                               PEXCEPTION_HANDLER handler, PEXCEPTION_HANDLER nested_handler)
124 {
125     EXC_NESTED_FRAME newframe;
126     DWORD ret;
127
128     newframe.frame.Handler = nested_handler;
129     newframe.prevFrame     = frame;
130     __wine_push_frame( &newframe.frame );
131     TRACE( "calling handler at %p code=%lx flags=%lx\n",
132            handler, record->ExceptionCode, record->ExceptionFlags );
133     ret = handler( record, frame, context, dispatcher );
134     TRACE( "handler returned %lx\n", ret );
135     __wine_pop_frame( &newframe.frame );
136     return ret;
137 }
138 #else
139 /* in signal_i386.c */
140 extern DWORD EXC_CallHandler( EXCEPTION_RECORD *record, EXCEPTION_REGISTRATION_RECORD *frame,
141                               CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher,
142                               PEXCEPTION_HANDLER handler, PEXCEPTION_HANDLER nested_handler);
143 #endif
144
145 /**********************************************************************
146  *           wait_suspend
147  *
148  * Wait until the thread is no longer suspended.
149  */
150 void wait_suspend( CONTEXT *context )
151 {
152     LARGE_INTEGER timeout;
153
154     /* store the context we got at suspend time */
155     SERVER_START_REQ( set_thread_context )
156     {
157         req->handle  = GetCurrentThread();
158         req->flags   = CONTEXT_FULL;
159         req->suspend = 1;
160         wine_server_add_data( req, context, sizeof(*context) );
161         wine_server_call( req );
162     }
163     SERVER_END_REQ;
164
165     /* wait with 0 timeout, will only return once the thread is no longer suspended */
166     timeout.QuadPart = 0;
167     NTDLL_wait_for_multiple_objects( 0, NULL, 0, &timeout, 0 );
168
169     /* retrieve the new context */
170     SERVER_START_REQ( get_thread_context )
171     {
172         req->handle  = GetCurrentThread();
173         req->flags   = CONTEXT_FULL;
174         req->suspend = 1;
175         wine_server_set_reply( req, context, sizeof(*context) );
176         wine_server_call( req );
177     }
178     SERVER_END_REQ;
179 }
180
181
182 /**********************************************************************
183  *           send_debug_event
184  *
185  * Send an EXCEPTION_DEBUG_EVENT event to the debugger.
186  */
187 static int send_debug_event( EXCEPTION_RECORD *rec, int first_chance, CONTEXT *context )
188 {
189     int ret;
190     HANDLE handle = 0;
191
192     if (!NtCurrentTeb()->Peb->BeingDebugged) return 0;  /* no debugger present */
193
194     SERVER_START_REQ( queue_exception_event )
195     {
196         req->first   = first_chance;
197         wine_server_add_data( req, context, sizeof(*context) );
198         wine_server_add_data( req, rec, sizeof(*rec) );
199         if (!wine_server_call( req )) handle = reply->handle;
200     }
201     SERVER_END_REQ;
202     if (!handle) return 0;
203
204     NTDLL_wait_for_multiple_objects( 1, &handle, 0, NULL, 0 );
205
206     SERVER_START_REQ( get_exception_status )
207     {
208         req->handle = handle;
209         wine_server_set_reply( req, context, sizeof(*context) );
210         ret = wine_server_call( req );
211     }
212     SERVER_END_REQ;
213     return ret;
214 }
215
216
217 /**********************************************************************
218  *           call_vectored_handlers
219  *
220  * Call the vectored handlers chain.
221  */
222 static LONG call_vectored_handlers( EXCEPTION_RECORD *rec, CONTEXT *context )
223 {
224     struct list *ptr;
225     LONG ret = EXCEPTION_CONTINUE_SEARCH;
226     EXCEPTION_POINTERS except_ptrs;
227
228     except_ptrs.ExceptionRecord = rec;
229     except_ptrs.ContextRecord = context;
230
231     RtlEnterCriticalSection( &vectored_handlers_section );
232     LIST_FOR_EACH( ptr, &vectored_handlers )
233     {
234         VECTORED_HANDLER *handler = LIST_ENTRY( ptr, VECTORED_HANDLER, entry );
235         ret = handler->func( &except_ptrs );
236         if (ret == EXCEPTION_CONTINUE_EXECUTION) break;
237     }
238     RtlLeaveCriticalSection( &vectored_handlers_section );
239     return ret;
240 }
241
242
243 /*******************************************************************
244  *         EXC_DefaultHandling
245  *
246  * Default handling for exceptions. Called when we didn't find a suitable handler.
247  */
248 static void EXC_DefaultHandling( EXCEPTION_RECORD *rec, CONTEXT *context )
249 {
250     if (send_debug_event( rec, FALSE, context ) == DBG_CONTINUE) return;  /* continue execution */
251
252     if (rec->ExceptionFlags & EH_STACK_INVALID)
253         ERR("Exception frame is not in stack limits => unable to dispatch exception.\n");
254     else if (rec->ExceptionCode == STATUS_NONCONTINUABLE_EXCEPTION)
255         ERR("Process attempted to continue execution after noncontinuable exception.\n");
256     else
257         ERR("Unhandled exception code %lx flags %lx addr %p\n",
258             rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress );
259     NtTerminateProcess( NtCurrentProcess(), 1 );
260 }
261
262
263 /***********************************************************************
264  *              RtlRaiseException (NTDLL.@)
265  */
266 void WINAPI __regs_RtlRaiseException( EXCEPTION_RECORD *rec, CONTEXT *context )
267 {
268     EXCEPTION_REGISTRATION_RECORD *frame, *dispatch, *nested_frame;
269     EXCEPTION_RECORD newrec;
270     DWORD res, c;
271
272     TRACE( "code=%lx flags=%lx addr=%p\n", rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress );
273     for (c=0; c<rec->NumberParameters; c++) TRACE(" info[%ld]=%08lx\n", c, rec->ExceptionInformation[c]);
274     if (rec->ExceptionCode == EXCEPTION_WINE_STUB)
275     {
276         if (HIWORD(rec->ExceptionInformation[1]))
277             MESSAGE( "wine: Call from %p to unimplemented function %s.%s, aborting\n",
278                    rec->ExceptionAddress,
279                    (char*)rec->ExceptionInformation[0], (char*)rec->ExceptionInformation[1] );
280         else
281             MESSAGE( "wine: Call from %p to unimplemented function %s.%ld, aborting\n",
282                    rec->ExceptionAddress,
283                    (char*)rec->ExceptionInformation[0], rec->ExceptionInformation[1] );
284     }
285 #ifdef __i386__
286     else
287     {
288         TRACE(" eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx esi=%08lx edi=%08lx\n",
289               context->Eax, context->Ebx, context->Ecx,
290               context->Edx, context->Esi, context->Edi );
291         TRACE(" ebp=%08lx esp=%08lx cs=%04lx ds=%04lx es=%04lx fs=%04lx gs=%04lx flags=%08lx\n",
292               context->Ebp, context->Esp, context->SegCs, context->SegDs,
293               context->SegEs, context->SegFs, context->SegGs, context->EFlags );
294     }
295 #endif
296
297     if (send_debug_event( rec, TRUE, context ) == DBG_CONTINUE) return;  /* continue execution */
298
299     if (call_vectored_handlers( rec, context ) == EXCEPTION_CONTINUE_EXECUTION) return;
300
301     frame = NtCurrentTeb()->Tib.ExceptionList;
302     nested_frame = NULL;
303     while (frame != (EXCEPTION_REGISTRATION_RECORD*)~0UL)
304     {
305         /* Check frame address */
306         if (((void*)frame < NtCurrentTeb()->Tib.StackLimit) ||
307             ((void*)(frame+1) > NtCurrentTeb()->Tib.StackBase) ||
308             (ULONG_PTR)frame & 3)
309         {
310             rec->ExceptionFlags |= EH_STACK_INVALID;
311             break;
312         }
313
314         /* Call handler */
315         res = EXC_CallHandler( rec, frame, context, &dispatch, frame->Handler, EXC_RaiseHandler );
316         if (frame == nested_frame)
317         {
318             /* no longer nested */
319             nested_frame = NULL;
320             rec->ExceptionFlags &= ~EH_NESTED_CALL;
321         }
322
323         switch(res)
324         {
325         case ExceptionContinueExecution:
326             if (!(rec->ExceptionFlags & EH_NONCONTINUABLE)) return;
327             newrec.ExceptionCode    = STATUS_NONCONTINUABLE_EXCEPTION;
328             newrec.ExceptionFlags   = EH_NONCONTINUABLE;
329             newrec.ExceptionRecord  = rec;
330             newrec.NumberParameters = 0;
331             RtlRaiseException( &newrec );  /* never returns */
332             break;
333         case ExceptionContinueSearch:
334             break;
335         case ExceptionNestedException:
336             if (nested_frame < dispatch) nested_frame = dispatch;
337             rec->ExceptionFlags |= EH_NESTED_CALL;
338             break;
339         default:
340             newrec.ExceptionCode    = STATUS_INVALID_DISPOSITION;
341             newrec.ExceptionFlags   = EH_NONCONTINUABLE;
342             newrec.ExceptionRecord  = rec;
343             newrec.NumberParameters = 0;
344             RtlRaiseException( &newrec );  /* never returns */
345             break;
346         }
347         frame = frame->Prev;
348     }
349     EXC_DefaultHandling( rec, context );
350 }
351
352 /**********************************************************************/
353
354 #ifdef DEFINE_REGS_ENTRYPOINT
355 DEFINE_REGS_ENTRYPOINT( RtlRaiseException, 4, 4 );
356 #else
357 void WINAPI RtlRaiseException( EXCEPTION_RECORD *rec )
358 {
359     CONTEXT context;
360     memset( &context, 0, sizeof(context) );
361     __regs_RtlRaiseException( rec, &context );
362 }
363 #endif
364
365
366 /*******************************************************************
367  *              RtlUnwind (NTDLL.@)
368  */
369 void WINAPI __regs_RtlUnwind( EXCEPTION_REGISTRATION_RECORD* pEndFrame, PVOID unusedEip,
370                               PEXCEPTION_RECORD pRecord, PVOID returnEax, CONTEXT *context )
371 {
372     EXCEPTION_RECORD record, newrec;
373     EXCEPTION_REGISTRATION_RECORD *frame, *dispatch;
374
375 #ifdef __i386__
376     context->Eax = (DWORD)returnEax;
377 #endif
378
379     /* build an exception record, if we do not have one */
380     if (!pRecord)
381     {
382         record.ExceptionCode    = STATUS_UNWIND;
383         record.ExceptionFlags   = 0;
384         record.ExceptionRecord  = NULL;
385         record.ExceptionAddress = GET_IP(context);
386         record.NumberParameters = 0;
387         pRecord = &record;
388     }
389
390     pRecord->ExceptionFlags |= EH_UNWINDING | (pEndFrame ? 0 : EH_EXIT_UNWIND);
391
392     TRACE( "code=%lx flags=%lx\n", pRecord->ExceptionCode, pRecord->ExceptionFlags );
393
394     /* get chain of exception frames */
395     frame = NtCurrentTeb()->Tib.ExceptionList;
396     while ((frame != (EXCEPTION_REGISTRATION_RECORD*)~0UL) && (frame != pEndFrame))
397     {
398         /* Check frame address */
399         if (pEndFrame && (frame > pEndFrame))
400         {
401             newrec.ExceptionCode    = STATUS_INVALID_UNWIND_TARGET;
402             newrec.ExceptionFlags   = EH_NONCONTINUABLE;
403             newrec.ExceptionRecord  = pRecord;
404             newrec.NumberParameters = 0;
405             RtlRaiseException( &newrec );  /* never returns */
406         }
407         if (((void*)frame < NtCurrentTeb()->Tib.StackLimit) ||
408             ((void*)(frame+1) > NtCurrentTeb()->Tib.StackBase) ||
409             (UINT_PTR)frame & 3)
410         {
411             newrec.ExceptionCode    = STATUS_BAD_STACK;
412             newrec.ExceptionFlags   = EH_NONCONTINUABLE;
413             newrec.ExceptionRecord  = pRecord;
414             newrec.NumberParameters = 0;
415             RtlRaiseException( &newrec );  /* never returns */
416         }
417
418         /* Call handler */
419         switch(EXC_CallHandler( pRecord, frame, context, &dispatch,
420                                 frame->Handler, EXC_UnwindHandler ))
421         {
422         case ExceptionContinueSearch:
423             break;
424         case ExceptionCollidedUnwind:
425             frame = dispatch;
426             break;
427         default:
428             newrec.ExceptionCode    = STATUS_INVALID_DISPOSITION;
429             newrec.ExceptionFlags   = EH_NONCONTINUABLE;
430             newrec.ExceptionRecord  = pRecord;
431             newrec.NumberParameters = 0;
432             RtlRaiseException( &newrec );  /* never returns */
433             break;
434         }
435         frame = __wine_pop_frame( frame );
436     }
437 }
438
439 /**********************************************************************/
440
441 #ifdef DEFINE_REGS_ENTRYPOINT
442 DEFINE_REGS_ENTRYPOINT( RtlUnwind, 16, 16 );
443 #else
444 void WINAPI RtlUnwind( PVOID pEndFrame, PVOID unusedEip,
445                        PEXCEPTION_RECORD pRecord, PVOID returnEax )
446 {
447     CONTEXT context;
448     memset( &context, 0, sizeof(context) );
449     __regs_RtlUnwind( pEndFrame, unusedEip, pRecord, returnEax, &context );
450 }
451 #endif
452
453
454 /*******************************************************************
455  *              NtRaiseException (NTDLL.@)
456  */
457 void WINAPI __regs_NtRaiseException( EXCEPTION_RECORD *rec, CONTEXT *ctx,
458                                   BOOL first, CONTEXT *context )
459 {
460     __regs_RtlRaiseException( rec, ctx );
461     *context = *ctx;
462 }
463
464 #ifdef DEFINE_REGS_ENTRYPOINT
465 DEFINE_REGS_ENTRYPOINT( NtRaiseException, 12, 12 );
466 #else
467 void WINAPI NtRaiseException( EXCEPTION_RECORD *rec, CONTEXT *ctx, BOOL first )
468 {
469     CONTEXT context;
470     memset( &context, 0, sizeof(context) );
471     __regs_NtRaiseException( rec, ctx, first, &context );
472 }
473 #endif
474
475
476 /***********************************************************************
477  *            RtlRaiseStatus  (NTDLL.@)
478  *
479  * Raise an exception with ExceptionCode = status
480  */
481 void WINAPI RtlRaiseStatus( NTSTATUS status )
482 {
483     EXCEPTION_RECORD ExceptionRec;
484
485     ExceptionRec.ExceptionCode    = status;
486     ExceptionRec.ExceptionFlags   = EH_NONCONTINUABLE;
487     ExceptionRec.ExceptionRecord  = NULL;
488     ExceptionRec.NumberParameters = 0;
489     RtlRaiseException( &ExceptionRec );
490 }
491
492
493 /*******************************************************************
494  *         RtlAddVectoredExceptionHandler   (NTDLL.@)
495  */
496 PVOID WINAPI RtlAddVectoredExceptionHandler( ULONG first, PVECTORED_EXCEPTION_HANDLER func )
497 {
498     VECTORED_HANDLER *handler = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*handler) );
499     if (handler)
500     {
501         handler->func = func;
502         RtlEnterCriticalSection( &vectored_handlers_section );
503         if (first) list_add_head( &vectored_handlers, &handler->entry );
504         else list_add_tail( &vectored_handlers, &handler->entry );
505         RtlLeaveCriticalSection( &vectored_handlers_section );
506     }
507     return handler;
508 }
509
510
511 /*******************************************************************
512  *         RtlRemoveVectoredExceptionHandler   (NTDLL.@)
513  */
514 ULONG WINAPI RtlRemoveVectoredExceptionHandler( PVOID handler )
515 {
516     struct list *ptr;
517     ULONG ret = FALSE;
518
519     RtlEnterCriticalSection( &vectored_handlers_section );
520     LIST_FOR_EACH( ptr, &vectored_handlers )
521     {
522         VECTORED_HANDLER *curr_handler = LIST_ENTRY( ptr, VECTORED_HANDLER, entry );
523         if (curr_handler == handler)
524         {
525             list_remove( ptr );
526             ret = TRUE;
527             break;
528         }
529     }
530     RtlLeaveCriticalSection( &vectored_handlers_section );
531     if (ret) RtlFreeHeap( GetProcessHeap(), 0, handler );
532     return ret;
533 }
534
535
536 /*************************************************************
537  *            __wine_exception_handler (NTDLL.@)
538  *
539  * Exception handler for exception blocks declared in Wine code.
540  */
541 DWORD __wine_exception_handler( EXCEPTION_RECORD *record, EXCEPTION_REGISTRATION_RECORD *frame,
542                                 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **pdispatcher )
543 {
544     __WINE_FRAME *wine_frame = (__WINE_FRAME *)frame;
545
546     if (record->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND | EH_NESTED_CALL))
547         return ExceptionContinueSearch;
548     if (wine_frame->u.filter)
549     {
550         EXCEPTION_POINTERS ptrs;
551         ptrs.ExceptionRecord = record;
552         ptrs.ContextRecord = context;
553         switch(wine_frame->u.filter( &ptrs ))
554         {
555         case EXCEPTION_CONTINUE_SEARCH:
556             return ExceptionContinueSearch;
557         case EXCEPTION_CONTINUE_EXECUTION:
558             return ExceptionContinueExecution;
559         case EXCEPTION_EXECUTE_HANDLER:
560             break;
561         default:
562             MESSAGE( "Invalid return value from exception filter\n" );
563             assert( FALSE );
564         }
565     }
566     /* hack to make GetExceptionCode() work in handler */
567     wine_frame->ExceptionCode   = record->ExceptionCode;
568     wine_frame->ExceptionRecord = wine_frame;
569
570     RtlUnwind( frame, 0, record, 0 );
571     __wine_pop_frame( frame );
572     siglongjmp( wine_frame->jmp, 1 );
573 }
574
575
576 /*************************************************************
577  *            __wine_finally_handler (NTDLL.@)
578  *
579  * Exception handler for try/finally blocks declared in Wine code.
580  */
581 DWORD __wine_finally_handler( EXCEPTION_RECORD *record, EXCEPTION_REGISTRATION_RECORD *frame,
582                               CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **pdispatcher )
583 {
584     if (record->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))
585     {
586         __WINE_FRAME *wine_frame = (__WINE_FRAME *)frame;
587         wine_frame->u.finally_func( FALSE );
588     }
589     return ExceptionContinueSearch;
590 }
591
592
593 /*************************************************************
594  *            __wine_spec_unimplemented_stub
595  *
596  * ntdll-specific implementation to avoid depending on kernel functions.
597  * Can be removed once ntdll.spec no longer contains stubs.
598  */
599 void __wine_spec_unimplemented_stub( const char *module, const char *function )
600 {
601     EXCEPTION_RECORD record;
602
603     record.ExceptionCode    = EXCEPTION_WINE_STUB;
604     record.ExceptionFlags   = EH_NONCONTINUABLE;
605     record.ExceptionRecord  = NULL;
606     record.ExceptionAddress = __wine_spec_unimplemented_stub;
607     record.NumberParameters = 2;
608     record.ExceptionInformation[0] = (ULONG_PTR)module;
609     record.ExceptionInformation[1] = (ULONG_PTR)function;
610     RtlRaiseException( &record );
611 }