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