do_relocations: process relocation blocks with a VirtualAddress of 0,
[wine] / loader / task.c
1 /*
2  * Task functions
3  *
4  * Copyright 1995 Alexandre Julliard
5  */
6
7 #include "config.h"
8 #include "wine/port.h"
9
10 #include <stdlib.h>
11 #include <string.h>
12 #include <assert.h>
13 #include <unistd.h>
14
15 #include "winbase.h"
16 #include "wingdi.h"
17 #include "winnt.h"
18 #include "winuser.h"
19
20 #include "wine/winbase16.h"
21 #include "drive.h"
22 #include "file.h"
23 #include "global.h"
24 #include "instance.h"
25 #include "miscemu.h"
26 #include "module.h"
27 #include "ntddk.h"
28 #include "selectors.h"
29 #include "services.h"
30 #include "wine/server.h"
31 #include "syslevel.h"
32 #include "stackframe.h"
33 #include "task.h"
34 #include "thread.h"
35 #include "toolhelp.h"
36
37 #include "debugtools.h"
38
39 DEFAULT_DEBUG_CHANNEL(task);
40 DECLARE_DEBUG_CHANNEL(relay);
41 DECLARE_DEBUG_CHANNEL(toolhelp);
42
43   /* Min. number of thunks allocated when creating a new segment */
44 #define MIN_THUNKS  32
45
46
47 static THHOOK DefaultThhook;
48 THHOOK *pThhook = &DefaultThhook;
49
50 #define hCurrentTask (pThhook->CurTDB)
51 #define hFirstTask   (pThhook->HeadTDB)
52 #define hLockedTask  (pThhook->LockTDB)
53
54 static UINT16 nTaskCount = 0;
55
56 static HTASK16 initial_task;
57
58 /***********************************************************************
59  *           TASK_InstallTHHook
60  */
61 void TASK_InstallTHHook( THHOOK *pNewThhook )
62 {
63      THHOOK *pOldThhook = pThhook;
64
65      pThhook = pNewThhook? pNewThhook : &DefaultThhook;
66
67      *pThhook = *pOldThhook;
68 }
69
70 /***********************************************************************
71  *           TASK_GetNextTask
72  */
73 HTASK16 TASK_GetNextTask( HTASK16 hTask )
74 {
75     TDB* pTask = TASK_GetPtr( hTask );
76
77     if (pTask->hNext) return pTask->hNext;
78     return (hFirstTask != hTask) ? hFirstTask : 0; 
79 }
80
81
82 /***********************************************************************
83  *           TASK_GetPtr
84  */
85 TDB *TASK_GetPtr( HTASK16 hTask )
86 {
87     return GlobalLock16( hTask );
88 }
89
90
91 /***********************************************************************
92  *           TASK_GetCurrent
93  */
94 TDB *TASK_GetCurrent(void)
95 {
96     return TASK_GetPtr( GetCurrentTask() );
97 }
98
99
100 /***********************************************************************
101  *           TASK_LinkTask
102  */
103 static void TASK_LinkTask( HTASK16 hTask )
104 {
105     HTASK16 *prevTask;
106     TDB *pTask;
107
108     if (!(pTask = TASK_GetPtr( hTask ))) return;
109     prevTask = &hFirstTask;
110     while (*prevTask)
111     {
112         TDB *prevTaskPtr = TASK_GetPtr( *prevTask );
113         if (prevTaskPtr->priority >= pTask->priority) break;
114         prevTask = &prevTaskPtr->hNext;
115     }
116     pTask->hNext = *prevTask;
117     *prevTask = hTask;
118     nTaskCount++;
119 }
120
121
122 /***********************************************************************
123  *           TASK_UnlinkTask
124  */
125 static void TASK_UnlinkTask( HTASK16 hTask )
126 {
127     HTASK16 *prevTask;
128     TDB *pTask;
129
130     prevTask = &hFirstTask;
131     while (*prevTask && (*prevTask != hTask))
132     {
133         pTask = TASK_GetPtr( *prevTask );
134         prevTask = &pTask->hNext;
135     }
136     if (*prevTask)
137     {
138         pTask = TASK_GetPtr( *prevTask );
139         *prevTask = pTask->hNext;
140         pTask->hNext = 0;
141         nTaskCount--;
142     }
143 }
144
145
146 /***********************************************************************
147  *           TASK_CreateThunks
148  *
149  * Create a thunk free-list in segment 'handle', starting from offset 'offset'
150  * and containing 'count' entries.
151  */
152 static void TASK_CreateThunks( HGLOBAL16 handle, WORD offset, WORD count )
153 {
154     int i;
155     WORD free;
156     THUNKS *pThunk;
157
158     pThunk = (THUNKS *)((BYTE *)GlobalLock16( handle ) + offset);
159     pThunk->next = 0;
160     pThunk->magic = THUNK_MAGIC;
161     pThunk->free = (int)&pThunk->thunks - (int)pThunk;
162     free = pThunk->free;
163     for (i = 0; i < count-1; i++)
164     {
165         free += 8;  /* Offset of next thunk */
166         pThunk->thunks[4*i] = free;
167     }
168     pThunk->thunks[4*i] = 0;  /* Last thunk */
169 }
170
171
172 /***********************************************************************
173  *           TASK_AllocThunk
174  *
175  * Allocate a thunk for MakeProcInstance().
176  */
177 static SEGPTR TASK_AllocThunk(void)
178 {
179     TDB *pTask;
180     THUNKS *pThunk;
181     WORD sel, base;
182
183     if (!(pTask = TASK_GetCurrent())) return 0;
184     sel = pTask->hCSAlias;
185     pThunk = &pTask->thunks;
186     base = (int)pThunk - (int)pTask;
187     while (!pThunk->free)
188     {
189         sel = pThunk->next;
190         if (!sel)  /* Allocate a new segment */
191         {
192             sel = GLOBAL_Alloc( GMEM_FIXED, sizeof(THUNKS) + (MIN_THUNKS-1)*8,
193                                 pTask->hPDB, WINE_LDT_FLAGS_CODE );
194             if (!sel) return (SEGPTR)0;
195             TASK_CreateThunks( sel, 0, MIN_THUNKS );
196             pThunk->next = sel;
197         }
198         pThunk = (THUNKS *)GlobalLock16( sel );
199         base = 0;
200     }
201     base += pThunk->free;
202     pThunk->free = *(WORD *)((BYTE *)pThunk + pThunk->free);
203     return MAKESEGPTR( sel, base );
204 }
205
206
207 /***********************************************************************
208  *           TASK_FreeThunk
209  *
210  * Free a MakeProcInstance() thunk.
211  */
212 static BOOL TASK_FreeThunk( SEGPTR thunk )
213 {
214     TDB *pTask;
215     THUNKS *pThunk;
216     WORD sel, base;
217
218     if (!(pTask = TASK_GetCurrent())) return 0;
219     sel = pTask->hCSAlias;
220     pThunk = &pTask->thunks;
221     base = (int)pThunk - (int)pTask;
222     while (sel && (sel != HIWORD(thunk)))
223     {
224         sel = pThunk->next;
225         pThunk = (THUNKS *)GlobalLock16( sel );
226         base = 0;
227     }
228     if (!sel) return FALSE;
229     *(WORD *)((BYTE *)pThunk + LOWORD(thunk) - base) = pThunk->free;
230     pThunk->free = LOWORD(thunk) - base;
231     return TRUE;
232 }
233
234
235 /***********************************************************************
236  *           TASK_Create
237  *
238  * NOTE: This routine might be called by a Win32 thread. Thus, we need
239  *       to be careful to protect global data structures. We do this
240  *       by entering the Win16Lock while linking the task into the
241  *       global task list.
242  */
243 static TDB *TASK_Create( NE_MODULE *pModule, UINT16 cmdShow, TEB *teb, LPCSTR cmdline, BYTE len )
244 {
245     HTASK16 hTask;
246     TDB *pTask;
247     char name[10];
248
249       /* Allocate the task structure */
250
251     hTask = GlobalAlloc16( GMEM_FIXED | GMEM_ZEROINIT, sizeof(TDB) );
252     if (!hTask) return NULL;
253     pTask = TASK_GetPtr( hTask );
254     FarSetOwner16( hTask, pModule->self );
255
256     /* Fill the task structure */
257
258     pTask->hSelf = hTask;
259
260     if (teb && teb->tibflags & TEBF_WIN32)
261     {
262         pTask->flags        |= TDBF_WIN32;
263         pTask->hInstance     = pModule->self;
264         pTask->hPrevInstance = 0;
265         /* NOTE: for 16-bit tasks, the instance handles are updated later on
266            in NE_InitProcess */
267     }
268
269     pTask->version       = pModule->expected_version;
270     pTask->hModule       = pModule->self;
271     pTask->hParent       = GetCurrentTask();
272     pTask->magic         = TDB_MAGIC;
273     pTask->nCmdShow      = cmdShow;
274     pTask->teb           = teb;
275     pTask->curdrive      = DRIVE_GetCurrentDrive() | 0x80;
276     strcpy( pTask->curdir, "\\" );
277     lstrcpynA( pTask->curdir + 1, DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() ),
278                  sizeof(pTask->curdir) - 1 );
279
280       /* Create the thunks block */
281
282     TASK_CreateThunks( hTask, (int)&pTask->thunks - (int)pTask, 7 );
283
284       /* Copy the module name */
285
286     GetModuleName16( pModule->self, name, sizeof(name) );
287     strncpy( pTask->module_name, name, sizeof(pTask->module_name) );
288
289       /* Allocate a selector for the PDB */
290
291     pTask->hPDB = GLOBAL_CreateBlock( GMEM_FIXED, &pTask->pdb, sizeof(PDB16),
292                                       pModule->self, WINE_LDT_FLAGS_DATA );
293
294       /* Fill the PDB */
295
296     pTask->pdb.int20 = 0x20cd;
297     pTask->pdb.dispatcher[0] = 0x9a;  /* ljmp */
298     PUT_UA_DWORD(&pTask->pdb.dispatcher[1], 
299                  (DWORD)GetProcAddress16( GetModuleHandle16("KERNEL"), "DOS3Call" ));
300     pTask->pdb.savedint22 = INT_GetPMHandler( 0x22 );
301     pTask->pdb.savedint23 = INT_GetPMHandler( 0x23 );
302     pTask->pdb.savedint24 = INT_GetPMHandler( 0x24 );
303     pTask->pdb.fileHandlesPtr =
304         MAKESEGPTR( GlobalHandleToSel16(pTask->hPDB), (int)&((PDB16 *)0)->fileHandles );
305     pTask->pdb.hFileHandles = 0;
306     memset( pTask->pdb.fileHandles, 0xff, sizeof(pTask->pdb.fileHandles) );
307     /* FIXME: should we make a copy of the environment? */
308     pTask->pdb.environment    = SELECTOROF(GetDOSEnvironment16());
309     pTask->pdb.nbFiles        = 20;
310
311     /* Fill the command line */
312
313     if (!cmdline)
314     {
315         cmdline = GetCommandLineA();
316         /* remove the first word (program name) */
317         if (*cmdline == '"')
318             if (!(cmdline = strchr( cmdline+1, '"' ))) cmdline = GetCommandLineA();
319         while (*cmdline && (*cmdline != ' ') && (*cmdline != '\t')) cmdline++;
320         while ((*cmdline == ' ') || (*cmdline == '\t')) cmdline++;
321         len = strlen(cmdline);
322     }
323     if (len >= sizeof(pTask->pdb.cmdLine)) len = sizeof(pTask->pdb.cmdLine)-1;
324     pTask->pdb.cmdLine[0] = len;
325     memcpy( pTask->pdb.cmdLine + 1, cmdline, len );
326     /* pTask->pdb.cmdLine[len+1] = 0; */
327
328     TRACE("module='%s' cmdline='%.*s' task=%04x\n", name, len, cmdline, hTask );
329
330       /* Get the compatibility flags */
331
332     pTask->compat_flags = GetProfileIntA( "Compatibility", name, 0 );
333
334       /* Allocate a code segment alias for the TDB */
335
336     pTask->hCSAlias = GLOBAL_CreateBlock( GMEM_FIXED, (void *)pTask,
337                                           sizeof(TDB), pTask->hPDB, WINE_LDT_FLAGS_CODE );
338
339       /* Default DTA overwrites command line */
340
341     pTask->dta = MAKESEGPTR( pTask->hPDB, (int)&pTask->pdb.cmdLine - (int)&pTask->pdb );
342
343     /* Create scheduler event for 16-bit tasks */
344
345     if ( !(pTask->flags & TDBF_WIN32) )
346         NtCreateEvent( &pTask->hEvent, EVENT_ALL_ACCESS, NULL, TRUE, FALSE );
347
348     /* Enter task handle into thread */
349
350     if (teb) teb->htask16 = hTask;
351     if (!initial_task) initial_task = hTask;
352
353     return pTask;
354 }
355
356
357 /***********************************************************************
358  *           TASK_DeleteTask
359  */
360 static void TASK_DeleteTask( HTASK16 hTask )
361 {
362     TDB *pTask;
363     HGLOBAL16 hPDB;
364
365     if (!(pTask = TASK_GetPtr( hTask ))) return;
366     hPDB = pTask->hPDB;
367
368     pTask->magic = 0xdead; /* invalidate signature */
369
370     /* Free the selector aliases */
371
372     GLOBAL_FreeBlock( pTask->hCSAlias );
373     GLOBAL_FreeBlock( pTask->hPDB );
374
375     /* Free the task module */
376
377     FreeModule16( pTask->hModule );
378
379     /* Free the task structure itself */
380
381     GlobalFree16( hTask );
382
383     /* Free all memory used by this task (including the 32-bit stack, */
384     /* the environment block and the thunk segments). */
385
386     GlobalFreeAll16( hPDB );
387 }
388
389
390 /***********************************************************************
391  *           TASK_CreateMainTask
392  *
393  * Create a task for the main (32-bit) process.
394  */
395 void TASK_CreateMainTask(void)
396 {
397     TDB *pTask;
398     STARTUPINFOA startup_info;
399     UINT cmdShow = 1; /* SW_SHOWNORMAL but we don't want to include winuser.h here */
400
401     GetStartupInfoA( &startup_info );
402     if (startup_info.dwFlags & STARTF_USESHOWWINDOW) cmdShow = startup_info.wShowWindow;
403     pTask = TASK_Create( (NE_MODULE *)GlobalLock16( MapHModuleLS(GetModuleHandleA(0)) ),
404                          cmdShow, NtCurrentTeb(), NULL, 0 );
405     if (!pTask)
406     {
407         ERR("could not create task for main process\n");
408         ExitProcess(1);
409     }
410
411     /* Add the task to the linked list */
412     /* (no need to get the win16 lock, we are the only thread at this point) */
413     TASK_LinkTask( pTask->hSelf );
414 }
415
416
417 /* startup routine for a new 16-bit thread */
418 static DWORD CALLBACK task_start( TDB *pTask )
419 {
420     DWORD ret;
421
422     NtCurrentTeb()->tibflags &= ~TEBF_WIN32;
423     NtCurrentTeb()->htask16 = pTask->hSelf;
424
425     _EnterWin16Lock();
426     TASK_LinkTask( pTask->hSelf );
427     pTask->teb = NtCurrentTeb();
428     ret = NE_StartTask();
429     _LeaveWin16Lock();
430     return ret;
431 }
432
433
434 /***********************************************************************
435  *           TASK_SpawnTask
436  *
437  * Spawn a new 16-bit task.
438  */
439 HTASK16 TASK_SpawnTask( NE_MODULE *pModule, WORD cmdShow,
440                         LPCSTR cmdline, BYTE len, HANDLE *hThread )
441 {
442     TDB *pTask;
443
444     if (!(pTask = TASK_Create( pModule, cmdShow, NULL, cmdline, len ))) return 0;
445     if (!(*hThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)task_start, pTask, 0, NULL )))
446     {
447         TASK_DeleteTask( pTask->hSelf );
448         return 0;
449     }
450     return pTask->hSelf;
451 }
452
453
454 /***********************************************************************
455  *           TASK_ExitTask
456  */
457 void TASK_ExitTask(void)
458 {
459     TDB *pTask; 
460     DWORD lockCount;
461
462     /* Enter the Win16Lock to protect global data structures */
463     _EnterWin16Lock();
464
465     pTask = TASK_GetCurrent();
466     if ( !pTask ) 
467     {
468         _LeaveWin16Lock();
469         return;
470     }
471
472     TRACE("Killing task %04x\n", pTask->hSelf );
473
474     /* Perform USER cleanup */
475
476     TASK_CallTaskSignalProc( USIG16_TERMINATION, pTask->hSelf );
477     PROCESS_CallUserSignalProc( USIG_PROCESS_EXIT, 0 );
478     PROCESS_CallUserSignalProc( USIG_THREAD_EXIT, 0 );
479     PROCESS_CallUserSignalProc( USIG_PROCESS_DESTROY, 0 );
480
481     /* Remove the task from the list to be sure we never switch back to it */
482     TASK_UnlinkTask( pTask->hSelf );
483
484     if (!nTaskCount || (nTaskCount == 1 && hFirstTask == initial_task))
485     {
486         TRACE("this is the last task, exiting\n" );
487         ExitKernel16();
488     }
489
490     if( nTaskCount )
491     {
492         TDB* p = TASK_GetPtr( hFirstTask );
493         while( p )
494         {
495             if( p->hYieldTo == pTask->hSelf ) p->hYieldTo = 0;
496             p = TASK_GetPtr( p->hNext );
497         }
498     }
499
500     pTask->nEvents = 0;
501
502     if ( hLockedTask == pTask->hSelf )
503         hLockedTask = 0;
504
505     TASK_DeleteTask( pTask->hSelf );
506
507     /* ... and completely release the Win16Lock, just in case. */
508     ReleaseThunkLock( &lockCount );
509 }
510
511
512 /***********************************************************************
513  *           InitTask  (KERNEL.91)
514  *
515  * Called by the application startup code.
516  */
517 void WINAPI InitTask16( CONTEXT86 *context )
518 {
519     TDB *pTask;
520     INSTANCEDATA *pinstance;
521     SEGPTR ptr;
522
523     context->Eax = 0;
524     if (!(pTask = TASK_GetCurrent())) return;
525
526     /* Note: we need to trust that BX/CX contain the stack/heap sizes, 
527        as some apps, notably Visual Basic apps, *modify* the heap/stack
528        size of the instance data segment before calling InitTask() */
529
530     /* Initialize the INSTANCEDATA structure */
531     pinstance = MapSL( MAKESEGPTR(CURRENT_DS, 0) );
532     pinstance->stackmin    = OFFSETOF( pTask->teb->cur_stack ) + sizeof( STACK16FRAME );
533     pinstance->stackbottom = pinstance->stackmin; /* yup, that's right. Confused me too. */
534     pinstance->stacktop    = ( pinstance->stackmin > LOWORD(context->Ebx) ?
535                                pinstance->stackmin - LOWORD(context->Ebx) : 0 ) + 150;
536
537     /* Initialize the local heap */
538     if (LOWORD(context->Ecx))
539         LocalInit16( GlobalHandleToSel16(pTask->hInstance), 0, LOWORD(context->Ecx) );
540
541     /* Initialize implicitly loaded DLLs */
542     NE_InitializeDLLs( pTask->hModule );
543     NE_DllProcessAttach( pTask->hModule );
544
545     /* Registers on return are:
546      * ax     1 if OK, 0 on error
547      * cx     stack limit in bytes
548      * dx     cmdShow parameter
549      * si     instance handle of the previous instance
550      * di     instance handle of the new task
551      * es:bx  pointer to command line inside PSP
552      *
553      * 0 (=%bp) is pushed on the stack
554      */
555     ptr = stack16_push( sizeof(WORD) );
556     *(WORD *)MapSL(ptr) = 0;
557     context->Esp -= 2;
558
559     context->Eax = 1;
560
561     if (!pTask->pdb.cmdLine[0]) context->Ebx = 0x80;
562     else
563     {
564         LPBYTE p = &pTask->pdb.cmdLine[1];
565         while ((*p == ' ') || (*p == '\t')) p++;
566         context->Ebx = 0x80 + (p - pTask->pdb.cmdLine);
567     }
568     context->Ecx   = pinstance->stacktop;
569     context->Edx   = pTask->nCmdShow;
570     context->Esi   = (DWORD)pTask->hPrevInstance;
571     context->Edi   = (DWORD)pTask->hInstance;
572     context->SegEs = (WORD)pTask->hPDB;
573 }
574
575
576 /***********************************************************************
577  *           WaitEvent  (KERNEL.30)
578  */
579 BOOL16 WINAPI WaitEvent16( HTASK16 hTask )
580 {
581     TDB *pTask;
582
583     if (!hTask) hTask = GetCurrentTask();
584     pTask = TASK_GetPtr( hTask );
585
586     if (pTask->flags & TDBF_WIN32)
587     {
588         FIXME("called for Win32 thread (%04x)!\n", NtCurrentTeb()->teb_sel);
589         return TRUE;
590     }
591
592     if (pTask->nEvents > 0)
593     {
594         pTask->nEvents--;
595         return FALSE;
596     }
597
598     if (pTask->teb == NtCurrentTeb())
599     {
600         DWORD lockCount;
601
602         NtResetEvent( pTask->hEvent, NULL );
603         ReleaseThunkLock( &lockCount );
604         SYSLEVEL_CheckNotLevel( 1 );
605         WaitForSingleObject( pTask->hEvent, INFINITE );
606         RestoreThunkLock( lockCount );
607         if (pTask->nEvents > 0) pTask->nEvents--;
608     }
609     else FIXME("for other task %04x cur=%04x\n",pTask->hSelf,GetCurrentTask());
610
611     return TRUE;
612 }
613
614
615 /***********************************************************************
616  *           PostEvent  (KERNEL.31)
617  */
618 void WINAPI PostEvent16( HTASK16 hTask )
619 {
620     TDB *pTask;
621
622     if (!hTask) hTask = GetCurrentTask();
623     if (!(pTask = TASK_GetPtr( hTask ))) return;
624
625     if (pTask->flags & TDBF_WIN32)
626     {
627         FIXME("called for Win32 thread (%04x)!\n", pTask->teb->teb_sel );
628         return;
629     }
630
631     pTask->nEvents++;
632
633     if (pTask->nEvents == 1) NtSetEvent( pTask->hEvent, NULL );
634 }
635
636
637 /***********************************************************************
638  *           SetPriority  (KERNEL.32)
639  */
640 void WINAPI SetPriority16( HTASK16 hTask, INT16 delta )
641 {
642     TDB *pTask;
643     INT16 newpriority;
644
645     if (!hTask) hTask = GetCurrentTask();
646     if (!(pTask = TASK_GetPtr( hTask ))) return;
647     newpriority = pTask->priority + delta;
648     if (newpriority < -32) newpriority = -32;
649     else if (newpriority > 15) newpriority = 15;
650
651     pTask->priority = newpriority + 1;
652     TASK_UnlinkTask( pTask->hSelf );
653     TASK_LinkTask( pTask->hSelf );
654     pTask->priority--;
655 }
656
657
658 /***********************************************************************
659  *           LockCurrentTask  (KERNEL.33)
660  */
661 HTASK16 WINAPI LockCurrentTask16( BOOL16 bLock )
662 {
663     if (bLock) hLockedTask = GetCurrentTask();
664     else hLockedTask = 0;
665     return hLockedTask;
666 }
667
668
669 /***********************************************************************
670  *           IsTaskLocked  (KERNEL.122)
671  */
672 HTASK16 WINAPI IsTaskLocked16(void)
673 {
674     return hLockedTask;
675 }
676
677
678 /***********************************************************************
679  *           OldYield  (KERNEL.117)
680  */
681 void WINAPI OldYield16(void)
682 {
683    DWORD count;
684
685    ReleaseThunkLock(&count);
686    RestoreThunkLock(count);
687 }
688
689 /***********************************************************************
690  *           WIN32_OldYield  (KERNEL.447)
691  */
692 void WINAPI WIN32_OldYield16(void)
693 {
694    DWORD count;
695
696    ReleaseThunkLock(&count);
697    RestoreThunkLock(count);
698 }
699
700 /***********************************************************************
701  *           DirectedYield  (KERNEL.150)
702  */
703 void WINAPI DirectedYield16( HTASK16 hTask )
704 {
705     TDB *pCurTask = TASK_GetCurrent();
706
707     if (pCurTask->flags & TDBF_WIN32)
708     {
709         FIXME("called for Win32 thread (%04x)!\n", NtCurrentTeb()->teb_sel);
710         return;
711     }
712
713     TRACE("%04x: DirectedYield(%04x)\n", pCurTask->hSelf, hTask );
714
715     pCurTask->hYieldTo = hTask;
716     OldYield16();
717
718     TRACE("%04x: back from DirectedYield(%04x)\n", pCurTask->hSelf, hTask );
719 }
720
721 /***********************************************************************
722  *           Yield  (KERNEL.29)
723  */
724 void WINAPI Yield16(void)
725 {
726     TDB *pCurTask = TASK_GetCurrent();
727
728     if (pCurTask) pCurTask->hYieldTo = 0;
729     if (pCurTask && pCurTask->hQueue)
730     {
731         HMODULE mod = GetModuleHandleA( "user32.dll" );
732         if (mod)
733         {
734             FARPROC proc = GetProcAddress( mod, "UserYield16" );
735             if (proc)
736             {
737                 proc();
738                 return;
739             }
740         }
741     }
742     OldYield16();
743 }
744
745 /***********************************************************************
746  *           KERNEL_490  (KERNEL.490)
747  */
748 HTASK16 WINAPI KERNEL_490( HTASK16 someTask )
749 {
750     if ( !someTask ) return 0;
751
752     FIXME("(%04x): stub\n", someTask );
753     return 0;
754 }
755
756 /***********************************************************************
757  *           MakeProcInstance  (KERNEL.51)
758  */
759 FARPROC16 WINAPI MakeProcInstance16( FARPROC16 func, HANDLE16 hInstance )
760 {
761     BYTE *thunk,*lfunc;
762     SEGPTR thunkaddr;
763     WORD hInstanceSelector;
764
765     hInstanceSelector = GlobalHandleToSel16(hInstance);
766
767     TRACE("(%08lx, %04x);\n", (DWORD)func, hInstance);
768
769     if (!HIWORD(func)) {
770       /* Win95 actually protects via SEH, but this is better for debugging */
771       WARN("Ouch ! Called with invalid func 0x%08lx !\n", (DWORD)func);
772       return (FARPROC16)0;
773     }
774
775     if (hInstance)
776     {
777         if ( (!(hInstance & 4)) ||
778              ((hInstance != 0xffff) && IS_SELECTOR_FREE(hInstance|7)) )
779         {
780             WARN("Invalid hInstance (%04x) passed to MakeProcInstance !\n",
781                 hInstance);
782             return 0;
783         }
784     }
785
786     if ( (GlobalHandleToSel16(CURRENT_DS) != hInstanceSelector)
787       && (hInstance != 0)
788       && (hInstance != 0xffff) )
789     {
790         /* calling MPI with a foreign DSEG is invalid ! */
791         WARN("Problem with hInstance? Got %04x, using %04x instead\n",
792                    hInstance,CURRENT_DS);
793     }
794
795     /* Always use the DSEG that MPI was entered with.
796      * We used to set hInstance to GetTaskDS16(), but this should be wrong
797      * as CURRENT_DS provides the DSEG value we need.
798      * ("calling" DS, *not* "task" DS !) */
799     hInstanceSelector = CURRENT_DS;
800     hInstance = GlobalHandle16(hInstanceSelector);
801
802     /* no thunking for DLLs */
803     if (NE_GetPtr(FarGetOwner16(hInstance))->flags & NE_FFLAGS_LIBMODULE)
804         return func;
805
806     thunkaddr = TASK_AllocThunk();
807     if (!thunkaddr) return (FARPROC16)0;
808     thunk = MapSL( thunkaddr );
809     lfunc = MapSL( (SEGPTR)func );
810
811     TRACE("(%08lx,%04x): got thunk %08lx\n",
812           (DWORD)func, hInstance, (DWORD)thunkaddr );
813     if (((lfunc[0]==0x8c) && (lfunc[1]==0xd8)) || /* movw %ds, %ax */
814         ((lfunc[0]==0x1e) && (lfunc[1]==0x58))    /* pushw %ds, popw %ax */
815     ) {
816         WARN("This was the (in)famous \"thunk useless\" warning. We thought we have to overwrite with nop;nop;, but this isn't true.\n");
817     }
818
819     *thunk++ = 0xb8;    /* movw instance, %ax */
820     *thunk++ = (BYTE)(hInstanceSelector & 0xff);
821     *thunk++ = (BYTE)(hInstanceSelector >> 8);
822     *thunk++ = 0xea;    /* ljmp func */
823     *(DWORD *)thunk = (DWORD)func;
824     return (FARPROC16)thunkaddr;
825     /* CX reg indicates if thunkaddr != NULL, implement if needed */
826 }
827
828
829 /***********************************************************************
830  *           FreeProcInstance  (KERNEL.52)
831  */
832 void WINAPI FreeProcInstance16( FARPROC16 func )
833 {
834     TRACE("(%08lx)\n", (DWORD)func );
835     TASK_FreeThunk( (SEGPTR)func );
836 }
837
838 /**********************************************************************
839  *          TASK_GetCodeSegment
840  * 
841  * Helper function for GetCodeHandle/GetCodeInfo: Retrieve the module 
842  * and logical segment number of a given code segment.
843  *
844  * 'proc' either *is* already a pair of module handle and segment number,
845  * in which case there's nothing to do.  Otherwise, it is a pointer to
846  * a function, and we need to retrieve the code segment.  If the pointer
847  * happens to point to a thunk, we'll retrieve info about the code segment
848  * where the function pointed to by the thunk resides, not the thunk itself.
849  *
850  * FIXME: if 'proc' is a SNOOP16 return stub, we should retrieve info about
851  *        the function the snoop code will return to ...
852  *
853  */
854 static BOOL TASK_GetCodeSegment( FARPROC16 proc, NE_MODULE **ppModule, 
855                                  SEGTABLEENTRY **ppSeg, int *pSegNr )
856 {
857     NE_MODULE *pModule = NULL;
858     SEGTABLEENTRY *pSeg = NULL;
859     int segNr=0;
860
861     /* Try pair of module handle / segment number */
862     pModule = (NE_MODULE *) GlobalLock16( HIWORD( proc ) );
863     if ( pModule && pModule->magic == IMAGE_OS2_SIGNATURE )
864     {
865         segNr = LOWORD( proc );
866         if ( segNr && segNr <= pModule->seg_count )
867             pSeg = NE_SEG_TABLE( pModule ) + segNr-1;
868     }
869
870     /* Try thunk or function */
871     else 
872     {
873         BYTE *thunk = MapSL( (SEGPTR)proc );
874         WORD selector;
875
876         if ((thunk[0] == 0xb8) && (thunk[3] == 0xea))
877             selector = thunk[6] + (thunk[7] << 8);
878         else
879             selector = HIWORD( proc );
880
881         pModule = NE_GetPtr( GlobalHandle16( selector ) );
882         pSeg = pModule? NE_SEG_TABLE( pModule ) : NULL;
883
884         if ( pModule )
885             for ( segNr = 1; segNr <= pModule->seg_count; segNr++, pSeg++ )
886                 if ( GlobalHandleToSel16(pSeg->hSeg) == selector )
887                     break;
888
889         if ( pModule && segNr > pModule->seg_count )
890             pSeg = NULL;
891     }
892
893     /* Abort if segment not found */
894
895     if ( !pModule || !pSeg )
896         return FALSE;
897
898     /* Return segment data */
899
900     if ( ppModule ) *ppModule = pModule;
901     if ( ppSeg    ) *ppSeg    = pSeg;
902     if ( pSegNr   ) *pSegNr   = segNr;
903
904     return TRUE;
905 }
906
907 /**********************************************************************
908  *          GetCodeHandle    (KERNEL.93)
909  */
910 HANDLE16 WINAPI GetCodeHandle16( FARPROC16 proc )
911 {
912     SEGTABLEENTRY *pSeg;
913
914     if ( !TASK_GetCodeSegment( proc, NULL, &pSeg, NULL ) )
915         return (HANDLE16)0;
916
917     return pSeg->hSeg;
918 }
919
920 /**********************************************************************
921  *          GetCodeInfo    (KERNEL.104)
922  */
923 BOOL16 WINAPI GetCodeInfo16( FARPROC16 proc, SEGINFO *segInfo )
924 {
925     NE_MODULE *pModule;
926     SEGTABLEENTRY *pSeg;
927     int segNr;
928
929     if ( !TASK_GetCodeSegment( proc, &pModule, &pSeg, &segNr ) )
930         return FALSE;
931
932     /* Fill in segment information */
933
934     segInfo->offSegment = pSeg->filepos;
935     segInfo->cbSegment  = pSeg->size;
936     segInfo->flags      = pSeg->flags;
937     segInfo->cbAlloc    = pSeg->minsize;
938     segInfo->h          = pSeg->hSeg;
939     segInfo->alignShift = pModule->alignment;
940
941     if ( segNr == pModule->dgroup )
942         segInfo->cbAlloc += pModule->heap_size + pModule->stack_size;
943
944     /* Return module handle in %es */
945
946     CURRENT_STACK16->es = GlobalHandleToSel16( pModule->self );
947
948     return TRUE;
949 }
950
951
952 /**********************************************************************
953  *          DefineHandleTable    (KERNEL.94)
954  */
955 BOOL16 WINAPI DefineHandleTable16( WORD wOffset )
956 {
957     FIXME("(%04x): stub ?\n", wOffset);
958     return TRUE;
959 }
960
961
962 /***********************************************************************
963  *           SetTaskQueue  (KERNEL.34)
964  */
965 HQUEUE16 WINAPI SetTaskQueue16( HTASK16 hTask, HQUEUE16 hQueue )
966 {
967     HQUEUE16 hPrev;
968     TDB *pTask;
969
970     if (!hTask) hTask = GetCurrentTask();
971     if (!(pTask = TASK_GetPtr( hTask ))) return 0;
972
973     hPrev = pTask->hQueue;
974     pTask->hQueue = hQueue;
975
976     return hPrev;
977 }
978
979
980 /***********************************************************************
981  *           GetTaskQueue  (KERNEL.35)
982  */
983 HQUEUE16 WINAPI GetTaskQueue16( HTASK16 hTask )
984 {
985     TDB *pTask;
986
987     if (!hTask) hTask = GetCurrentTask();
988     if (!(pTask = TASK_GetPtr( hTask ))) return 0;
989     return pTask->hQueue;
990 }
991
992 /***********************************************************************
993  *           SetThreadQueue  (KERNEL.463)
994  */
995 HQUEUE16 WINAPI SetThreadQueue16( DWORD thread, HQUEUE16 hQueue )
996 {
997     TEB *teb = thread? THREAD_IdToTEB( thread ) : NtCurrentTeb();
998     HQUEUE16 oldQueue = teb? teb->queue : 0;
999
1000     if ( teb )
1001     {
1002         teb->queue = hQueue;
1003
1004         if ( GetTaskQueue16( teb->htask16 ) == oldQueue )
1005             SetTaskQueue16( teb->htask16, hQueue );
1006     }
1007
1008     return oldQueue;
1009 }
1010
1011 /***********************************************************************
1012  *           GetThreadQueue  (KERNEL.464)
1013  */
1014 HQUEUE16 WINAPI GetThreadQueue16( DWORD thread )
1015 {
1016     TEB *teb = NULL;
1017     if ( !thread )
1018         teb = NtCurrentTeb();
1019     else if ( HIWORD(thread) )
1020         teb = THREAD_IdToTEB( thread );
1021     else if ( IsTask16( (HTASK16)thread ) )
1022         teb = (TASK_GetPtr( (HANDLE16)thread ))->teb;
1023
1024     return (HQUEUE16)(teb? teb->queue : 0);
1025 }
1026
1027 /***********************************************************************
1028  *           SetFastQueue  (KERNEL.624)
1029  */
1030 VOID WINAPI SetFastQueue16( DWORD thread, HANDLE hQueue )
1031 {
1032     TEB *teb = NULL;
1033     if ( !thread )
1034         teb = NtCurrentTeb();
1035     else if ( HIWORD(thread) )
1036         teb = THREAD_IdToTEB( thread );
1037     else if ( IsTask16( (HTASK16)thread ) )
1038         teb = (TASK_GetPtr( (HANDLE16)thread ))->teb;
1039
1040     if ( teb ) teb->queue = (HQUEUE16) hQueue;
1041 }
1042
1043 /***********************************************************************
1044  *           GetFastQueue  (KERNEL.625)
1045  */
1046 HANDLE WINAPI GetFastQueue16( void )
1047 {
1048     HANDLE ret = (HANDLE)NtCurrentTeb()->queue;
1049
1050     if (!ret) FIXME("(): should initialize thread-local queue, expect failure!\n" );
1051     return ret;
1052 }
1053
1054 /***********************************************************************
1055  *           SwitchStackTo   (KERNEL.108)
1056  */
1057 void WINAPI SwitchStackTo16( WORD seg, WORD ptr, WORD top )
1058 {
1059     TDB *pTask;
1060     STACK16FRAME *oldFrame, *newFrame;
1061     INSTANCEDATA *pData;
1062     UINT16 copySize;
1063
1064     if (!(pTask = TASK_GetCurrent())) return;
1065     if (!(pData = (INSTANCEDATA *)GlobalLock16( seg ))) return;
1066     TRACE("old=%04x:%04x new=%04x:%04x\n",
1067           SELECTOROF( pTask->teb->cur_stack ),
1068           OFFSETOF( pTask->teb->cur_stack ), seg, ptr );
1069
1070     /* Save the old stack */
1071
1072     oldFrame = THREAD_STACK16( pTask->teb );
1073     /* pop frame + args and push bp */
1074     pData->old_ss_sp   = pTask->teb->cur_stack + sizeof(STACK16FRAME)
1075                            + 2 * sizeof(WORD);
1076     *(WORD *)MapSL(pData->old_ss_sp) = oldFrame->bp;
1077     pData->stacktop    = top;
1078     pData->stackmin    = ptr;
1079     pData->stackbottom = ptr;
1080
1081     /* Switch to the new stack */
1082
1083     /* Note: we need to take the 3 arguments into account; otherwise,
1084      * the stack will underflow upon return from this function.
1085      */
1086     copySize = oldFrame->bp - OFFSETOF(pData->old_ss_sp);
1087     copySize += 3 * sizeof(WORD) + sizeof(STACK16FRAME);
1088     pTask->teb->cur_stack = MAKESEGPTR( seg, ptr - copySize );
1089     newFrame = THREAD_STACK16( pTask->teb );
1090
1091     /* Copy the stack frame and the local variables to the new stack */
1092
1093     memmove( newFrame, oldFrame, copySize );
1094     newFrame->bp = ptr;
1095     *(WORD *)MapSL( MAKESEGPTR( seg, ptr ) ) = 0;  /* clear previous bp */
1096 }
1097
1098
1099 /***********************************************************************
1100  *           SwitchStackBack   (KERNEL.109)
1101  */
1102 void WINAPI SwitchStackBack16( CONTEXT86 *context )
1103 {
1104     STACK16FRAME *oldFrame, *newFrame;
1105     INSTANCEDATA *pData;
1106
1107     if (!(pData = (INSTANCEDATA *)GlobalLock16(SELECTOROF(NtCurrentTeb()->cur_stack))))
1108         return;
1109     if (!pData->old_ss_sp)
1110     {
1111         WARN("No previous SwitchStackTo\n" );
1112         return;
1113     }
1114     TRACE("restoring stack %04x:%04x\n",
1115           SELECTOROF(pData->old_ss_sp), OFFSETOF(pData->old_ss_sp) );
1116
1117     oldFrame = CURRENT_STACK16;
1118
1119     /* Pop bp from the previous stack */
1120
1121     BP_reg(context) = *(WORD *)MapSL(pData->old_ss_sp);
1122     pData->old_ss_sp += sizeof(WORD);
1123
1124     /* Switch back to the old stack */
1125
1126     NtCurrentTeb()->cur_stack = pData->old_ss_sp - sizeof(STACK16FRAME);
1127     context->SegSs = SELECTOROF(pData->old_ss_sp);
1128     context->Esp   = OFFSETOF(pData->old_ss_sp) - sizeof(DWORD); /*ret addr*/
1129     pData->old_ss_sp = 0;
1130
1131     /* Build a stack frame for the return */
1132
1133     newFrame = CURRENT_STACK16;
1134     newFrame->frame32     = oldFrame->frame32;
1135     newFrame->module_cs   = oldFrame->module_cs;
1136     newFrame->callfrom_ip = oldFrame->callfrom_ip;
1137     newFrame->entry_ip    = oldFrame->entry_ip;
1138 }
1139
1140
1141 /***********************************************************************
1142  *           GetTaskQueueDS  (KERNEL.118)
1143  */
1144 void WINAPI GetTaskQueueDS16(void)
1145 {
1146     CURRENT_STACK16->ds = GlobalHandleToSel16( GetTaskQueue16(0) );
1147 }
1148
1149
1150 /***********************************************************************
1151  *           GetTaskQueueES  (KERNEL.119)
1152  */
1153 void WINAPI GetTaskQueueES16(void)
1154 {
1155     CURRENT_STACK16->es = GlobalHandleToSel16( GetTaskQueue16(0) );
1156 }
1157
1158
1159 /***********************************************************************
1160  *           GetCurrentTask   (KERNEL32.@)
1161  */
1162 HTASK16 WINAPI GetCurrentTask(void)
1163 {
1164     return NtCurrentTeb()->htask16;
1165 }
1166
1167 /***********************************************************************
1168  *           GetCurrentTask   (KERNEL.36)
1169  */
1170 DWORD WINAPI WIN16_GetCurrentTask(void)
1171 {
1172     /* This is the version used by relay code; the first task is */
1173     /* returned in the high word of the result */
1174     return MAKELONG( GetCurrentTask(), hFirstTask );
1175 }
1176
1177
1178 /***********************************************************************
1179  *           GetCurrentPDB   (KERNEL.37)
1180  *
1181  * UNDOC: returns PSP of KERNEL in high word
1182  */
1183 DWORD WINAPI GetCurrentPDB16(void)
1184 {
1185     TDB *pTask;
1186
1187     if (!(pTask = TASK_GetCurrent())) return 0;
1188     return MAKELONG(pTask->hPDB, 0); /* FIXME */
1189 }
1190
1191
1192 /***********************************************************************
1193  *           GetCurPID   (KERNEL.157)
1194  */
1195 DWORD WINAPI GetCurPID16( DWORD unused )
1196 {
1197     return 0;
1198 }
1199
1200
1201 /***********************************************************************
1202  *           GetInstanceData   (KERNEL.54)
1203  */
1204 INT16 WINAPI GetInstanceData16( HINSTANCE16 instance, WORD buffer, INT16 len )
1205 {
1206     char *ptr = (char *)GlobalLock16( instance );
1207     if (!ptr || !len) return 0;
1208     if ((int)buffer + len >= 0x10000) len = 0x10000 - buffer;
1209     memcpy( (char *)GlobalLock16(CURRENT_DS) + buffer, ptr + buffer, len );
1210     return len;
1211 }
1212
1213
1214 /***********************************************************************
1215  *           GetExeVersion   (KERNEL.105)
1216  */
1217 WORD WINAPI GetExeVersion16(void)
1218 {
1219     TDB *pTask;
1220
1221     if (!(pTask = TASK_GetCurrent())) return 0;
1222     return pTask->version;
1223 }
1224
1225
1226 /***********************************************************************
1227  *           SetErrorMode   (KERNEL.107)
1228  */
1229 UINT16 WINAPI SetErrorMode16( UINT16 mode )
1230 {
1231     TDB *pTask;
1232     if (!(pTask = TASK_GetCurrent())) return 0;
1233     pTask->error_mode = mode;
1234     return SetErrorMode( mode );
1235 }
1236
1237
1238 /***********************************************************************
1239  *           GetNumTasks   (KERNEL.152)
1240  */
1241 UINT16 WINAPI GetNumTasks16(void)
1242 {
1243     return nTaskCount;
1244 }
1245
1246
1247 /***********************************************************************
1248  *           GetTaskDS   (KERNEL.155)
1249  *
1250  * Note: this function apparently returns a DWORD with LOWORD == HIWORD.
1251  * I don't think we need to bother with this.
1252  */
1253 HINSTANCE16 WINAPI GetTaskDS16(void)
1254 {
1255     TDB *pTask;
1256
1257     if (!(pTask = TASK_GetCurrent())) return 0;
1258     return GlobalHandleToSel16(pTask->hInstance);
1259 }
1260
1261 /***********************************************************************
1262  *           GetDummyModuleHandleDS   (KERNEL.602)
1263  */
1264 WORD WINAPI GetDummyModuleHandleDS16(void)
1265 {
1266     TDB *pTask;
1267     WORD selector;
1268
1269     if (!(pTask = TASK_GetCurrent())) return 0;
1270     if (!(pTask->flags & TDBF_WIN32)) return 0;
1271     selector = GlobalHandleToSel16( pTask->hModule );
1272     CURRENT_DS = selector;
1273     return selector;
1274 }
1275
1276 /***********************************************************************
1277  *           IsTask   (KERNEL.320)
1278  *           IsTask16 (KERNEL32.@)
1279  */
1280 BOOL16 WINAPI IsTask16( HTASK16 hTask )
1281 {
1282     TDB *pTask;
1283
1284     if (!(pTask = TASK_GetPtr( hTask ))) return FALSE;
1285     if (GlobalSize16( hTask ) < sizeof(TDB)) return FALSE;
1286     return (pTask->magic == TDB_MAGIC);
1287 }
1288
1289
1290 /***********************************************************************
1291  *           IsWinOldApTask   (KERNEL.158)
1292  */
1293 BOOL16 WINAPI IsWinOldApTask16( HTASK16 hTask )
1294 {
1295     /* should return bit 0 of byte 0x48 in PSP */
1296     return FALSE;
1297 }
1298
1299 /***********************************************************************
1300  *           SetTaskSignalProc   (KERNEL.38)
1301  */
1302 FARPROC16 WINAPI SetTaskSignalProc( HTASK16 hTask, FARPROC16 proc )
1303 {
1304     TDB *pTask;
1305     FARPROC16 oldProc;
1306
1307     if (!hTask) hTask = GetCurrentTask();
1308     if (!(pTask = TASK_GetPtr( hTask ))) return NULL;
1309     oldProc = pTask->userhandler;
1310     pTask->userhandler = proc;
1311     return oldProc;
1312 }
1313
1314 /***********************************************************************
1315  *           TASK_CallTaskSignalProc
1316  */
1317 /* ### start build ### */
1318 extern WORD CALLBACK TASK_CallTo16_word_wwwww(FARPROC16,WORD,WORD,WORD,WORD,WORD);
1319 /* ### stop build ### */
1320 void TASK_CallTaskSignalProc( UINT16 uCode, HANDLE16 hTaskOrModule )
1321 {
1322     TDB *pTask = TASK_GetCurrent();
1323     if ( !pTask || !pTask->userhandler ) return;
1324
1325     TASK_CallTo16_word_wwwww( pTask->userhandler, 
1326                               hTaskOrModule, uCode, 0, 
1327                               pTask->hInstance, pTask->hQueue );
1328 }
1329
1330 /***********************************************************************
1331  *           SetSigHandler   (KERNEL.140)
1332  */
1333 WORD WINAPI SetSigHandler16( FARPROC16 newhandler, FARPROC16* oldhandler,
1334                            UINT16 *oldmode, UINT16 newmode, UINT16 flag )
1335 {
1336     FIXME("(%p,%p,%p,%d,%d), unimplemented.\n",
1337           newhandler,oldhandler,oldmode,newmode,flag );
1338
1339     if (flag != 1) return 0;
1340     if (!newmode) newhandler = NULL;  /* Default handler */
1341     if (newmode != 4)
1342     {
1343         TDB *pTask;
1344
1345         if (!(pTask = TASK_GetCurrent())) return 0;
1346         if (oldmode) *oldmode = pTask->signal_flags;
1347         pTask->signal_flags = newmode;
1348         if (oldhandler) *oldhandler = pTask->sighandler;
1349         pTask->sighandler = newhandler;
1350     }
1351     return 0;
1352 }
1353
1354
1355 /***********************************************************************
1356  *           GlobalNotify   (KERNEL.154)
1357  *
1358  * Note that GlobalNotify does _not_ return the old NotifyProc
1359  * -- contrary to LocalNotify !!
1360  */
1361 VOID WINAPI GlobalNotify16( FARPROC16 proc )
1362 {
1363     TDB *pTask;
1364
1365     if (!(pTask = TASK_GetCurrent())) return;
1366     pTask->discardhandler = proc;
1367 }
1368
1369
1370 /***********************************************************************
1371  *           GetExePtrHelper
1372  */
1373 static inline HMODULE16 GetExePtrHelper( HANDLE16 handle, HTASK16 *hTask )
1374 {
1375     char *ptr;
1376     HANDLE16 owner;
1377
1378       /* Check for module handle */
1379
1380     if (!(ptr = GlobalLock16( handle ))) return 0;
1381     if (((NE_MODULE *)ptr)->magic == IMAGE_OS2_SIGNATURE) return handle;
1382
1383       /* Search for this handle inside all tasks */
1384
1385     *hTask = hFirstTask;
1386     while (*hTask)
1387     {
1388         TDB *pTask = TASK_GetPtr( *hTask );
1389         if ((*hTask == handle) ||
1390             (pTask->hInstance == handle) ||
1391             (pTask->hQueue == handle) ||
1392             (pTask->hPDB == handle)) return pTask->hModule;
1393         *hTask = pTask->hNext;
1394     }
1395
1396       /* Check the owner for module handle */
1397
1398     owner = FarGetOwner16( handle );
1399     if (!(ptr = GlobalLock16( owner ))) return 0;
1400     if (((NE_MODULE *)ptr)->magic == IMAGE_OS2_SIGNATURE) return owner;
1401
1402       /* Search for the owner inside all tasks */
1403
1404     *hTask = hFirstTask;
1405     while (*hTask)
1406     {
1407         TDB *pTask = TASK_GetPtr( *hTask );
1408         if ((*hTask == owner) ||
1409             (pTask->hInstance == owner) ||
1410             (pTask->hQueue == owner) ||
1411             (pTask->hPDB == owner)) return pTask->hModule;
1412         *hTask = pTask->hNext;
1413     }
1414
1415     return 0;
1416 }
1417
1418 /***********************************************************************
1419  *           GetExePtr   (KERNEL.133)
1420  */
1421 HMODULE16 WINAPI WIN16_GetExePtr( HANDLE16 handle )
1422 {
1423     HTASK16 hTask = 0;
1424     HMODULE16 hModule = GetExePtrHelper( handle, &hTask );
1425     STACK16FRAME *frame = CURRENT_STACK16;
1426     frame->ecx = hModule;
1427     if (hTask) frame->es = hTask;
1428     return hModule;
1429 }
1430
1431
1432 /***********************************************************************
1433  *           K228   (KERNEL.228)
1434  */
1435 HMODULE16 WINAPI GetExePtr( HANDLE16 handle )
1436 {
1437     HTASK16 hTask = 0;
1438     return GetExePtrHelper( handle, &hTask );
1439 }
1440
1441
1442 /***********************************************************************
1443  *           TaskFirst   (TOOLHELP.63)
1444  */
1445 BOOL16 WINAPI TaskFirst16( TASKENTRY *lpte )
1446 {
1447     lpte->hNext = hFirstTask;
1448     return TaskNext16( lpte );
1449 }
1450
1451
1452 /***********************************************************************
1453  *           TaskNext   (TOOLHELP.64)
1454  */
1455 BOOL16 WINAPI TaskNext16( TASKENTRY *lpte )
1456 {
1457     TDB *pTask;
1458     INSTANCEDATA *pInstData;
1459
1460     TRACE_(toolhelp)("(%p): task=%04x\n", lpte, lpte->hNext );
1461     if (!lpte->hNext) return FALSE;
1462
1463     /* make sure that task and hInstance are valid (skip initial Wine task !) */
1464     while (1) {
1465         pTask = TASK_GetPtr( lpte->hNext );
1466         if (!pTask || pTask->magic != TDB_MAGIC) return FALSE;
1467         if (pTask->hInstance)
1468             break;
1469         lpte->hNext = pTask->hNext;
1470     }
1471     pInstData = MapSL( MAKESEGPTR( GlobalHandleToSel16(pTask->hInstance), 0 ) );
1472     lpte->hTask         = lpte->hNext;
1473     lpte->hTaskParent   = pTask->hParent;
1474     lpte->hInst         = pTask->hInstance;
1475     lpte->hModule       = pTask->hModule;
1476     lpte->wSS           = SELECTOROF( pTask->teb->cur_stack );
1477     lpte->wSP           = OFFSETOF( pTask->teb->cur_stack );
1478     lpte->wStackTop     = pInstData->stacktop;
1479     lpte->wStackMinimum = pInstData->stackmin;
1480     lpte->wStackBottom  = pInstData->stackbottom;
1481     lpte->wcEvents      = pTask->nEvents;
1482     lpte->hQueue        = pTask->hQueue;
1483     lstrcpynA( lpte->szModule, pTask->module_name, sizeof(lpte->szModule) );
1484     lpte->wPSPOffset    = 0x100;  /*??*/
1485     lpte->hNext         = pTask->hNext;
1486     return TRUE;
1487 }
1488
1489
1490 /***********************************************************************
1491  *           TaskFindHandle   (TOOLHELP.65)
1492  */
1493 BOOL16 WINAPI TaskFindHandle16( TASKENTRY *lpte, HTASK16 hTask )
1494 {
1495     lpte->hNext = hTask;
1496     return TaskNext16( lpte );
1497 }
1498
1499
1500 typedef INT (WINAPI *MessageBoxA_funcptr)(HWND hWnd, LPCSTR text, LPCSTR title, UINT type);
1501
1502 /**************************************************************************
1503  *           FatalAppExit   (KERNEL.137)
1504  */
1505 void WINAPI FatalAppExit16( UINT16 action, LPCSTR str )
1506 {
1507     TDB *pTask = TASK_GetCurrent();
1508
1509     if (!pTask || !(pTask->error_mode & SEM_NOGPFAULTERRORBOX))
1510     {
1511         HMODULE mod = GetModuleHandleA( "user32.dll" );
1512         if (mod)
1513         {
1514             MessageBoxA_funcptr pMessageBoxA = (MessageBoxA_funcptr)GetProcAddress( mod, "MessageBoxA" );
1515             if (pMessageBoxA)
1516             {
1517                 pMessageBoxA( 0, str, NULL, MB_SYSTEMMODAL | MB_OK );
1518                 goto done;
1519             }
1520         }
1521         ERR( "%s\n", debugstr_a(str) );
1522     }
1523  done:
1524     ExitThread(0xff);
1525 }
1526
1527
1528 /***********************************************************************
1529  *           TerminateApp   (TOOLHELP.77)
1530  *
1531  * See "Undocumented Windows".
1532  */
1533 void WINAPI TerminateApp16(HTASK16 hTask, WORD wFlags)
1534 {
1535     if (hTask && hTask != GetCurrentTask())
1536     {
1537         FIXME("cannot terminate task %x\n", hTask);
1538         return;
1539     }
1540
1541     if (wFlags & NO_UAE_BOX)
1542     {
1543         UINT16 old_mode;
1544         old_mode = SetErrorMode16(0);
1545         SetErrorMode16(old_mode|SEM_NOGPFAULTERRORBOX);
1546     }
1547     FatalAppExit16( 0, NULL );
1548
1549     /* hmm, we're still alive ?? */
1550
1551     /* check undocumented flag */
1552     if (!(wFlags & 0x8000))
1553         TASK_CallTaskSignalProc( USIG16_TERMINATION, hTask );
1554
1555     /* UndocWin says to call int 0x21/0x4c exit=0xff here,
1556        but let's just call ExitThread */
1557     ExitThread(0xff);
1558 }
1559
1560
1561 /***********************************************************************
1562  *           GetAppCompatFlags   (KERNEL.354)
1563  */
1564 DWORD WINAPI GetAppCompatFlags16( HTASK16 hTask )
1565 {
1566     return GetAppCompatFlags( hTask );
1567 }
1568
1569
1570 /***********************************************************************
1571  *           GetAppCompatFlags   (USER32.@)
1572  */
1573 DWORD WINAPI GetAppCompatFlags( HTASK hTask )
1574 {
1575     TDB *pTask;
1576
1577     if (!hTask) hTask = GetCurrentTask();
1578     if (!(pTask=TASK_GetPtr( (HTASK16)hTask ))) return 0;
1579     if (GlobalSize16(hTask) < sizeof(TDB)) return 0;
1580     return pTask->compat_flags;
1581 }