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