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