winex11.drv: Release window data before calling sync_window_cursor().
[wine.git] / dlls / krnl386.exe16 / wowthunk.c
blob22471d6d612003e0f293e5ea413520fb09449145
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 "ntgdi.h"
32 #include "kernel16_private.h"
33 #include "wine/asm.h"
34 #include "wine/exception.h"
35 #include "wine/debug.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(thunk);
38 WINE_DECLARE_DEBUG_CHANNEL(relay);
39 WINE_DECLARE_DEBUG_CHANNEL(snoop);
41 /* symbols exported from relay16.s */
42 extern DWORD WINAPI wine_call_to_16( FARPROC16 target, DWORD cbArgs, PEXCEPTION_HANDLER handler );
43 extern void WINAPI wine_call_to_16_regs( CONTEXT *context, DWORD cbArgs, PEXCEPTION_HANDLER handler );
44 extern void __wine_call_to_16_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 extern const BYTE cbclient_ret[], cbclient_ret_end[];
51 __ASM_GLOBAL_FUNC( cbclient_ret,
52 "movzwl %sp,%ebx\n\t"
53 "lssl %ss:-16(%ebx),%esp\n\t"
54 "lretl\n\t"
55 ".globl " __ASM_NAME("cbclient_ret_end") "\n"
56 __ASM_NAME("cbclient_ret_end") ":" )
58 extern const BYTE cbclientex_ret[], cbclientex_ret_end[];
59 __ASM_GLOBAL_FUNC( cbclientex_ret,
60 "movzwl %bp,%ebx\n\t"
61 "subw %bp,%sp\n\t"
62 "movzwl %sp,%ebp\n\t"
63 "lssl %ss:-12(%ebx),%esp\n\t"
64 "lretl\n\t"
65 ".globl " __ASM_NAME("cbclientex_ret_end") "\n"
66 __ASM_NAME("cbclientex_ret_end") ":" )
68 /***********************************************************************
69 * WOWTHUNK_Init
71 BOOL WOWTHUNK_Init(void)
73 /* allocate the code selector for CallTo16 routines */
74 WORD codesel = SELECTOR_AllocBlock( __wine_call16_start,
75 (BYTE *)(&CallTo16_TebSelector + 1) - __wine_call16_start,
76 LDT_FLAGS_CODE | LDT_FLAGS_32BIT );
78 cbclient_selector = SELECTOR_AllocBlock( cbclient_ret, cbclient_ret_end - cbclient_ret,
79 LDT_FLAGS_CODE | LDT_FLAGS_32BIT );
80 cbclientex_selector = SELECTOR_AllocBlock( cbclientex_ret, cbclientex_ret_end - cbclientex_ret,
81 LDT_FLAGS_CODE | LDT_FLAGS_32BIT );
82 if (!codesel || !cbclient_selector || !cbclientex_selector)
83 return FALSE;
85 /* Patch the return addresses for CallTo16 routines */
87 CallTo16_DataSelector = get_ds();
88 call16_ret_addr = MAKESEGPTR( codesel, (BYTE *)__wine_call_to_16_ret - __wine_call16_start );
90 if (TRACE_ON(relay) || TRACE_ON(snoop)) RELAY16_InitDebugLists();
92 return TRUE;
96 /*************************************************************
97 * fix_selector
99 * Fix a selector load that caused an exception if it's in the
100 * 16-bit relay code.
102 static BOOL fix_selector( CONTEXT *context )
104 WORD *stack;
105 BYTE *instr = (BYTE *)context->Eip;
107 if (instr < __wine_call16_start || instr >= __wine_call16_end) return FALSE;
109 /* skip prefixes */
110 while (*instr == 0x66 || *instr == 0x67) instr++;
112 switch(instr[0])
114 case 0x07: /* pop es */
115 case 0x1f: /* pop ds */
116 break;
117 case 0x0f: /* extended instruction */
118 switch(instr[1])
120 case 0xa1: /* pop fs */
121 case 0xa9: /* pop gs */
122 break;
123 default:
124 return FALSE;
126 break;
127 default:
128 return FALSE;
130 stack = ldt_get_ptr( context->SegSs, context->Esp );
131 TRACE( "fixing up selector %x for pop instruction\n", *stack );
132 *stack = 0;
133 return TRUE;
137 /*************************************************************
138 * call16_handler
140 * Handler for exceptions occurring in 16-bit code.
142 static DWORD call16_handler( EXCEPTION_RECORD *record, EXCEPTION_REGISTRATION_RECORD *frame,
143 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **pdispatcher )
145 if (record->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))
147 /* unwinding: restore the stack pointer in the TEB, and leave the Win16 mutex */
148 STACK32FRAME *frame32 = CONTAINING_RECORD(frame, STACK32FRAME, frame);
149 kernel_get_thread_data()->stack = frame32->frame16;
150 _LeaveWin16Lock();
152 else if (record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION ||
153 record->ExceptionCode == EXCEPTION_PRIV_INSTRUCTION)
155 if (ldt_is_system(context->SegCs))
157 if (fix_selector( context )) return ExceptionContinueExecution;
159 else
161 SEGPTR gpHandler;
162 DWORD ret = __wine_emulate_instruction( record, context );
164 if (ret != ExceptionContinueSearch) return ret;
166 /* check for Win16 __GP handler */
167 if ((gpHandler = HasGPHandler16( MAKESEGPTR( context->SegCs, context->Eip ) )))
169 WORD *stack = ldt_get_ptr( context->SegSs, context->Esp );
170 *--stack = context->SegCs;
171 *--stack = context->Eip;
173 if (!IS_SELECTOR_32BIT(context->SegSs))
174 context->Esp = MAKELONG( LOWORD(context->Esp - 2*sizeof(WORD)),
175 HIWORD(context->Esp) );
176 else
177 context->Esp -= 2*sizeof(WORD);
179 context->SegCs = SELECTOROF( gpHandler );
180 context->Eip = OFFSETOF( gpHandler );
181 return ExceptionContinueExecution;
185 return ExceptionContinueSearch;
190 * 32-bit WOW routines (in WOW32, but actually forwarded to KERNEL32)
193 /**********************************************************************
194 * K32WOWGetDescriptor (KERNEL32.70)
196 BOOL WINAPI K32WOWGetDescriptor( SEGPTR segptr, LPLDT_ENTRY ldtent )
198 return GetThreadSelectorEntry( GetCurrentThread(),
199 segptr >> 16, ldtent );
202 /**********************************************************************
203 * K32WOWGetVDMPointer (KERNEL32.56)
205 LPVOID WINAPI K32WOWGetVDMPointer( DWORD vp, DWORD dwBytes, BOOL fProtectedMode )
207 /* FIXME: add size check too */
209 if ( fProtectedMode )
210 return MapSL( vp );
211 else
212 return DOSMEM_MapRealToLinear( vp );
215 /**********************************************************************
216 * K32WOWGetVDMPointerFix (KERNEL32.68)
218 LPVOID WINAPI K32WOWGetVDMPointerFix( DWORD vp, DWORD dwBytes, BOOL fProtectedMode )
221 * Hmmm. According to the docu, we should call:
223 * GlobalFix16( SELECTOROF(vp) );
225 * But this is unnecessary under Wine, as we never move global
226 * memory segments in linear memory anyway.
228 * (I'm not so sure what we are *supposed* to do if
229 * fProtectedMode is TRUE, anyway ...)
232 return K32WOWGetVDMPointer( vp, dwBytes, fProtectedMode );
235 /**********************************************************************
236 * K32WOWGetVDMPointerUnfix (KERNEL32.69)
238 VOID WINAPI K32WOWGetVDMPointerUnfix( DWORD vp )
241 * See above why we don't call:
243 * GlobalUnfix16( SELECTOROF(vp) );
248 /**********************************************************************
249 * K32WOWGlobalAlloc16 (KERNEL32.59)
251 WORD WINAPI K32WOWGlobalAlloc16( WORD wFlags, DWORD cb )
253 return (WORD)GlobalAlloc16( wFlags, cb );
256 /**********************************************************************
257 * K32WOWGlobalFree16 (KERNEL32.62)
259 WORD WINAPI K32WOWGlobalFree16( WORD hMem )
261 return (WORD)GlobalFree16( (HGLOBAL16)hMem );
264 /**********************************************************************
265 * K32WOWGlobalUnlock16 (KERNEL32.61)
267 BOOL WINAPI K32WOWGlobalUnlock16( WORD hMem )
269 return (BOOL)GlobalUnlock16( (HGLOBAL16)hMem );
272 /**********************************************************************
273 * K32WOWGlobalAllocLock16 (KERNEL32.63)
275 DWORD WINAPI K32WOWGlobalAllocLock16( WORD wFlags, DWORD cb, WORD *phMem )
277 WORD hMem = K32WOWGlobalAlloc16( wFlags, cb );
278 if (phMem) *phMem = hMem;
280 return K32WOWGlobalLock16( hMem );
283 /**********************************************************************
284 * K32WOWGlobalLockSize16 (KERNEL32.65)
286 DWORD WINAPI K32WOWGlobalLockSize16( WORD hMem, PDWORD pcb )
288 if ( pcb )
289 *pcb = GlobalSize16( (HGLOBAL16)hMem );
291 return K32WOWGlobalLock16( hMem );
294 /**********************************************************************
295 * K32WOWGlobalUnlockFree16 (KERNEL32.64)
297 WORD WINAPI K32WOWGlobalUnlockFree16( DWORD vpMem )
299 if ( !K32WOWGlobalUnlock16( HIWORD(vpMem) ) )
300 return FALSE;
302 return K32WOWGlobalFree16( HIWORD(vpMem) );
306 /**********************************************************************
307 * K32WOWYield16 (KERNEL32.66)
309 VOID WINAPI K32WOWYield16( void )
312 * This does the right thing for both Win16 and Win32 tasks.
313 * More or less, at least :-/
315 Yield16();
318 /**********************************************************************
319 * K32WOWDirectedYield16 (KERNEL32.67)
321 VOID WINAPI K32WOWDirectedYield16( WORD htask16 )
324 * Argh. Our scheduler doesn't like DirectedYield by Win32
325 * tasks at all. So we do hope that this routine is indeed
326 * only ever called by Win16 tasks that have thunked up ...
328 DirectedYield16( (HTASK16)htask16 );
331 static HANDLE gdi_handle32( WORD handle )
333 static GDI_SHARED_MEMORY *gdi_shared;
335 if (!gdi_shared)
337 if (NtCurrentTeb()->GdiBatchCount)
339 TEB64 *teb64 = (TEB64 *)(UINT_PTR)NtCurrentTeb()->GdiBatchCount;
340 PEB64 *peb64 = (PEB64 *)(UINT_PTR)teb64->Peb;
341 gdi_shared = (GDI_SHARED_MEMORY *)(UINT_PTR)peb64->GdiSharedHandleTable;
343 else gdi_shared = (GDI_SHARED_MEMORY *)NtCurrentTeb()->Peb->GdiSharedHandleTable;
344 if (!gdi_shared) return ULongToHandle( handle );
347 return ULongToHandle( (gdi_shared->Handles[handle].Unique << 16) | handle );
350 /***********************************************************************
351 * K32WOWHandle32 (KERNEL32.57)
353 HANDLE WINAPI K32WOWHandle32( WORD handle, WOW_HANDLE_TYPE type )
355 switch ( type )
357 case WOW_TYPE_HWND:
358 case WOW_TYPE_HMENU:
359 case WOW_TYPE_HDWP:
360 case WOW_TYPE_HDROP:
361 case WOW_TYPE_HACCEL:
362 return (HANDLE)(ULONG_PTR)handle;
364 case WOW_TYPE_HDC:
365 case WOW_TYPE_HFONT:
366 case WOW_TYPE_HRGN:
367 case WOW_TYPE_HBITMAP:
368 case WOW_TYPE_HBRUSH:
369 case WOW_TYPE_HPALETTE:
370 case WOW_TYPE_HPEN:
371 case WOW_TYPE_HMETAFILE:
372 return gdi_handle32( handle );
374 case WOW_TYPE_HTASK:
375 return ((TDB *)GlobalLock16(handle))->teb->ClientId.UniqueThread;
377 case WOW_TYPE_FULLHWND:
378 FIXME( "conversion of full window handles not supported yet\n" );
379 return (HANDLE)(ULONG_PTR)handle;
381 default:
382 ERR( "handle 0x%04x of unknown type %d\n", handle, type );
383 return (HANDLE)(ULONG_PTR)handle;
387 /***********************************************************************
388 * K32WOWHandle16 (KERNEL32.58)
390 WORD WINAPI K32WOWHandle16( HANDLE handle, WOW_HANDLE_TYPE type )
392 switch ( type )
394 case WOW_TYPE_HWND:
395 case WOW_TYPE_HMENU:
396 case WOW_TYPE_HDWP:
397 case WOW_TYPE_HDROP:
398 case WOW_TYPE_HDC:
399 case WOW_TYPE_HFONT:
400 case WOW_TYPE_HRGN:
401 case WOW_TYPE_HBITMAP:
402 case WOW_TYPE_HBRUSH:
403 case WOW_TYPE_HPALETTE:
404 case WOW_TYPE_HPEN:
405 case WOW_TYPE_HACCEL:
406 case WOW_TYPE_FULLHWND:
407 if ( HIWORD(handle ) )
408 ERR( "handle %p of type %d has non-zero HIWORD\n", handle, type );
409 return LOWORD(handle);
411 case WOW_TYPE_HMETAFILE:
412 FIXME( "conversion of metafile handles not supported yet\n" );
413 return LOWORD(handle);
415 case WOW_TYPE_HTASK:
416 return TASK_GetTaskFromThread( (DWORD)handle );
418 default:
419 ERR( "handle %p of unknown type %d\n", handle, type );
420 return LOWORD(handle);
424 /**********************************************************************
425 * K32WOWCallback16Ex (KERNEL32.55)
427 BOOL WINAPI K32WOWCallback16Ex( DWORD vpfn16, DWORD dwFlags,
428 DWORD cbArgs, LPVOID pArgs, LPDWORD pdwRetCode )
431 * Arguments must be prepared in the correct order by the caller
432 * (both for PASCAL and CDECL calling convention), so we simply
433 * copy them to the 16-bit stack ...
435 char *stack = (char *)CURRENT_STACK16 - cbArgs;
437 memcpy( stack, pArgs, cbArgs );
439 if (dwFlags & WCB16_REGS)
441 CONTEXT *context = (CONTEXT *)pdwRetCode;
443 if (TRACE_ON(relay))
445 DWORD count = cbArgs / sizeof(WORD);
446 WORD * wstack = (WORD *)stack;
448 TRACE_(relay)( "\1CallTo16(func=%04lx:%04x", context->SegCs, LOWORD(context->Eip) );
449 while (count) TRACE_(relay)( ",%04x", wstack[--count] );
450 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",
451 CURRENT_SS, CURRENT_SP,
452 (WORD)context->Eax, (WORD)context->Ebx, (WORD)context->Ecx,
453 (WORD)context->Edx, (WORD)context->Esi, (WORD)context->Edi,
454 (WORD)context->Ebp, (WORD)context->SegDs, (WORD)context->SegEs );
455 SYSLEVEL_CheckNotLevel( 2 );
458 /* push return address */
459 stack -= sizeof(SEGPTR);
460 *((SEGPTR *)stack) = call16_ret_addr;
461 cbArgs += sizeof(SEGPTR);
463 _EnterWin16Lock();
464 wine_call_to_16_regs( context, cbArgs, call16_handler );
465 _LeaveWin16Lock();
467 if (TRACE_ON(relay))
469 TRACE_(relay)( "\1RetFrom16() ss:sp=%04x:%04x ax=%04x bx=%04x cx=%04x dx=%04x bp=%04x sp=%04x\n",
470 CURRENT_SS, CURRENT_SP,
471 (WORD)context->Eax, (WORD)context->Ebx, (WORD)context->Ecx,
472 (WORD)context->Edx, (WORD)context->Ebp, (WORD)context->Esp );
473 SYSLEVEL_CheckNotLevel( 2 );
476 else
478 DWORD ret;
480 if (TRACE_ON(relay))
482 DWORD count = cbArgs / sizeof(WORD);
483 WORD * wstack = (WORD *)stack;
485 TRACE_(relay)( "\1CallTo16(func=%04x:%04x,ds=%04x",
486 HIWORD(vpfn16), LOWORD(vpfn16), CURRENT_SS );
487 while (count) TRACE_(relay)( ",%04x", wstack[--count] );
488 TRACE_(relay)( ") ss:sp=%04x:%04x\n", CURRENT_SS, CURRENT_SP );
489 SYSLEVEL_CheckNotLevel( 2 );
492 /* push return address */
493 stack -= sizeof(SEGPTR);
494 *((SEGPTR *)stack) = call16_ret_addr;
495 cbArgs += sizeof(SEGPTR);
498 * Actually, we should take care whether the called routine cleans up
499 * its stack or not. Fortunately, our wine_call_to_16 core doesn't rely on
500 * the callee to do so; after the routine has returned, the 16-bit
501 * stack pointer is always reset to the position it had before.
503 _EnterWin16Lock();
504 ret = wine_call_to_16( (FARPROC16)vpfn16, cbArgs, call16_handler );
505 if (pdwRetCode) *pdwRetCode = ret;
506 _LeaveWin16Lock();
508 if (TRACE_ON(relay))
510 TRACE_(relay)( "\1RetFrom16() ss:sp=%04x:%04x retval=%08lx\n", CURRENT_SS, CURRENT_SP, ret );
511 SYSLEVEL_CheckNotLevel( 2 );
515 return TRUE; /* success */
518 /**********************************************************************
519 * K32WOWCallback16 (KERNEL32.54)
521 DWORD WINAPI K32WOWCallback16( DWORD vpfn16, DWORD dwParam )
523 DWORD ret;
525 if ( !K32WOWCallback16Ex( vpfn16, WCB16_PASCAL,
526 sizeof(DWORD), &dwParam, &ret ) )
527 ret = 0L;
529 return ret;