msscript.ocx: Keep script host running as long as any script module is alive.
[wine.git] / dlls / krnl386.exe16 / wowthunk.c
blob2dddbf93289865e4adf66bdbd1ad9acca0ff743c
1 /*
2 * Win32 WOW Generic Thunk API
4 * Copyright 1999 Ulrich Weigand
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.
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.
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
22 #include <errno.h>
24 #include "wine/winbase16.h"
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "wownt32.h"
29 #include "excpt.h"
30 #include "winternl.h"
31 #include "kernel16_private.h"
32 #include "wine/exception.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(thunk);
36 WINE_DECLARE_DEBUG_CHANNEL(relay);
37 WINE_DECLARE_DEBUG_CHANNEL(snoop);
39 /* symbols exported from relay16.s */
40 extern DWORD WINAPI wine_call_to_16( FARPROC16 target, DWORD cbArgs, PEXCEPTION_HANDLER handler );
41 extern void WINAPI wine_call_to_16_regs( CONTEXT *context, DWORD cbArgs, PEXCEPTION_HANDLER handler );
42 extern void __wine_call_to_16_ret(void);
43 extern void CALL32_CBClient_Ret(void);
44 extern void CALL32_CBClientEx_Ret(void);
45 extern BYTE __wine_call16_start[];
46 extern BYTE __wine_call16_end[];
48 static SEGPTR call16_ret_addr; /* segptr to __wine_call_to_16_ret routine */
50 /***********************************************************************
51 * WOWTHUNK_Init
53 BOOL WOWTHUNK_Init(void)
55 /* allocate the code selector for CallTo16 routines */
56 WORD codesel = SELECTOR_AllocBlock( __wine_call16_start,
57 (BYTE *)(&CallTo16_TebSelector + 1) - __wine_call16_start,
58 LDT_FLAGS_CODE | LDT_FLAGS_32BIT );
59 if (!codesel) return FALSE;
61 /* Patch the return addresses for CallTo16 routines */
63 CallTo16_DataSelector = get_ds();
64 call16_ret_addr = MAKESEGPTR( codesel, (BYTE *)__wine_call_to_16_ret - __wine_call16_start );
65 CALL32_CBClient_RetAddr =
66 MAKESEGPTR( codesel, (BYTE *)CALL32_CBClient_Ret - __wine_call16_start );
67 CALL32_CBClientEx_RetAddr =
68 MAKESEGPTR( codesel, (BYTE *)CALL32_CBClientEx_Ret - __wine_call16_start );
70 if (TRACE_ON(relay) || TRACE_ON(snoop)) RELAY16_InitDebugLists();
72 return TRUE;
76 /*************************************************************
77 * fix_selector
79 * Fix a selector load that caused an exception if it's in the
80 * 16-bit relay code.
82 static BOOL fix_selector( CONTEXT *context )
84 WORD *stack;
85 BYTE *instr = (BYTE *)context->Eip;
87 if (instr < __wine_call16_start || instr >= __wine_call16_end) return FALSE;
89 /* skip prefixes */
90 while (*instr == 0x66 || *instr == 0x67) instr++;
92 switch(instr[0])
94 case 0x07: /* pop es */
95 case 0x17: /* pop ss */
96 case 0x1f: /* pop ds */
97 break;
98 case 0x0f: /* extended instruction */
99 switch(instr[1])
101 case 0xa1: /* pop fs */
102 case 0xa9: /* pop gs */
103 break;
104 default:
105 return FALSE;
107 break;
108 default:
109 return FALSE;
111 stack = ldt_get_ptr( context->SegSs, context->Esp );
112 TRACE( "fixing up selector %x for pop instruction\n", *stack );
113 *stack = 0;
114 return TRUE;
118 /*************************************************************
119 * call16_handler
121 * Handler for exceptions occurring in 16-bit code.
123 static DWORD call16_handler( EXCEPTION_RECORD *record, EXCEPTION_REGISTRATION_RECORD *frame,
124 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **pdispatcher )
126 if (record->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))
128 /* unwinding: restore the stack pointer in the TEB, and leave the Win16 mutex */
129 STACK32FRAME *frame32 = CONTAINING_RECORD(frame, STACK32FRAME, frame);
130 NtCurrentTeb()->WOW32Reserved = (void *)frame32->frame16;
131 _LeaveWin16Lock();
133 else if (record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION ||
134 record->ExceptionCode == EXCEPTION_PRIV_INSTRUCTION)
136 if (ldt_is_system(context->SegCs))
138 if (fix_selector( context )) return ExceptionContinueExecution;
140 else
142 SEGPTR gpHandler;
143 DWORD ret = __wine_emulate_instruction( record, context );
145 if (ret != ExceptionContinueSearch) return ret;
147 /* check for Win16 __GP handler */
148 if ((gpHandler = HasGPHandler16( MAKESEGPTR( context->SegCs, context->Eip ) )))
150 WORD *stack = ldt_get_ptr( context->SegSs, context->Esp );
151 *--stack = context->SegCs;
152 *--stack = context->Eip;
154 if (!IS_SELECTOR_32BIT(context->SegSs))
155 context->Esp = MAKELONG( LOWORD(context->Esp - 2*sizeof(WORD)),
156 HIWORD(context->Esp) );
157 else
158 context->Esp -= 2*sizeof(WORD);
160 context->SegCs = SELECTOROF( gpHandler );
161 context->Eip = OFFSETOF( gpHandler );
162 return ExceptionContinueExecution;
166 return ExceptionContinueSearch;
171 * 32-bit WOW routines (in WOW32, but actually forwarded to KERNEL32)
174 /**********************************************************************
175 * K32WOWGetDescriptor (KERNEL32.70)
177 BOOL WINAPI K32WOWGetDescriptor( SEGPTR segptr, LPLDT_ENTRY ldtent )
179 return GetThreadSelectorEntry( GetCurrentThread(),
180 segptr >> 16, ldtent );
183 /**********************************************************************
184 * K32WOWGetVDMPointer (KERNEL32.56)
186 LPVOID WINAPI K32WOWGetVDMPointer( DWORD vp, DWORD dwBytes, BOOL fProtectedMode )
188 /* FIXME: add size check too */
190 if ( fProtectedMode )
191 return MapSL( vp );
192 else
193 return DOSMEM_MapRealToLinear( vp );
196 /**********************************************************************
197 * K32WOWGetVDMPointerFix (KERNEL32.68)
199 LPVOID WINAPI K32WOWGetVDMPointerFix( DWORD vp, DWORD dwBytes, BOOL fProtectedMode )
202 * Hmmm. According to the docu, we should call:
204 * GlobalFix16( SELECTOROF(vp) );
206 * But this is unnecessary under Wine, as we never move global
207 * memory segments in linear memory anyway.
209 * (I'm not so sure what we are *supposed* to do if
210 * fProtectedMode is TRUE, anyway ...)
213 return K32WOWGetVDMPointer( vp, dwBytes, fProtectedMode );
216 /**********************************************************************
217 * K32WOWGetVDMPointerUnfix (KERNEL32.69)
219 VOID WINAPI K32WOWGetVDMPointerUnfix( DWORD vp )
222 * See above why we don't call:
224 * GlobalUnfix16( SELECTOROF(vp) );
229 /**********************************************************************
230 * K32WOWGlobalAlloc16 (KERNEL32.59)
232 WORD WINAPI K32WOWGlobalAlloc16( WORD wFlags, DWORD cb )
234 return (WORD)GlobalAlloc16( wFlags, cb );
237 /**********************************************************************
238 * K32WOWGlobalFree16 (KERNEL32.62)
240 WORD WINAPI K32WOWGlobalFree16( WORD hMem )
242 return (WORD)GlobalFree16( (HGLOBAL16)hMem );
245 /**********************************************************************
246 * K32WOWGlobalUnlock16 (KERNEL32.61)
248 BOOL WINAPI K32WOWGlobalUnlock16( WORD hMem )
250 return (BOOL)GlobalUnlock16( (HGLOBAL16)hMem );
253 /**********************************************************************
254 * K32WOWGlobalAllocLock16 (KERNEL32.63)
256 DWORD WINAPI K32WOWGlobalAllocLock16( WORD wFlags, DWORD cb, WORD *phMem )
258 WORD hMem = K32WOWGlobalAlloc16( wFlags, cb );
259 if (phMem) *phMem = hMem;
261 return K32WOWGlobalLock16( hMem );
264 /**********************************************************************
265 * K32WOWGlobalLockSize16 (KERNEL32.65)
267 DWORD WINAPI K32WOWGlobalLockSize16( WORD hMem, PDWORD pcb )
269 if ( pcb )
270 *pcb = GlobalSize16( (HGLOBAL16)hMem );
272 return K32WOWGlobalLock16( hMem );
275 /**********************************************************************
276 * K32WOWGlobalUnlockFree16 (KERNEL32.64)
278 WORD WINAPI K32WOWGlobalUnlockFree16( DWORD vpMem )
280 if ( !K32WOWGlobalUnlock16( HIWORD(vpMem) ) )
281 return FALSE;
283 return K32WOWGlobalFree16( HIWORD(vpMem) );
287 /**********************************************************************
288 * K32WOWYield16 (KERNEL32.66)
290 VOID WINAPI K32WOWYield16( void )
293 * This does the right thing for both Win16 and Win32 tasks.
294 * More or less, at least :-/
296 Yield16();
299 /**********************************************************************
300 * K32WOWDirectedYield16 (KERNEL32.67)
302 VOID WINAPI K32WOWDirectedYield16( WORD htask16 )
305 * Argh. Our scheduler doesn't like DirectedYield by Win32
306 * tasks at all. So we do hope that this routine is indeed
307 * only ever called by Win16 tasks that have thunked up ...
309 DirectedYield16( (HTASK16)htask16 );
313 /***********************************************************************
314 * K32WOWHandle32 (KERNEL32.57)
316 HANDLE WINAPI K32WOWHandle32( WORD handle, WOW_HANDLE_TYPE type )
318 switch ( type )
320 case WOW_TYPE_HWND:
321 case WOW_TYPE_HMENU:
322 case WOW_TYPE_HDWP:
323 case WOW_TYPE_HDROP:
324 case WOW_TYPE_HDC:
325 case WOW_TYPE_HFONT:
326 case WOW_TYPE_HRGN:
327 case WOW_TYPE_HBITMAP:
328 case WOW_TYPE_HBRUSH:
329 case WOW_TYPE_HPALETTE:
330 case WOW_TYPE_HPEN:
331 case WOW_TYPE_HACCEL:
332 return (HANDLE)(ULONG_PTR)handle;
334 case WOW_TYPE_HMETAFILE:
335 FIXME( "conversion of metafile handles not supported yet\n" );
336 return (HANDLE)(ULONG_PTR)handle;
338 case WOW_TYPE_HTASK:
339 return ((TDB *)GlobalLock16(handle))->teb->ClientId.UniqueThread;
341 case WOW_TYPE_FULLHWND:
342 FIXME( "conversion of full window handles not supported yet\n" );
343 return (HANDLE)(ULONG_PTR)handle;
345 default:
346 ERR( "handle 0x%04x of unknown type %d\n", handle, type );
347 return (HANDLE)(ULONG_PTR)handle;
351 /***********************************************************************
352 * K32WOWHandle16 (KERNEL32.58)
354 WORD WINAPI K32WOWHandle16( HANDLE handle, WOW_HANDLE_TYPE type )
356 switch ( type )
358 case WOW_TYPE_HWND:
359 case WOW_TYPE_HMENU:
360 case WOW_TYPE_HDWP:
361 case WOW_TYPE_HDROP:
362 case WOW_TYPE_HDC:
363 case WOW_TYPE_HFONT:
364 case WOW_TYPE_HRGN:
365 case WOW_TYPE_HBITMAP:
366 case WOW_TYPE_HBRUSH:
367 case WOW_TYPE_HPALETTE:
368 case WOW_TYPE_HPEN:
369 case WOW_TYPE_HACCEL:
370 case WOW_TYPE_FULLHWND:
371 if ( HIWORD(handle ) )
372 ERR( "handle %p of type %d has non-zero HIWORD\n", handle, type );
373 return LOWORD(handle);
375 case WOW_TYPE_HMETAFILE:
376 FIXME( "conversion of metafile handles not supported yet\n" );
377 return LOWORD(handle);
379 case WOW_TYPE_HTASK:
380 return TASK_GetTaskFromThread( (DWORD)handle );
382 default:
383 ERR( "handle %p of unknown type %d\n", handle, type );
384 return LOWORD(handle);
388 /**********************************************************************
389 * K32WOWCallback16Ex (KERNEL32.55)
391 BOOL WINAPI K32WOWCallback16Ex( DWORD vpfn16, DWORD dwFlags,
392 DWORD cbArgs, LPVOID pArgs, LPDWORD pdwRetCode )
395 * Arguments must be prepared in the correct order by the caller
396 * (both for PASCAL and CDECL calling convention), so we simply
397 * copy them to the 16-bit stack ...
399 char *stack = (char *)CURRENT_STACK16 - cbArgs;
401 memcpy( stack, pArgs, cbArgs );
403 if (dwFlags & (WCB16_REGS|WCB16_REGS_LONG))
405 CONTEXT *context = (CONTEXT *)pdwRetCode;
407 if (TRACE_ON(relay))
409 DWORD count = cbArgs / sizeof(WORD);
410 WORD * wstack = (WORD *)stack;
412 TRACE_(relay)( "\1CallTo16(func=%04x:%04x", context->SegCs, LOWORD(context->Eip) );
413 while (count) TRACE_(relay)( ",%04x", wstack[--count] );
414 TRACE_(relay)( ") ss:sp=%04x:%04x ax=%04x bx=%04x cx=%04x dx=%04x si=%04x di=%04x bp=%04x ds=%04x es=%04x\n",
415 SELECTOROF(NtCurrentTeb()->WOW32Reserved),
416 OFFSETOF(NtCurrentTeb()->WOW32Reserved),
417 (WORD)context->Eax, (WORD)context->Ebx, (WORD)context->Ecx,
418 (WORD)context->Edx, (WORD)context->Esi, (WORD)context->Edi,
419 (WORD)context->Ebp, (WORD)context->SegDs, (WORD)context->SegEs );
420 SYSLEVEL_CheckNotLevel( 2 );
423 /* push return address */
424 if (dwFlags & WCB16_REGS_LONG)
426 stack -= sizeof(DWORD);
427 *((DWORD *)stack) = HIWORD(call16_ret_addr);
428 stack -= sizeof(DWORD);
429 *((DWORD *)stack) = LOWORD(call16_ret_addr);
430 cbArgs += 2 * sizeof(DWORD);
432 else
434 stack -= sizeof(SEGPTR);
435 *((SEGPTR *)stack) = call16_ret_addr;
436 cbArgs += sizeof(SEGPTR);
439 _EnterWin16Lock();
440 wine_call_to_16_regs( context, cbArgs, call16_handler );
441 _LeaveWin16Lock();
443 if (TRACE_ON(relay))
445 TRACE_(relay)( "\1RetFrom16() ss:sp=%04x:%04x ax=%04x bx=%04x cx=%04x dx=%04x bp=%04x sp=%04x\n",
446 SELECTOROF(NtCurrentTeb()->WOW32Reserved),
447 OFFSETOF(NtCurrentTeb()->WOW32Reserved),
448 (WORD)context->Eax, (WORD)context->Ebx, (WORD)context->Ecx,
449 (WORD)context->Edx, (WORD)context->Ebp, (WORD)context->Esp );
450 SYSLEVEL_CheckNotLevel( 2 );
453 else
455 DWORD ret;
457 if (TRACE_ON(relay))
459 DWORD count = cbArgs / sizeof(WORD);
460 WORD * wstack = (WORD *)stack;
462 TRACE_(relay)( "\1CallTo16(func=%04x:%04x,ds=%04x",
463 HIWORD(vpfn16), LOWORD(vpfn16), SELECTOROF(NtCurrentTeb()->WOW32Reserved) );
464 while (count) TRACE_(relay)( ",%04x", wstack[--count] );
465 TRACE_(relay)( ") ss:sp=%04x:%04x\n", SELECTOROF(NtCurrentTeb()->WOW32Reserved),
466 OFFSETOF(NtCurrentTeb()->WOW32Reserved) );
467 SYSLEVEL_CheckNotLevel( 2 );
470 /* push return address */
471 stack -= sizeof(SEGPTR);
472 *((SEGPTR *)stack) = call16_ret_addr;
473 cbArgs += sizeof(SEGPTR);
476 * Actually, we should take care whether the called routine cleans up
477 * its stack or not. Fortunately, our wine_call_to_16 core doesn't rely on
478 * the callee to do so; after the routine has returned, the 16-bit
479 * stack pointer is always reset to the position it had before.
481 _EnterWin16Lock();
482 ret = wine_call_to_16( (FARPROC16)vpfn16, cbArgs, call16_handler );
483 if (pdwRetCode) *pdwRetCode = ret;
484 _LeaveWin16Lock();
486 if (TRACE_ON(relay))
488 TRACE_(relay)( "\1RetFrom16() ss:sp=%04x:%04x retval=%08x\n",
489 SELECTOROF(NtCurrentTeb()->WOW32Reserved),
490 OFFSETOF(NtCurrentTeb()->WOW32Reserved), ret );
491 SYSLEVEL_CheckNotLevel( 2 );
495 return TRUE; /* success */
498 /**********************************************************************
499 * K32WOWCallback16 (KERNEL32.54)
501 DWORD WINAPI K32WOWCallback16( DWORD vpfn16, DWORD dwParam )
503 DWORD ret;
505 if ( !K32WOWCallback16Ex( vpfn16, WCB16_PASCAL,
506 sizeof(DWORD), &dwParam, &ret ) )
507 ret = 0L;
509 return ret;