SetupSetFileQueueAlternatePlatform{AW} implemented as stub.
[wine] / dlls / kernel / wowthunk.c
1 /*
2  * Win32 WOW Generic Thunk API
3  *
4  * Copyright 1999 Ulrich Weigand
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <assert.h>
25 #include <stdarg.h>
26
27 #include "wine/winbase16.h"
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winerror.h"
31 #include "wownt32.h"
32 #include "excpt.h"
33 #include "thread.h"
34 #include "winternl.h"
35 #include "kernel_private.h"
36 #include "kernel16_private.h"
37 #include "wine/exception.h"
38 #include "wine/debug.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(thunk);
41 WINE_DECLARE_DEBUG_CHANNEL(relay);
42 WINE_DECLARE_DEBUG_CHANNEL(snoop);
43
44 /*
45  * These are the 16-bit side WOW routines.  They reside in wownt16.h
46  * in the SDK; since we don't support Win16 source code anyway, I've
47  * placed them here for compilation with Wine ...
48  */
49
50 DWORD WINAPI GetVDMPointer32W16(SEGPTR,UINT16);
51
52 DWORD WINAPI LoadLibraryEx32W16(LPCSTR,DWORD,DWORD);
53 DWORD WINAPI GetProcAddress32W16(DWORD,LPCSTR);
54 DWORD WINAPI FreeLibrary32W16(DWORD);
55
56 #define CPEX_DEST_STDCALL   0x00000000L
57 #define CPEX_DEST_CDECL     0x80000000L
58
59 /* thunk for 16-bit CreateThread */
60 struct thread_args
61 {
62     FARPROC16 proc;
63     DWORD     param;
64 };
65
66 static DWORD CALLBACK start_thread16( LPVOID threadArgs )
67 {
68     struct thread_args args = *(struct thread_args *)threadArgs;
69     HeapFree( GetProcessHeap(), 0, threadArgs );
70     return K32WOWCallback16( (DWORD)args.proc, args.param );
71 }
72
73
74 #ifdef __i386__
75
76 /* symbols exported from relay16.s */
77 extern DWORD WINAPI wine_call_to_16( FARPROC16 target, DWORD cbArgs, PEXCEPTION_HANDLER handler );
78 extern void WINAPI wine_call_to_16_regs( CONTEXT86 *context, DWORD cbArgs, PEXCEPTION_HANDLER handler );
79 extern void __wine_call_to_16_ret(void);
80 extern void CALL32_CBClient_Ret();
81 extern void CALL32_CBClientEx_Ret();
82 extern void DPMI_PendingEventCheck();
83 extern void DPMI_PendingEventCheck_Cleanup();
84 extern void DPMI_PendingEventCheck_Return();
85 extern BYTE __wine_call16_start[];
86 extern BYTE __wine_call16_end[];
87
88 extern void RELAY16_InitDebugLists(void);
89
90 static LONG CALLBACK vectored_handler( EXCEPTION_POINTERS *ptrs );
91 static SEGPTR call16_ret_addr;  /* segptr to __wine_call_to_16_ret routine */
92
93 static WORD  dpmi_checker_selector;
94 static DWORD dpmi_checker_offset_call;
95 static DWORD dpmi_checker_offset_cleanup;
96 static DWORD dpmi_checker_offset_return;
97
98 /***********************************************************************
99  *           WOWTHUNK_Init
100  */
101 BOOL WOWTHUNK_Init(void)
102 {
103     /* allocate the code selector for CallTo16 routines */
104     LDT_ENTRY entry;
105     WORD codesel = wine_ldt_alloc_entries(1);
106
107     if (!codesel) return FALSE;
108     wine_ldt_set_base( &entry, __wine_call16_start );
109     wine_ldt_set_limit( &entry, (BYTE *)(&CallTo16_TebSelector + 1) - __wine_call16_start - 1 );
110     wine_ldt_set_flags( &entry, WINE_LDT_FLAGS_CODE | WINE_LDT_FLAGS_32BIT );
111     wine_ldt_set_entry( codesel, &entry );
112
113       /* Patch the return addresses for CallTo16 routines */
114
115     CallTo16_DataSelector = wine_get_ds();
116     call16_ret_addr = MAKESEGPTR( codesel, (BYTE *)__wine_call_to_16_ret - __wine_call16_start );
117     CALL32_CBClient_RetAddr =
118         MAKESEGPTR( codesel, (BYTE *)CALL32_CBClient_Ret - __wine_call16_start );
119     CALL32_CBClientEx_RetAddr =
120         MAKESEGPTR( codesel, (BYTE *)CALL32_CBClientEx_Ret - __wine_call16_start );
121
122     /* Prepare selector and offsets for DPMI event checking. */
123     dpmi_checker_selector = codesel;
124     dpmi_checker_offset_call = (BYTE *)DPMI_PendingEventCheck - __wine_call16_start;
125     dpmi_checker_offset_cleanup = (BYTE *)DPMI_PendingEventCheck_Cleanup - __wine_call16_start;
126     dpmi_checker_offset_return = (BYTE *)DPMI_PendingEventCheck_Return - __wine_call16_start;
127
128     if (TRACE_ON(relay) || TRACE_ON(snoop)) RELAY16_InitDebugLists();
129
130     /* setup emulation of protected instructions from 32-bit code (only for Win9x versions) */
131     if (GetVersion() & 0x80000000) RtlAddVectoredExceptionHandler( TRUE, vectored_handler );
132     return TRUE;
133 }
134
135
136 /*************************************************************
137  *            fix_selector
138  *
139  * Fix a selector load that caused an exception if it's in the
140  * 16-bit relay code.
141  */
142 static BOOL fix_selector( CONTEXT *context )
143 {
144     WORD *stack;
145     BYTE *instr = (BYTE *)context->Eip;
146
147     if (instr < __wine_call16_start || instr >= __wine_call16_end) return FALSE;
148
149     /* skip prefixes */
150     while (*instr == 0x66 || *instr == 0x67) instr++;
151
152     switch(instr[0])
153     {
154     case 0x07: /* pop es */
155     case 0x17: /* pop ss */
156     case 0x1f: /* pop ds */
157         break;
158     case 0x0f: /* extended instruction */
159         switch(instr[1])
160         {
161         case 0xa1: /* pop fs */
162         case 0xa9: /* pop gs */
163             break;
164         default:
165             return FALSE;
166         }
167         break;
168     default:
169         return FALSE;
170     }
171     stack = wine_ldt_get_ptr( context->SegSs, context->Esp );
172     TRACE( "fixing up selector %x for pop instruction\n", *stack );
173     *stack = 0;
174     return TRUE;
175 }
176
177
178 /*************************************************************
179  *            insert_event_check
180  *
181  * Make resuming the context check for pending DPMI events
182  * before the original context is restored. This is required
183  * because DPMI events are asynchronous, they are blocked while 
184  * Wine 32-bit code is being executed and we want to prevent 
185  * a race when returning back to 16-bit or 32-bit DPMI context.
186  */
187 static void insert_event_check( CONTEXT *context )
188 {
189     char *stack = wine_ldt_get_ptr( context->SegSs, context->Esp );
190
191     /* don't do event check while in system code */
192     if (wine_ldt_is_system(context->SegCs))
193         return;
194
195     if(context->SegCs == dpmi_checker_selector &&
196        context->Eip   >= dpmi_checker_offset_call && 
197        context->Eip   <= dpmi_checker_offset_cleanup)
198     {
199         /*
200          * Nested call. Stack will be preserved. 
201          */
202     }
203     else if(context->SegCs == dpmi_checker_selector &&
204             context->Eip   == dpmi_checker_offset_return)
205     {
206         /*
207          * Nested call. We have just finished popping the fs
208          * register, lets put it back into stack.
209          */
210
211         stack -= sizeof(WORD);
212         *(WORD*)stack = context->SegFs;
213
214         context->Esp -= 2;
215     }
216     else
217     {
218         /*
219          * Call is not nested.
220          * Push modified registers into stack.
221          * These will be popped by the assembler stub.
222          */
223
224         stack -= sizeof(DWORD);
225         *(DWORD*)stack = context->EFlags;
226    
227         stack -= sizeof(DWORD);
228         *(DWORD*)stack = context->SegCs;
229
230         stack -= sizeof(DWORD);
231         *(DWORD*)stack = context->Eip;
232
233         stack -= sizeof(WORD);
234         *(WORD*)stack = context->SegFs;
235
236         context->Esp  -= 14;
237     }
238
239     /*
240      * Modify the context so that we jump into assembler stub.
241      * TEB access is made easier by providing the stub
242      * with the correct fs register value.
243      */
244
245     context->SegCs = dpmi_checker_selector;
246     context->Eip   = dpmi_checker_offset_call;
247     context->SegFs = wine_get_fs();
248 }
249
250
251 /*************************************************************
252  *            call16_handler
253  *
254  * Handler for exceptions occurring in 16-bit code.
255  */
256 static DWORD call16_handler( EXCEPTION_RECORD *record, EXCEPTION_REGISTRATION_RECORD *frame,
257                              CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **pdispatcher )
258 {
259     if (record->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))
260     {
261         /* unwinding: restore the stack pointer in the TEB, and leave the Win16 mutex */
262         STACK32FRAME *frame32 = (STACK32FRAME *)((char *)frame - offsetof(STACK32FRAME,frame));
263         NtCurrentTeb()->WOW32Reserved = (void *)frame32->frame16;
264         _LeaveWin16Lock();
265     }
266     else if (record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION ||
267              record->ExceptionCode == EXCEPTION_PRIV_INSTRUCTION)
268     {
269         if (wine_ldt_is_system(context->SegCs))
270         {
271             if (fix_selector( context )) return ExceptionContinueExecution;
272         }
273         else
274         {
275             SEGPTR gpHandler;
276             DWORD ret = INSTR_EmulateInstruction( record, context );
277
278             /*
279              * Insert check for pending DPMI events. Note that this 
280              * check must be inserted after instructions have been 
281              * emulated because the instruction emulation requires
282              * original CS:IP and the emulation may change TEB.dpmi_vif.
283              */
284             if(NtCurrentTeb()->dpmi_vif)
285                 insert_event_check( context );
286
287             if (ret != ExceptionContinueSearch) return ret;
288
289             /* check for Win16 __GP handler */
290             if ((gpHandler = HasGPHandler16( MAKESEGPTR( context->SegCs, context->Eip ) )))
291             {
292                 WORD *stack = wine_ldt_get_ptr( context->SegSs, context->Esp );
293                 *--stack = context->SegCs;
294                 *--stack = context->Eip;
295
296                 if (!IS_SELECTOR_32BIT(context->SegSs))
297                     context->Esp = MAKELONG( LOWORD(context->Esp - 2*sizeof(WORD)),
298                                              HIWORD(context->Esp) );
299                 else
300                     context->Esp -= 2*sizeof(WORD);
301
302                 context->SegCs = SELECTOROF( gpHandler );
303                 context->Eip   = OFFSETOF( gpHandler );
304                 return ExceptionContinueExecution;
305             }
306         }
307     }
308     else if (record->ExceptionCode == EXCEPTION_VM86_STI)
309     {
310         insert_event_check( context );
311     }
312     return ExceptionContinueSearch;
313 }
314
315
316 /*************************************************************
317  *            vm86_handler
318  *
319  * Handler for exceptions occurring in vm86 code.
320  */
321 static DWORD vm86_handler( EXCEPTION_RECORD *record, EXCEPTION_REGISTRATION_RECORD *frame,
322                            CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **pdispatcher )
323 {
324     if (record->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))
325         return ExceptionContinueSearch;
326
327     if (record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION ||
328         record->ExceptionCode == EXCEPTION_PRIV_INSTRUCTION)
329     {
330         return INSTR_EmulateInstruction( record, context );
331     }
332
333     return ExceptionContinueSearch;
334 }
335
336
337 /***********************************************************************
338  *           vectored_handler
339  *
340  * Vectored exception handler used to emulate protected instructions
341  * from 32-bit code.
342  */
343 static LONG CALLBACK vectored_handler( EXCEPTION_POINTERS *ptrs )
344 {
345     EXCEPTION_RECORD *record = ptrs->ExceptionRecord;
346     CONTEXT *context = ptrs->ContextRecord;
347
348     if (wine_ldt_is_system(context->SegCs) &&
349         (record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION ||
350          record->ExceptionCode == EXCEPTION_PRIV_INSTRUCTION))
351     {
352         if (INSTR_EmulateInstruction( record, context ) == ExceptionContinueExecution)
353             return EXCEPTION_CONTINUE_EXECUTION;
354     }
355     return EXCEPTION_CONTINUE_SEARCH;
356 }
357
358
359 #else  /* __i386__ */
360
361 BOOL WOWTHUNK_Init(void)
362 {
363     return TRUE;
364 }
365
366 #endif  /* __i386__ */
367
368
369 /*
370  *  32-bit WOW routines (in WOW32, but actually forwarded to KERNEL32)
371  */
372
373 /**********************************************************************
374  *           K32WOWGetDescriptor        (KERNEL32.70)
375  */
376 BOOL WINAPI K32WOWGetDescriptor( SEGPTR segptr, LPLDT_ENTRY ldtent )
377 {
378     return GetThreadSelectorEntry( GetCurrentThread(),
379                                    segptr >> 16, ldtent );
380 }
381
382 /**********************************************************************
383  *           K32WOWGetVDMPointer        (KERNEL32.56)
384  */
385 LPVOID WINAPI K32WOWGetVDMPointer( DWORD vp, DWORD dwBytes, BOOL fProtectedMode )
386 {
387     /* FIXME: add size check too */
388
389     if ( fProtectedMode )
390         return MapSL( vp );
391     else
392         return DOSMEM_MapRealToLinear( vp );
393 }
394
395 /**********************************************************************
396  *           K32WOWGetVDMPointerFix     (KERNEL32.68)
397  */
398 LPVOID WINAPI K32WOWGetVDMPointerFix( DWORD vp, DWORD dwBytes, BOOL fProtectedMode )
399 {
400     /*
401      * Hmmm. According to the docu, we should call:
402      *
403      *          GlobalFix16( SELECTOROF(vp) );
404      *
405      * But this is unnecessary under Wine, as we never move global
406      * memory segments in linear memory anyway.
407      *
408      * (I'm not so sure what we are *supposed* to do if
409      *  fProtectedMode is TRUE, anyway ...)
410      */
411
412     return K32WOWGetVDMPointer( vp, dwBytes, fProtectedMode );
413 }
414
415 /**********************************************************************
416  *           K32WOWGetVDMPointerUnfix   (KERNEL32.69)
417  */
418 VOID WINAPI K32WOWGetVDMPointerUnfix( DWORD vp )
419 {
420     /*
421      * See above why we don't call:
422      *
423      * GlobalUnfix16( SELECTOROF(vp) );
424      *
425      */
426 }
427
428 /**********************************************************************
429  *           K32WOWGlobalAlloc16        (KERNEL32.59)
430  */
431 WORD WINAPI K32WOWGlobalAlloc16( WORD wFlags, DWORD cb )
432 {
433     return (WORD)GlobalAlloc16( wFlags, cb );
434 }
435
436 /**********************************************************************
437  *           K32WOWGlobalFree16         (KERNEL32.62)
438  */
439 WORD WINAPI K32WOWGlobalFree16( WORD hMem )
440 {
441     return (WORD)GlobalFree16( (HGLOBAL16)hMem );
442 }
443
444 /**********************************************************************
445  *           K32WOWGlobalUnlock16       (KERNEL32.61)
446  */
447 BOOL WINAPI K32WOWGlobalUnlock16( WORD hMem )
448 {
449     return (BOOL)GlobalUnlock16( (HGLOBAL16)hMem );
450 }
451
452 /**********************************************************************
453  *           K32WOWGlobalAllocLock16    (KERNEL32.63)
454  */
455 DWORD WINAPI K32WOWGlobalAllocLock16( WORD wFlags, DWORD cb, WORD *phMem )
456 {
457     WORD hMem = K32WOWGlobalAlloc16( wFlags, cb );
458     if (phMem) *phMem = hMem;
459
460     return K32WOWGlobalLock16( hMem );
461 }
462
463 /**********************************************************************
464  *           K32WOWGlobalLockSize16     (KERNEL32.65)
465  */
466 DWORD WINAPI K32WOWGlobalLockSize16( WORD hMem, PDWORD pcb )
467 {
468     if ( pcb )
469         *pcb = GlobalSize16( (HGLOBAL16)hMem );
470
471     return K32WOWGlobalLock16( hMem );
472 }
473
474 /**********************************************************************
475  *           K32WOWGlobalUnlockFree16   (KERNEL32.64)
476  */
477 WORD WINAPI K32WOWGlobalUnlockFree16( DWORD vpMem )
478 {
479     if ( !K32WOWGlobalUnlock16( HIWORD(vpMem) ) )
480         return FALSE;
481
482     return K32WOWGlobalFree16( HIWORD(vpMem) );
483 }
484
485
486 /**********************************************************************
487  *           K32WOWYield16              (KERNEL32.66)
488  */
489 VOID WINAPI K32WOWYield16( void )
490 {
491     /*
492      * This does the right thing for both Win16 and Win32 tasks.
493      * More or less, at least :-/
494      */
495     Yield16();
496 }
497
498 /**********************************************************************
499  *           K32WOWDirectedYield16       (KERNEL32.67)
500  */
501 VOID WINAPI K32WOWDirectedYield16( WORD htask16 )
502 {
503     /*
504      * Argh.  Our scheduler doesn't like DirectedYield by Win32
505      * tasks at all.  So we do hope that this routine is indeed
506      * only ever called by Win16 tasks that have thunked up ...
507      */
508     DirectedYield16( (HTASK16)htask16 );
509 }
510
511
512 /***********************************************************************
513  *           K32WOWHandle32              (KERNEL32.57)
514  */
515 HANDLE WINAPI K32WOWHandle32( WORD handle, WOW_HANDLE_TYPE type )
516 {
517     switch ( type )
518     {
519     case WOW_TYPE_HWND:
520     case WOW_TYPE_HMENU:
521     case WOW_TYPE_HDWP:
522     case WOW_TYPE_HDROP:
523     case WOW_TYPE_HDC:
524     case WOW_TYPE_HFONT:
525     case WOW_TYPE_HRGN:
526     case WOW_TYPE_HBITMAP:
527     case WOW_TYPE_HBRUSH:
528     case WOW_TYPE_HPALETTE:
529     case WOW_TYPE_HPEN:
530     case WOW_TYPE_HACCEL:
531         return (HANDLE)(ULONG_PTR)handle;
532
533     case WOW_TYPE_HMETAFILE:
534         FIXME( "conversion of metafile handles not supported yet\n" );
535         return (HANDLE)(ULONG_PTR)handle;
536
537     case WOW_TYPE_HTASK:
538         return ((TDB *)GlobalLock16(handle))->teb->ClientId.UniqueThread;
539
540     case WOW_TYPE_FULLHWND:
541         FIXME( "conversion of full window handles not supported yet\n" );
542         return (HANDLE)(ULONG_PTR)handle;
543
544     default:
545         ERR( "handle 0x%04x of unknown type %d\n", handle, type );
546         return (HANDLE)(ULONG_PTR)handle;
547     }
548 }
549
550 /***********************************************************************
551  *           K32WOWHandle16              (KERNEL32.58)
552  */
553 WORD WINAPI K32WOWHandle16( HANDLE handle, WOW_HANDLE_TYPE type )
554 {
555     switch ( type )
556     {
557     case WOW_TYPE_HWND:
558     case WOW_TYPE_HMENU:
559     case WOW_TYPE_HDWP:
560     case WOW_TYPE_HDROP:
561     case WOW_TYPE_HDC:
562     case WOW_TYPE_HFONT:
563     case WOW_TYPE_HRGN:
564     case WOW_TYPE_HBITMAP:
565     case WOW_TYPE_HBRUSH:
566     case WOW_TYPE_HPALETTE:
567     case WOW_TYPE_HPEN:
568     case WOW_TYPE_HACCEL:
569     case WOW_TYPE_FULLHWND:
570         if ( HIWORD(handle ) )
571                 ERR( "handle %p of type %d has non-zero HIWORD\n", handle, type );
572         return LOWORD(handle);
573
574     case WOW_TYPE_HMETAFILE:
575         FIXME( "conversion of metafile handles not supported yet\n" );
576         return LOWORD(handle);
577
578     case WOW_TYPE_HTASK:
579         return TASK_GetTaskFromThread( (DWORD)handle );
580
581     default:
582         ERR( "handle %p of unknown type %d\n", handle, type );
583         return LOWORD(handle);
584     }
585 }
586
587 /**********************************************************************
588  *           K32WOWCallback16Ex         (KERNEL32.55)
589  */
590 BOOL WINAPI K32WOWCallback16Ex( DWORD vpfn16, DWORD dwFlags,
591                                 DWORD cbArgs, LPVOID pArgs, LPDWORD pdwRetCode )
592 {
593 #ifdef __i386__
594     /*
595      * Arguments must be prepared in the correct order by the caller
596      * (both for PASCAL and CDECL calling convention), so we simply
597      * copy them to the 16-bit stack ...
598      */
599     char *stack = (char *)CURRENT_STACK16 - cbArgs;
600
601     memcpy( stack, pArgs, cbArgs );
602
603     if (dwFlags & (WCB16_REGS|WCB16_REGS_LONG))
604     {
605         CONTEXT *context = (CONTEXT *)pdwRetCode;
606
607         if (TRACE_ON(relay))
608         {
609             DWORD count = cbArgs / sizeof(WORD);
610             WORD * wstack = (WORD *)stack;
611
612             DPRINTF("%04lx:CallTo16(func=%04lx:%04x,ds=%04lx",
613                     GetCurrentThreadId(),
614                     context->SegCs, LOWORD(context->Eip), context->SegDs );
615             while (count) DPRINTF( ",%04x", wstack[--count] );
616             DPRINTF(") ss:sp=%04x:%04x",
617                     SELECTOROF(NtCurrentTeb()->WOW32Reserved), OFFSETOF(NtCurrentTeb()->WOW32Reserved) );
618             DPRINTF(" ax=%04x bx=%04x cx=%04x dx=%04x si=%04x di=%04x bp=%04x es=%04x fs=%04x\n",
619                     (WORD)context->Eax, (WORD)context->Ebx, (WORD)context->Ecx,
620                     (WORD)context->Edx, (WORD)context->Esi, (WORD)context->Edi,
621                     (WORD)context->Ebp, (WORD)context->SegEs, (WORD)context->SegFs );
622             SYSLEVEL_CheckNotLevel( 2 );
623         }
624
625         if (context->EFlags & 0x00020000)  /* v86 mode */
626         {
627             EXCEPTION_REGISTRATION_RECORD frame;
628             frame.Handler = vm86_handler;
629             __wine_push_frame( &frame );
630             __wine_enter_vm86( context );
631             __wine_pop_frame( &frame );
632         }
633         else
634         {
635             /* push return address */
636             if (dwFlags & WCB16_REGS_LONG)
637             {
638                 stack -= sizeof(DWORD);
639                 *((DWORD *)stack) = HIWORD(call16_ret_addr);
640                 stack -= sizeof(DWORD);
641                 *((DWORD *)stack) = LOWORD(call16_ret_addr);
642                 cbArgs += 2 * sizeof(DWORD);
643             }
644             else
645             {
646                 stack -= sizeof(SEGPTR);
647                 *((SEGPTR *)stack) = call16_ret_addr;
648                 cbArgs += sizeof(SEGPTR);
649             }
650
651             /*
652              * Start call by checking for pending events.
653              * Note that wine_call_to_16_regs overwrites context stack
654              * pointer so we may modify it here without a problem.
655              */
656             if (NtCurrentTeb()->dpmi_vif)
657             {
658                 context->SegSs = wine_get_ds();
659                 context->Esp   = (DWORD)stack;
660                 insert_event_check( context );
661                 cbArgs += (DWORD)stack - context->Esp;
662             }
663
664             _EnterWin16Lock();
665             wine_call_to_16_regs( context, cbArgs, call16_handler );
666             _LeaveWin16Lock();
667         }
668
669         if (TRACE_ON(relay))
670         {
671             DPRINTF("%04lx:RetFrom16() ss:sp=%04x:%04x ",
672                     GetCurrentThreadId(), SELECTOROF(NtCurrentTeb()->WOW32Reserved),
673                     OFFSETOF(NtCurrentTeb()->WOW32Reserved));
674             DPRINTF(" ax=%04x bx=%04x cx=%04x dx=%04x bp=%04x sp=%04x\n",
675                     (WORD)context->Eax, (WORD)context->Ebx, (WORD)context->Ecx,
676                     (WORD)context->Edx, (WORD)context->Ebp, (WORD)context->Esp );
677             SYSLEVEL_CheckNotLevel( 2 );
678         }
679     }
680     else
681     {
682         DWORD ret;
683
684         if (TRACE_ON(relay))
685         {
686             DWORD count = cbArgs / sizeof(WORD);
687             WORD * wstack = (WORD *)stack;
688
689             DPRINTF("%04lx:CallTo16(func=%04x:%04x,ds=%04x",
690                     GetCurrentThreadId(), HIWORD(vpfn16), LOWORD(vpfn16),
691                     SELECTOROF(NtCurrentTeb()->WOW32Reserved) );
692             while (count) DPRINTF( ",%04x", wstack[--count] );
693             DPRINTF(") ss:sp=%04x:%04x\n",
694                     SELECTOROF(NtCurrentTeb()->WOW32Reserved), OFFSETOF(NtCurrentTeb()->WOW32Reserved) );
695             SYSLEVEL_CheckNotLevel( 2 );
696         }
697
698         /* push return address */
699         stack -= sizeof(SEGPTR);
700         *((SEGPTR *)stack) = call16_ret_addr;
701         cbArgs += sizeof(SEGPTR);
702
703         /*
704          * Actually, we should take care whether the called routine cleans up
705          * its stack or not.  Fortunately, our wine_call_to_16 core doesn't rely on
706          * the callee to do so; after the routine has returned, the 16-bit
707          * stack pointer is always reset to the position it had before.
708          */
709         _EnterWin16Lock();
710         ret = wine_call_to_16( (FARPROC16)vpfn16, cbArgs, call16_handler );
711         if (pdwRetCode) *pdwRetCode = ret;
712         _LeaveWin16Lock();
713
714         if (TRACE_ON(relay))
715         {
716             DPRINTF("%04lx:RetFrom16() ss:sp=%04x:%04x retval=%08lx\n",
717                     GetCurrentThreadId(), SELECTOROF(NtCurrentTeb()->WOW32Reserved),
718                     OFFSETOF(NtCurrentTeb()->WOW32Reserved), ret);
719             SYSLEVEL_CheckNotLevel( 2 );
720         }
721     }
722 #else
723     assert(0);  /* cannot call to 16-bit on non-Intel architectures */
724 #endif  /* __i386__ */
725
726     return TRUE;  /* success */
727 }
728
729 /**********************************************************************
730  *           K32WOWCallback16            (KERNEL32.54)
731  */
732 DWORD WINAPI K32WOWCallback16( DWORD vpfn16, DWORD dwParam )
733 {
734     DWORD ret;
735
736     if ( !K32WOWCallback16Ex( vpfn16, WCB16_PASCAL,
737                            sizeof(DWORD), &dwParam, &ret ) )
738         ret = 0L;
739
740     return ret;
741 }
742
743
744 /*
745  *  16-bit WOW routines (in KERNEL)
746  */
747
748 /**********************************************************************
749  *           GetVDMPointer32W      (KERNEL.516)
750  */
751 DWORD WINAPI GetVDMPointer32W16( SEGPTR vp, UINT16 fMode )
752 {
753     GlobalPageLock16(GlobalHandle16(SELECTOROF(vp)));
754     return (DWORD)K32WOWGetVDMPointer( vp, 0, (DWORD)fMode );
755 }
756
757 /***********************************************************************
758  *           LoadLibraryEx32W      (KERNEL.513)
759  */
760 DWORD WINAPI LoadLibraryEx32W16( LPCSTR lpszLibFile, DWORD hFile, DWORD dwFlags )
761 {
762     HMODULE hModule;
763     DWORD mutex_count;
764     OFSTRUCT ofs;
765     const char *p;
766
767     if (!lpszLibFile)
768     {
769         SetLastError(ERROR_INVALID_PARAMETER);
770         return 0;
771     }
772
773     /* if the file cannot be found, call LoadLibraryExA anyway, since it might be
774        a builtin module. This case is handled in MODULE_LoadLibraryExA */
775
776     if ((p = strrchr( lpszLibFile, '.' )) && !strchr( p, '\\' ))  /* got an extension */
777     {
778         if (OpenFile16( lpszLibFile, &ofs, OF_EXIST ) != HFILE_ERROR16)
779             lpszLibFile = ofs.szPathName;
780     }
781     else
782     {
783         char buffer[MAX_PATH+4];
784         strcpy( buffer, lpszLibFile );
785         strcat( buffer, ".dll" );
786         if (OpenFile16( buffer, &ofs, OF_EXIST ) != HFILE_ERROR16)
787             lpszLibFile = ofs.szPathName;
788     }
789
790     ReleaseThunkLock( &mutex_count );
791     hModule = LoadLibraryExA( lpszLibFile, (HANDLE)hFile, dwFlags );
792     RestoreThunkLock( mutex_count );
793
794     return (DWORD)hModule;
795 }
796
797 /***********************************************************************
798  *           GetProcAddress32W     (KERNEL.515)
799  */
800 DWORD WINAPI GetProcAddress32W16( DWORD hModule, LPCSTR lpszProc )
801 {
802     return (DWORD)GetProcAddress( (HMODULE)hModule, lpszProc );
803 }
804
805 /***********************************************************************
806  *           FreeLibrary32W        (KERNEL.514)
807  */
808 DWORD WINAPI FreeLibrary32W16( DWORD hLibModule )
809 {
810     BOOL retv;
811     DWORD mutex_count;
812
813     ReleaseThunkLock( &mutex_count );
814     retv = FreeLibrary( (HMODULE)hLibModule );
815     RestoreThunkLock( mutex_count );
816     return (DWORD)retv;
817 }
818
819
820 /**********************************************************************
821  *           WOW_CallProc32W
822  */
823 static DWORD WOW_CallProc32W16( FARPROC proc32, DWORD nrofargs, DWORD *args )
824 {
825     DWORD ret;
826     DWORD mutex_count;
827
828     ReleaseThunkLock( &mutex_count );
829
830     /*
831      * FIXME:  If ( nrofargs & CPEX_DEST_CDECL ) != 0, we should call a
832      *         32-bit CDECL routine ...
833      */
834
835     if (!proc32) ret = 0;
836     else switch (nrofargs)
837     {
838     case 0: ret = proc32();
839             break;
840     case 1: ret = proc32(args[0]);
841             break;
842     case 2: ret = proc32(args[0],args[1]);
843             break;
844     case 3: ret = proc32(args[0],args[1],args[2]);
845             break;
846     case 4: ret = proc32(args[0],args[1],args[2],args[3]);
847             break;
848     case 5: ret = proc32(args[0],args[1],args[2],args[3],args[4]);
849             break;
850     case 6: ret = proc32(args[0],args[1],args[2],args[3],args[4],args[5]);
851             break;
852     case 7: ret = proc32(args[0],args[1],args[2],args[3],args[4],args[5],args[6]);
853             break;
854     case 8: ret = proc32(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7]);
855             break;
856     case 9: ret = proc32(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8]);
857             break;
858     case 10:ret = proc32(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9]);
859             break;
860     case 11:ret = proc32(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9],args[10]);
861             break;
862     case 12:ret = proc32(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9],args[10],args[11]);
863             break;
864     case 13:ret = proc32(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9],args[10],args[11],args[12]);
865             break;
866     case 14:ret = proc32(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9],args[10],args[11],args[12],args[13]);
867             break;
868     case 15:ret = proc32(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9],args[10],args[11],args[12],args[13],args[14]);
869             break;
870     default:
871             /* FIXME: should go up to 32  arguments */
872             ERR("Unsupported number of arguments %ld, please report.\n",nrofargs);
873             ret = 0;
874             break;
875     }
876
877     RestoreThunkLock( mutex_count );
878
879     TRACE("returns %08lx\n",ret);
880     return ret;
881 }
882
883 /**********************************************************************
884  *           CallProc32W           (KERNEL.517)
885  */
886 DWORD WINAPIV CallProc32W16( DWORD nrofargs, DWORD argconvmask, FARPROC proc32, VA_LIST16 valist )
887 {
888     DWORD args[32];
889     unsigned int i;
890
891     TRACE("(%ld,%ld,%p args[",nrofargs,argconvmask,proc32);
892
893     for (i=0;i<nrofargs;i++)
894     {
895         if (argconvmask & (1<<i))
896         {
897             SEGPTR ptr = VA_ARG16( valist, SEGPTR );
898             /* pascal convention, have to reverse the arguments order */
899             args[nrofargs - i - 1] = (DWORD)MapSL(ptr);
900             TRACE("%08lx(%p),",ptr,MapSL(ptr));
901         }
902         else
903         {
904             DWORD arg = VA_ARG16( valist, DWORD );
905             /* pascal convention, have to reverse the arguments order */
906             args[nrofargs - i - 1] = arg;
907             TRACE("%ld,", arg);
908         }
909     }
910     TRACE("])\n");
911
912     /* POP nrofargs DWORD arguments and 3 DWORD parameters */
913     stack16_pop( (3 + nrofargs) * sizeof(DWORD) );
914
915     return WOW_CallProc32W16( proc32, nrofargs, args );
916 }
917
918 /**********************************************************************
919  *           _CallProcEx32W         (KERNEL.518)
920  */
921 DWORD WINAPIV CallProcEx32W16( DWORD nrofargs, DWORD argconvmask, FARPROC proc32, VA_LIST16 valist )
922 {
923     DWORD args[32];
924     unsigned int i;
925
926     TRACE("(%ld,%ld,%p args[",nrofargs,argconvmask,proc32);
927
928     for (i=0;i<nrofargs;i++)
929     {
930         if (argconvmask & (1<<i))
931         {
932             SEGPTR ptr = VA_ARG16( valist, SEGPTR );
933             args[i] = (DWORD)MapSL(ptr);
934             TRACE("%08lx(%p),",ptr,MapSL(ptr));
935         }
936         else
937         {
938             DWORD arg = VA_ARG16( valist, DWORD );
939             args[i] = arg;
940             TRACE("%ld,", arg);
941         }
942     }
943     TRACE("])\n");
944     return WOW_CallProc32W16( proc32, nrofargs, args );
945 }
946
947
948 /**********************************************************************
949  *           WOW16Call               (KERNEL.500)
950  *
951  * FIXME!!!
952  *
953  */
954 DWORD WINAPIV WOW16Call(WORD x, WORD y, WORD z, VA_LIST16 args)
955 {
956         int     i;
957         DWORD   calladdr;
958         FIXME("(0x%04x,0x%04x,%d),calling (",x,y,z);
959
960         for (i=0;i<x/2;i++) {
961                 WORD    a = VA_ARG16(args,WORD);
962                 DPRINTF("%04x ",a);
963         }
964         calladdr = VA_ARG16(args,DWORD);
965         stack16_pop( 3*sizeof(WORD) + x + sizeof(DWORD) );
966         DPRINTF(") calling address was 0x%08lx\n",calladdr);
967         return 0;
968 }
969
970
971 /***********************************************************************
972  *           CreateThread16   (KERNEL.441)
973  */
974 HANDLE WINAPI CreateThread16( SECURITY_ATTRIBUTES *sa, DWORD stack,
975                               FARPROC16 start, SEGPTR param,
976                               DWORD flags, LPDWORD id )
977 {
978     struct thread_args *args = HeapAlloc( GetProcessHeap(), 0, sizeof(*args) );
979     if (!args) return INVALID_HANDLE_VALUE;
980     args->proc = start;
981     args->param = param;
982     return CreateThread( sa, stack, start_thread16, args, flags, id );
983 }