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