Release 960414
[wine.git] / loader / task.c
blob7ddac311bb476769c8cc1b66a60cbf09460d4a18
1 /*
2 * Task functions
4 * Copyright 1995 Alexandre Julliard
5 */
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include "windows.h"
11 #include "task.h"
12 #include "callback.h"
13 #include "directory.h"
14 #include "dos_fs.h"
15 #include "file.h"
16 #include "debugger.h"
17 #include "global.h"
18 #include "instance.h"
19 #include "miscemu.h"
20 #include "module.h"
21 #include "neexe.h"
22 #include "options.h"
23 #include "queue.h"
24 #include "toolhelp.h"
25 #include "stddebug.h"
26 #include "debug.h"
27 #include "dde_proc.h"
29 /* Min. number of thunks allocated when creating a new segment */
30 #define MIN_THUNKS 32
32 /* 32-bit stack size for each task */
33 /* Must not be greater than 64k, or MAKE_SEGPTR won't work */
34 #define STACK32_SIZE 0x10000
36 extern void TIMER_SwitchQueue(HQUEUE, HQUEUE );
37 extern void TIMER_NukeTimers(HWND, HQUEUE );
39 /* ------ Internal variables ------ */
41 static HTASK hFirstTask = 0;
42 static HTASK hCurrentTask = 0;
43 static HTASK hTaskToKill = 0;
44 static HTASK hLockedTask = 0;
45 static WORD nTaskCount = 0;
46 static HANDLE hDOSEnvironment = 0;
48 /* ------ Internal declarations ------ */
50 /* TASK_Reschedule() 16-bit entry point */
51 static FARPROC TASK_RescheduleProc;
53 #ifdef WINELIB
54 #define TASK_SCHEDULE() TASK_Reschedule();
55 #else
56 #define TASK_SCHEDULE() CallTo16_word_(TASK_RescheduleProc,0)
57 #endif
59 static HANDLE TASK_CreateDOSEnvironment(void);
61 /***********************************************************************
62 * TASK_Init
64 BOOL TASK_Init(void)
66 TASK_RescheduleProc = MODULE_GetWndProcEntry16( "TASK_Reschedule" );
67 if (!(hDOSEnvironment = TASK_CreateDOSEnvironment()))
68 fprintf( stderr, "Not enough memory for DOS Environment\n" );
69 return (hDOSEnvironment != 0);
73 /***********************************************************************
74 * TASK_CreateDOSEnvironment
76 * Create the original DOS environment.
78 static HANDLE TASK_CreateDOSEnvironment(void)
80 static const char program_name[] = "KRNL386.EXE";
81 char **e, *p;
82 int initial_size, size, i, winpathlen, sysdirlen;
83 HANDLE handle;
85 extern char **environ;
87 /* DOS environment format:
88 * ASCIIZ string 1
89 * ASCIIZ string 2
90 * ...
91 * ASCIIZ string n
92 * ASCIIZ PATH=xxx
93 * BYTE 0
94 * WORD 1
95 * ASCIIZ program name (e.g. C:\WINDOWS\SYSTEM\KRNL386.EXE)
98 /* First compute the size of the fixed part of the environment */
100 for (i = winpathlen = 0; ; i++)
102 int len = DIR_GetDosPath( i, NULL, 0 );
103 if (!len) break;
104 winpathlen += len + 1;
106 if (!winpathlen) winpathlen = 1;
107 sysdirlen = GetSystemDirectory( NULL, 0 ) + 1;
108 initial_size = 5 + winpathlen + /* PATH=xxxx */
109 1 + /* BYTE 0 at end */
110 sizeof(WORD) + /* WORD 1 */
111 sysdirlen + /* program directory */
112 strlen(program_name) + 1; /* program name */
114 /* Compute the total size of the Unix environment (except path) */
116 for (e = environ, size = initial_size; *e; e++)
118 if (lstrncmpi(*e, "path=", 5))
120 int len = strlen(*e) + 1;
121 if (size + len >= 32767)
123 fprintf( stderr, "Warning: environment larger than 32k.\n" );
124 break;
126 size += len;
131 /* Now allocate the environment */
133 if (!(handle = GlobalAlloc( GMEM_FIXED, size ))) return 0;
134 p = (char *)GlobalLock( handle );
136 /* And fill it with the Unix environment */
138 for (e = environ, size = initial_size; *e; e++)
140 if (lstrncmpi(*e, "path=", 5))
142 int len = strlen(*e) + 1;
143 if (size + len >= 32767) break;
144 strcpy( p, *e );
145 size += len;
146 p += len;
150 /* Now add the path */
152 strcpy( p, "PATH=" );
153 for (i = 0, p += 5; ; i++)
155 if (!DIR_GetDosPath( i, p, winpathlen )) break;
156 p += strlen(p);
157 *p++ = ';';
159 if (p[-1] == ';') p[-1] = '\0';
160 else p++;
162 /* Now add the program name */
164 *p++ = '\0';
165 PUT_WORD( p, 1 );
166 p += sizeof(WORD);
167 GetSystemDirectory( p, sysdirlen );
168 strcat( p, "\\" );
169 strcat( p, program_name );
171 /* Display it */
173 p = (char *) GlobalLock( handle );
174 dprintf_task(stddeb, "Master DOS environment at %p\n", p);
175 for (; *p; p += strlen(p) + 1) dprintf_task(stddeb, " %s\n", p);
176 dprintf_task( stddeb, "Progname: %s\n", p+3 );
178 return handle;
182 /***********************************************************************
183 * TASK_LinkTask
185 static void TASK_LinkTask( HTASK hTask )
187 HTASK *prevTask;
188 TDB *pTask;
190 if (!(pTask = (TDB *)GlobalLock( hTask ))) return;
191 prevTask = &hFirstTask;
192 while (*prevTask)
194 TDB *prevTaskPtr = (TDB *)GlobalLock( *prevTask );
195 if (prevTaskPtr->priority >= pTask->priority) break;
196 prevTask = &prevTaskPtr->hNext;
198 pTask->hNext = *prevTask;
199 *prevTask = hTask;
200 nTaskCount++;
204 /***********************************************************************
205 * TASK_UnlinkTask
207 static void TASK_UnlinkTask( HTASK hTask )
209 HTASK *prevTask;
210 TDB *pTask;
212 prevTask = &hFirstTask;
213 while (*prevTask && (*prevTask != hTask))
215 pTask = (TDB *)GlobalLock( *prevTask );
216 prevTask = &pTask->hNext;
218 if (*prevTask)
220 pTask = (TDB *)GlobalLock( *prevTask );
221 *prevTask = pTask->hNext;
222 pTask->hNext = 0;
223 nTaskCount--;
228 /***********************************************************************
229 * TASK_CreateThunks
231 * Create a thunk free-list in segment 'handle', starting from offset 'offset'
232 * and containing 'count' entries.
234 static void TASK_CreateThunks( HGLOBAL handle, WORD offset, WORD count )
236 int i;
237 WORD free;
238 THUNKS *pThunk;
240 pThunk = (THUNKS *)((BYTE *)GlobalLock( handle ) + offset);
241 pThunk->next = 0;
242 pThunk->magic = THUNK_MAGIC;
243 pThunk->free = (int)&pThunk->thunks - (int)pThunk;
244 free = pThunk->free;
245 for (i = 0; i < count-1; i++)
247 free += 8; /* Offset of next thunk */
248 pThunk->thunks[4*i] = free;
250 pThunk->thunks[4*i] = 0; /* Last thunk */
254 /***********************************************************************
255 * TASK_AllocThunk
257 * Allocate a thunk for MakeProcInstance().
259 #ifndef WINELIB32
260 static SEGPTR TASK_AllocThunk( HTASK hTask )
262 TDB *pTask;
263 THUNKS *pThunk;
264 WORD sel, base;
266 if (!(pTask = (TDB *)GlobalLock( hTask ))) return 0;
267 sel = pTask->hCSAlias;
268 pThunk = &pTask->thunks;
269 base = (int)pThunk - (int)pTask;
270 while (!pThunk->free)
272 sel = pThunk->next;
273 if (!sel) /* Allocate a new segment */
275 sel = GLOBAL_Alloc( GMEM_FIXED, sizeof(THUNKS) + (MIN_THUNKS-1)*8,
276 pTask->hPDB, TRUE, FALSE, FALSE );
277 if (!sel) return (SEGPTR)0;
278 TASK_CreateThunks( sel, 0, MIN_THUNKS );
279 pThunk->next = sel;
281 pThunk = (THUNKS *)GlobalLock( sel );
282 base = 0;
284 base += pThunk->free;
285 pThunk->free = *(WORD *)((BYTE *)pThunk + pThunk->free);
286 return MAKELONG( base, sel );
288 #endif
291 /***********************************************************************
292 * TASK_FreeThunk
294 * Free a MakeProcInstance() thunk.
296 #ifndef WINELIB32
297 static BOOL TASK_FreeThunk( HTASK hTask, SEGPTR thunk )
299 TDB *pTask;
300 THUNKS *pThunk;
301 WORD sel, base;
303 if (!(pTask = (TDB *)GlobalLock( hTask ))) return 0;
304 sel = pTask->hCSAlias;
305 pThunk = &pTask->thunks;
306 base = (int)pThunk - (int)pTask;
307 while (sel && (sel != HIWORD(thunk)))
309 sel = pThunk->next;
310 pThunk = (THUNKS *)GlobalLock( sel );
311 base = 0;
313 if (!sel) return FALSE;
314 *(WORD *)((BYTE *)pThunk + LOWORD(thunk) - base) = pThunk->free;
315 pThunk->free = LOWORD(thunk) - base;
316 return TRUE;
318 #endif
321 /***********************************************************************
322 * TASK_CallToStart
324 * 32-bit entry point for a new task. This function is responsible for
325 * setting up the registers and jumping to the 16-bit entry point.
327 #ifndef WINELIB
328 static void TASK_CallToStart(void)
330 int cs_reg, ds_reg, ip_reg;
331 TDB *pTask = (TDB *)GlobalLock( hCurrentTask );
332 NE_MODULE *pModule = MODULE_GetPtr( pTask->hModule );
333 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
335 /* Registers at initialization must be:
336 * ax zero
337 * bx stack size in bytes
338 * cx heap size in bytes
339 * si previous app instance
340 * di current app instance
341 * bp zero
342 * es selector to the PSP
343 * ds dgroup of the application
344 * ss stack selector
345 * sp top of the stack
348 cs_reg = pSegTable[pModule->cs - 1].selector;
349 ip_reg = pModule->ip;
350 ds_reg = pSegTable[pModule->dgroup - 1].selector;
352 IF1632_Saved16_ss = pTask->ss;
353 IF1632_Saved16_sp = pTask->sp;
354 dprintf_task( stddeb, "Starting main program: cs:ip=%04x:%04x ds=%04x ss:sp=%04x:%04x\n",
355 cs_reg, ip_reg, ds_reg,
356 IF1632_Saved16_ss, IF1632_Saved16_sp);
358 CallTo16_regs_( (FARPROC)(cs_reg << 16 | ip_reg), ds_reg,
359 pTask->hPDB /*es*/, 0 /*bp*/, 0 /*ax*/,
360 pModule->stack_size /*bx*/, pModule->heap_size /*cx*/,
361 0 /*dx*/, 0 /*si*/, ds_reg /*di*/ );
363 /* This should never return */
364 fprintf( stderr, "TASK_CallToStart: Main program returned!\n" );
365 TASK_KillCurrentTask( 1 );
367 #endif
370 /***********************************************************************
371 * TASK_CreateTask
373 HTASK TASK_CreateTask( HMODULE hModule, HANDLE hInstance, HANDLE hPrevInstance,
374 HANDLE hEnvironment, char *cmdLine, WORD cmdShow )
376 HTASK hTask;
377 TDB *pTask;
378 HANDLE hParentEnv;
379 NE_MODULE *pModule;
380 SEGTABLEENTRY *pSegTable;
381 LPSTR name;
382 char filename[256];
383 #ifndef WINELIB32
384 char *stack16Top, *stack32Top;
385 STACK16FRAME *frame16;
386 STACK32FRAME *frame32;
387 extern DWORD CALLTO16_RetAddr_word;
388 #endif
390 if (!(pModule = MODULE_GetPtr( hModule ))) return 0;
391 pSegTable = NE_SEG_TABLE( pModule );
393 /* Allocate the task structure */
395 hTask = GLOBAL_Alloc( GMEM_FIXED | GMEM_ZEROINIT, sizeof(TDB),
396 hModule, FALSE, FALSE, FALSE );
397 if (!hTask) return 0;
398 pTask = (TDB *)GlobalLock( hTask );
400 /* Allocate the new environment block */
402 if (!(hParentEnv = hEnvironment))
404 TDB *pParent = (TDB *)GlobalLock( hCurrentTask );
405 hParentEnv = pParent ? pParent->pdb.environment : hDOSEnvironment;
407 /* FIXME: do we really need to make a copy also when */
408 /* we don't use the parent environment? */
409 if (!(hEnvironment = GlobalAlloc( GMEM_FIXED, GlobalSize( hParentEnv ) )))
411 GlobalFree( hTask );
412 return 0;
414 memcpy( GlobalLock( hEnvironment ), GlobalLock( hParentEnv ),
415 GlobalSize( hParentEnv ) );
417 /* Get current directory */
419 GetModuleFileName( hModule, filename, sizeof(filename) );
420 name = strrchr(filename, '\\');
421 if (name) *(name+1) = 0;
423 /* Fill the task structure */
425 pTask->nEvents = 1; /* So the task can be started */
426 pTask->hSelf = hTask;
427 pTask->flags = 0;
428 pTask->version = pModule->expected_version;
429 pTask->hInstance = hInstance;
430 pTask->hPrevInstance = hPrevInstance;
431 pTask->hModule = hModule;
432 pTask->hParent = hCurrentTask;
433 #ifdef WINELIB
434 pTask->curdrive = 'C' - 'A' + 0x80;
435 strcpy( pTask->curdir, "\\" );
436 #else
437 pTask->curdrive = filename[0] - 'A' + 0x80;
438 strcpy( pTask->curdir, filename+2 );
439 #endif
440 pTask->magic = TDB_MAGIC;
441 pTask->nCmdShow = cmdShow;
443 /* Create the thunks block */
445 TASK_CreateThunks( hTask, (int)&pTask->thunks - (int)pTask, 7 );
447 /* Copy the module name */
449 name = MODULE_GetModuleName( hModule );
450 strncpy( pTask->module_name, name, sizeof(pTask->module_name) );
452 /* Allocate a selector for the PDB */
454 pTask->hPDB = GLOBAL_CreateBlock( GMEM_FIXED, &pTask->pdb, sizeof(PDB),
455 hModule, FALSE, FALSE, FALSE, NULL );
457 /* Fill the PDB */
459 pTask->pdb.int20 = 0x20cd;
460 #ifndef WINELIB
461 pTask->pdb.dispatcher[0] = 0x9a; /* ljmp */
462 *(DWORD *)&pTask->pdb.dispatcher[1] = MODULE_GetEntryPoint( GetModuleHandle("KERNEL"), 102 ); /* KERNEL.102 is DOS3Call() */
463 pTask->pdb.savedint22 = INT_GetHandler( 0x22 );
464 pTask->pdb.savedint23 = INT_GetHandler( 0x23 );
465 pTask->pdb.savedint24 = INT_GetHandler( 0x24 );
466 pTask->pdb.fileHandlesPtr = (SEGPTR)MAKELONG( 0x18,
467 GlobalHandleToSel(pTask->hPDB) );
468 #else
469 pTask->pdb.fileHandlesPtr = pTask->pdb.fileHandles;
470 #endif
471 memset( pTask->pdb.fileHandles, 0xff, sizeof(pTask->pdb.fileHandles) );
472 pTask->pdb.environment = hEnvironment;
473 pTask->pdb.nbFiles = 20;
474 lstrcpyn( pTask->pdb.cmdLine + 1, cmdLine, 127 );
475 pTask->pdb.cmdLine[0] = strlen( pTask->pdb.cmdLine + 1 );
477 /* Get the compatibility flags */
479 pTask->compat_flags = GetProfileInt( name, "Compatibility", 0 );
481 /* Allocate a code segment alias for the TDB */
483 pTask->hCSAlias = GLOBAL_CreateBlock( GMEM_FIXED, (void *)pTask,
484 sizeof(TDB), pTask->hPDB, TRUE,
485 FALSE, FALSE, NULL );
487 /* Set the owner of the environment block */
489 FarSetOwner( pTask->pdb.environment, pTask->hPDB );
491 /* Default DTA overwrites command-line */
493 pTask->dta = MAKELONG( (int)&pTask->pdb.cmdLine - (int)&pTask->pdb,
494 pTask->hPDB );
496 /* Allocate the 32-bit stack */
498 #ifndef WINELIB
499 pTask->hStack32 = GLOBAL_Alloc( GMEM_FIXED, STACK32_SIZE, pTask->hPDB,
500 FALSE, FALSE, FALSE );
502 /* Create the 32-bit stack frame */
504 *(DWORD *)GlobalLock(pTask->hStack32) = 0xDEADBEEF;
505 stack32Top = (char*)GlobalLock(pTask->hStack32) + STACK32_SIZE;
506 frame32 = (STACK32FRAME *)stack32Top - 1;
507 frame32->saved_esp = (DWORD)stack32Top;
508 frame32->edi = 0;
509 frame32->esi = 0;
510 frame32->edx = 0;
511 frame32->ecx = 0;
512 frame32->ebx = 0;
513 frame32->ebp = 0;
514 frame32->retaddr = (DWORD)TASK_CallToStart;
515 frame32->codeselector = WINE_CODE_SELECTOR;
516 pTask->esp = (DWORD)frame32;
518 /* Create the 16-bit stack frame */
520 pTask->ss = hInstance;
521 pTask->sp = ((pModule->sp != 0) ? pModule->sp :
522 pSegTable[pModule->ss-1].minsize + pModule->stack_size) & ~1;
523 stack16Top = (char *)PTR_SEG_OFF_TO_LIN( pTask->ss, pTask->sp );
524 frame16 = (STACK16FRAME *)stack16Top - 1;
525 frame16->saved_ss = 0; /*pTask->ss;*/
526 frame16->saved_sp = 0; /*pTask->sp;*/
527 frame16->ds = frame16->es = pTask->hInstance;
528 frame16->entry_point = 0;
529 frame16->ordinal_number = 24; /* WINPROCS.24 is TASK_Reschedule */
530 frame16->dll_id = 24; /* WINPROCS */
531 frame16->bp = 0;
532 frame16->ip = LOWORD( CALLTO16_RetAddr_word );
533 frame16->cs = HIWORD( CALLTO16_RetAddr_word );
534 pTask->sp -= sizeof(STACK16FRAME);
536 /* If there's no 16-bit stack yet, use a part of the new task stack */
537 /* This is only needed to have a stack to switch from on the first */
538 /* call to DirectedYield(). */
540 if (!IF1632_Saved16_ss)
542 IF1632_Saved16_ss = pTask->ss;
543 IF1632_Saved16_sp = pTask->sp;
546 /* Add a breakpoint at the start of the task */
548 if (Options.debug)
550 DBG_ADDR addr = { pSegTable[pModule->cs-1].selector, pModule->ip };
551 fprintf( stderr, "Task '%s': ", name );
552 DEBUG_AddBreakpoint( &addr );
554 #endif
556 /* Add the task to the linked list */
558 TASK_LinkTask( hTask );
560 dprintf_task( stddeb, "CreateTask: module='%s' cmdline='%s' task=%04x\n",
561 name, cmdLine, hTask );
563 return hTask;
567 /***********************************************************************
568 * TASK_DeleteTask
570 static void TASK_DeleteTask( HTASK hTask )
572 TDB *pTask;
573 HANDLE hPDB;
575 if (!(pTask = (TDB *)GlobalLock( hTask ))) return;
576 hPDB = pTask->hPDB;
578 /* Free the task module */
580 FreeModule( pTask->hModule );
582 /* Close all open files of this task */
584 FILE_CloseAllFiles( pTask->hPDB );
586 /* Nuke timers */
588 TIMER_NukeTimers( 0, pTask->hQueue );
590 /* Free the message queue */
592 QUEUE_DeleteMsgQueue( pTask->hQueue );
594 /* Free the selector aliases */
596 GLOBAL_FreeBlock( pTask->hCSAlias );
597 GLOBAL_FreeBlock( pTask->hPDB );
599 /* Free the task structure itself */
601 GlobalFree( hTask );
603 /* Free all memory used by this task (including the 32-bit stack, */
604 /* the environment block and the thunk segments). */
606 GlobalFreeAll( hPDB );
610 /***********************************************************************
611 * TASK_KillCurrentTask
613 * Kill the currently running task. As it's not possible to kill the
614 * current task like this, it is simply marked for destruction, and will
615 * be killed when either TASK_Reschedule or this function is called again
616 * in the context of another task.
618 void TASK_KillCurrentTask( int exitCode )
620 extern void EXEC_ExitWindows( int retCode );
622 if (hTaskToKill && (hTaskToKill != hCurrentTask))
624 /* If another task is already marked for destruction, */
625 /* we call kill it now, as we are in another context. */
626 TASK_DeleteTask( hTaskToKill );
629 if (nTaskCount <= 1)
631 dprintf_task( stddeb, "Killing the last task, exiting\n" );
632 EXEC_ExitWindows( 0 );
635 /* Remove the task from the list to be sure we never switch back to it */
636 TASK_UnlinkTask( hCurrentTask );
638 hTaskToKill = hCurrentTask;
639 hLockedTask = 0;
641 Yield();
642 /* We should never return from this Yield() */
644 fprintf(stderr,"It's alive! Alive!!!\n");
645 exit(1);
649 /***********************************************************************
650 * TASK_Reschedule
652 * This is where all the magic of task-switching happens!
654 * This function should only be called via the TASK_SCHEDULE() macro, to make
655 * sure that all the context is saved correctly.
657 void TASK_Reschedule(void)
659 TDB *pOldTask = NULL, *pNewTask;
660 HTASK hTask = 0;
662 #ifdef CONFIG_IPC
663 dde_reschedule();
664 #endif
665 /* First check if there's a task to kill */
667 if (hTaskToKill && (hTaskToKill != hCurrentTask))
669 TASK_DeleteTask( hTaskToKill );
670 hTaskToKill = 0;
673 /* If current task is locked, simply return */
675 if (hLockedTask) return;
677 /* Find a task to yield to */
679 pOldTask = (TDB *)GlobalLock( hCurrentTask );
680 if (pOldTask && pOldTask->hYieldTo)
682 /* If a task is stored in hYieldTo of the current task (put there */
683 /* by DirectedYield), yield to it only if it has events pending. */
684 hTask = pOldTask->hYieldTo;
685 if (!(pNewTask = (TDB *)GlobalLock( hTask )) || !pNewTask->nEvents)
686 hTask = 0;
689 if (!hTask)
691 hTask = hFirstTask;
692 while (hTask)
694 pNewTask = (TDB *)GlobalLock( hTask );
695 if (pNewTask->nEvents && (hTask != hCurrentTask)) break;
696 hTask = pNewTask->hNext;
700 /* If there's a task to kill, switch to any other task, */
701 /* even if it doesn't have events pending. */
703 if (!hTask && hTaskToKill) hTask = hFirstTask;
705 if (!hTask) return; /* Do nothing */
707 pNewTask = (TDB *)GlobalLock( hTask );
708 dprintf_task( stddeb, "Switching to task %04x (%.8s)\n",
709 hTask, pNewTask->module_name );
711 /* Save the stacks of the previous task (if any) */
713 #ifndef WINELIB /* FIXME: JBP: IF1632 not allowed in libwine.a */
714 if (pOldTask)
716 pOldTask->ss = IF1632_Saved16_ss;
717 pOldTask->sp = IF1632_Saved16_sp;
718 pOldTask->esp = IF1632_Saved32_esp;
720 else IF1632_Original32_esp = IF1632_Saved32_esp;
721 #endif
723 /* Make the task the last in the linked list (round-robin scheduling) */
725 pNewTask->priority++;
726 TASK_UnlinkTask( hTask );
727 TASK_LinkTask( hTask );
728 pNewTask->priority--;
730 /* Switch to the new stack */
732 hCurrentTask = hTask;
733 #ifndef WINELIB /* FIXME: JBP: IF1632 not allowed in libwine.a */
734 IF1632_Saved16_ss = pNewTask->ss;
735 IF1632_Saved16_sp = pNewTask->sp;
736 IF1632_Saved32_esp = pNewTask->esp;
737 IF1632_Stack32_base = WIN16_GlobalLock( pNewTask->hStack32 );
738 #endif
742 /***********************************************************************
743 * InitTask (KERNEL.91)
745 #ifdef WINELIB
746 void InitTask(void)
747 #else
748 void InitTask( struct sigcontext_struct context )
749 #endif
751 static int firstTask = 1;
752 TDB *pTask;
753 NE_MODULE *pModule;
754 SEGTABLEENTRY *pSegTable;
755 INSTANCEDATA *pinstance;
756 LONG stacklow, stackhi;
758 #ifndef WINELIB
759 EAX_reg(&context) = 0;
760 #endif
761 if (!(pTask = (TDB *)GlobalLock( hCurrentTask ))) return;
762 if (!(pModule = MODULE_GetPtr( pTask->hModule ))) return;
764 if (firstTask)
766 extern BOOL WIDGETS_Init(void);
767 extern BOOL WIN_CreateDesktopWindow(void);
769 /* Perform global initialisations that need a task context */
771 /* Initialize built-in window classes */
772 if (!WIDGETS_Init()) return;
774 /* Create desktop window */
775 if (!WIN_CreateDesktopWindow()) return;
777 firstTask = 0;
780 #ifndef WINELIB
781 NE_InitializeDLLs( pTask->hModule );
783 /* Registers on return are:
784 * ax 1 if OK, 0 on error
785 * cx stack limit in bytes
786 * dx cmdShow parameter
787 * si instance handle of the previous instance
788 * di instance handle of the new task
789 * es:bx pointer to command-line inside PSP
791 EAX_reg(&context) = 1;
792 EBX_reg(&context) = 0x81;
793 ECX_reg(&context) = pModule->stack_size;
794 EDX_reg(&context) = pTask->nCmdShow;
795 ESI_reg(&context) = (DWORD)pTask->hPrevInstance;
796 EDI_reg(&context) = (DWORD)pTask->hInstance;
797 ES_reg (&context) = (WORD)pTask->hPDB;
799 /* Initialize the local heap */
800 if ( pModule->heap_size )
802 LocalInit( pTask->hInstance, 0, pModule->heap_size );
804 #endif
807 /* Initialize the INSTANCEDATA structure */
808 pSegTable = NE_SEG_TABLE( pModule );
809 stacklow = pSegTable[pModule->ss - 1].minsize;
810 stackhi = stacklow + pModule->stack_size;
811 if (stackhi > 0xffff) stackhi = 0xffff;
812 pinstance = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN(CURRENT_DS, 0);
813 pinstance->stackbottom = stackhi; /* yup, that's right. Confused me too. */
814 pinstance->stacktop = stacklow;
815 #ifndef WINELIB
816 pinstance->stackmin = IF1632_Saved16_sp;
817 #endif
821 /***********************************************************************
822 * WaitEvent (KERNEL.30)
824 BOOL WaitEvent( HTASK hTask )
826 TDB *pTask;
828 if (!hTask) hTask = hCurrentTask;
829 pTask = (TDB *)GlobalLock( hTask );
830 if (pTask->nEvents > 0)
832 pTask->nEvents--;
833 return FALSE;
835 TASK_SCHEDULE();
836 /* When we get back here, we have an event (or the task is the only one) */
837 if (pTask->nEvents > 0) pTask->nEvents--;
838 return TRUE;
842 /***********************************************************************
843 * PostEvent (KERNEL.31)
845 void PostEvent( HTASK hTask )
847 TDB *pTask;
849 if (!hTask) hTask = hCurrentTask;
850 if (!(pTask = (TDB *)GlobalLock( hTask ))) return;
851 pTask->nEvents++;
855 /***********************************************************************
856 * SetPriority (KERNEL.32)
858 void SetPriority( HTASK hTask, int delta )
860 TDB *pTask;
861 int newpriority;
863 if (!hTask) hTask = hCurrentTask;
864 if (!(pTask = (TDB *)GlobalLock( hTask ))) return;
865 newpriority = pTask->priority + delta;
866 if (newpriority < -32) newpriority = -32;
867 else if (newpriority > 15) newpriority = 15;
869 pTask->priority = newpriority + 1;
870 TASK_UnlinkTask( hTask );
871 TASK_LinkTask( hTask );
872 pTask->priority--;
876 /***********************************************************************
877 * LockCurrentTask (KERNEL.33)
879 HTASK LockCurrentTask( BOOL bLock )
881 if (bLock) hLockedTask = hCurrentTask;
882 else hLockedTask = 0;
883 return hLockedTask;
887 /***********************************************************************
888 * IsTaskLocked (KERNEL.122)
890 HTASK IsTaskLocked(void)
892 return hLockedTask;
896 /***********************************************************************
897 * OldYield (KERNEL.117)
899 void OldYield(void)
901 TDB *pCurTask;
903 pCurTask = (TDB *)GlobalLock( hCurrentTask );
904 if (pCurTask) pCurTask->nEvents++; /* Make sure we get back here */
905 TASK_SCHEDULE();
906 if (pCurTask) pCurTask->nEvents--;
910 /***********************************************************************
911 * DirectedYield (KERNEL.150)
913 void DirectedYield( HTASK hTask )
915 TDB *pCurTask;
917 if ((pCurTask = (TDB *)GlobalLock( hCurrentTask )) != NULL)
918 pCurTask->hYieldTo = hTask;
920 OldYield();
924 /***********************************************************************
925 * Yield (KERNEL.29)
927 void Yield(void)
929 DirectedYield( 0 );
933 /***********************************************************************
934 * MakeProcInstance (KERNEL.51)
936 FARPROC MakeProcInstance( FARPROC func, HANDLE hInstance )
938 #ifdef WINELIB32
939 return func; /* func can be called directly in Win32 */
940 #else
941 BYTE *thunk;
942 SEGPTR thunkaddr;
944 thunkaddr = TASK_AllocThunk( hCurrentTask );
945 if (!thunkaddr) return (FARPROC)0;
946 thunk = PTR_SEG_TO_LIN( thunkaddr );
948 dprintf_task( stddeb, "MakeProcInstance(%08lx,%04x): got thunk %08lx\n",
949 (DWORD)func, hInstance, (DWORD)thunkaddr );
951 *thunk++ = 0xb8; /* movw instance, %ax */
952 *thunk++ = (BYTE)(hInstance & 0xff);
953 *thunk++ = (BYTE)(hInstance >> 8);
954 *thunk++ = 0xea; /* ljmp func */
955 *(DWORD *)thunk = (DWORD)func;
956 return (FARPROC)thunkaddr;
957 #endif
961 /***********************************************************************
962 * FreeProcInstance (KERNEL.52)
964 void FreeProcInstance( FARPROC func )
966 #ifndef WINELIB32
967 dprintf_task( stddeb, "FreeProcInstance(%08lx)\n", (DWORD)func );
968 TASK_FreeThunk( hCurrentTask, (SEGPTR)func );
969 #endif
973 /**********************************************************************
974 * GetCodeHandle (KERNEL.93)
976 HANDLE GetCodeHandle( FARPROC proc )
978 #ifndef WINELIB32
979 HANDLE handle;
980 BYTE *thunk = (BYTE *)PTR_SEG_TO_LIN( proc );
982 /* Return the code segment containing 'proc'. */
983 /* Not sure if this is really correct (shouldn't matter that much). */
985 /* Check if it is really a thunk */
986 if ((thunk[0] == 0xb8) && (thunk[3] == 0xea))
987 handle = GlobalHandle( thunk[6] + (thunk[7] << 8) );
988 else
989 handle = GlobalHandle( HIWORD(proc) );
991 return handle;
992 #else
993 return (HANDLE)proc;
994 #endif
998 /***********************************************************************
999 * SetTaskQueue (KERNEL.34)
1001 HQUEUE SetTaskQueue( HANDLE hTask, HQUEUE hQueue )
1003 HQUEUE hPrev;
1004 TDB *pTask;
1006 if (!hTask) hTask = hCurrentTask;
1007 if (!(pTask = (TDB *)GlobalLock( hTask ))) return 0;
1009 hPrev = pTask->hQueue;
1010 pTask->hQueue = hQueue;
1012 TIMER_SwitchQueue( hPrev, hQueue );
1014 return hPrev;
1018 /***********************************************************************
1019 * GetTaskQueue (KERNEL.35)
1021 HQUEUE GetTaskQueue( HANDLE hTask )
1023 TDB *pTask;
1025 if (!hTask) hTask = hCurrentTask;
1026 if (!(pTask = (TDB *)GlobalLock( hTask ))) return 0;
1027 return pTask->hQueue;
1031 /***********************************************************************
1032 * GetTaskQueueDS (KERNEL.118)
1034 #ifndef WINELIB
1035 void GetTaskQueueDS( struct sigcontext_struct context )
1037 DS_reg(&context) = GlobalHandleToSel( GetTaskQueue(0) );
1039 #endif /* WINELIB */
1042 /***********************************************************************
1043 * GetTaskQueueES (KERNEL.119)
1045 #ifndef WINELIB
1046 void GetTaskQueueES( struct sigcontext_struct context )
1048 ES_reg(&context) = GlobalHandleToSel( GetTaskQueue(0) );
1050 #endif /* WINELIB */
1053 /***********************************************************************
1054 * GetCurrentTask (KERNEL.36)
1056 HTASK GetCurrentTask(void)
1058 /* Undocumented: first task is returned in high word */
1059 #ifdef WINELIB32
1060 return hCurrentTask;
1061 #else
1062 return MAKELONG( hCurrentTask, hFirstTask );
1063 #endif
1067 /***********************************************************************
1068 * GetCurrentPDB (KERNEL.37)
1070 HANDLE GetCurrentPDB(void)
1072 TDB *pTask;
1074 if (!(pTask = (TDB *)GlobalLock( hCurrentTask ))) return 0;
1075 return pTask->hPDB;
1079 /***********************************************************************
1080 * GetInstanceData (KERNEL.54)
1082 int GetInstanceData( HANDLE instance, WORD buffer, int len )
1084 char *ptr = (char *)GlobalLock( instance );
1085 if (!ptr || !len) return 0;
1086 if ((int)buffer + len >= 0x10000) len = 0x10000 - buffer;
1087 memcpy( ptr + buffer, (char *)GlobalLock( CURRENT_DS ) + buffer, len );
1088 return len;
1092 /***********************************************************************
1093 * SetErrorMode (KERNEL.107)
1095 UINT SetErrorMode( UINT mode )
1097 TDB *pTask;
1098 UINT oldMode;
1100 if (!(pTask = (TDB *)GlobalLock( hCurrentTask ))) return 0;
1101 oldMode = pTask->error_mode;
1102 pTask->error_mode = mode;
1103 return oldMode;
1107 /***********************************************************************
1108 * GetDOSEnvironment (KERNEL.131)
1110 SEGPTR GetDOSEnvironment(void)
1112 TDB *pTask;
1114 if (!(pTask = (TDB *)GlobalLock( hCurrentTask ))) return 0;
1115 return (SEGPTR)WIN16_GlobalLock( pTask->pdb.environment );
1119 /***********************************************************************
1120 * GetNumTasks (KERNEL.152)
1122 WORD GetNumTasks(void)
1124 return nTaskCount;
1128 /***********************************************************************
1129 * GetTaskDS (KERNEL.155)
1131 HINSTANCE GetTaskDS(void)
1133 TDB *pTask;
1135 if (!(pTask = (TDB *)GlobalLock( hCurrentTask ))) return 0;
1136 return pTask->hInstance;
1140 /***********************************************************************
1141 * IsTask (KERNEL.320)
1143 BOOL IsTask( HTASK hTask )
1145 TDB *pTask;
1147 if (!(pTask = (TDB *)GlobalLock( hTask ))) return FALSE;
1148 if (GlobalSize( hTask ) < sizeof(TDB)) return FALSE;
1149 return (pTask->magic == TDB_MAGIC);
1153 /***********************************************************************
1154 * GetExePtr (KERNEL.133)
1156 HMODULE GetExePtr( HANDLE handle )
1158 char *ptr;
1159 HTASK hTask;
1160 HANDLE owner;
1162 /* Check for module handle */
1164 if (!(ptr = GlobalLock( handle ))) return 0;
1165 if (((NE_MODULE *)ptr)->magic == NE_SIGNATURE) return handle;
1167 /* Check the owner for module handle */
1169 #ifndef WINELIB
1170 owner = FarGetOwner( handle );
1171 #else
1172 owner = NULL;
1173 #endif
1174 if (!(ptr = GlobalLock( owner ))) return 0;
1175 if (((NE_MODULE *)ptr)->magic == NE_SIGNATURE) return owner;
1177 /* Search for this handle and its owner inside all tasks */
1179 hTask = hFirstTask;
1180 while (hTask)
1182 TDB *pTask = (TDB *)GlobalLock( hTask );
1183 if ((hTask == handle) ||
1184 (pTask->hInstance == handle) ||
1185 (pTask->hQueue == handle) ||
1186 (pTask->hPDB == handle)) return pTask->hModule;
1187 if ((hTask == owner) ||
1188 (pTask->hInstance == owner) ||
1189 (pTask->hQueue == owner) ||
1190 (pTask->hPDB == owner)) return pTask->hModule;
1191 hTask = pTask->hNext;
1193 return 0;
1197 /***********************************************************************
1198 * TaskFirst (TOOLHELP.63)
1200 BOOL TaskFirst( TASKENTRY *lpte )
1202 lpte->hNext = hFirstTask;
1203 return TaskNext( lpte );
1207 /***********************************************************************
1208 * TaskNext (TOOLHELP.64)
1210 BOOL TaskNext( TASKENTRY *lpte )
1212 TDB *pTask;
1213 INSTANCEDATA *pInstData;
1215 dprintf_toolhelp( stddeb, "TaskNext(%p): task=%04x\n", lpte, lpte->hNext );
1216 if (!lpte->hNext) return FALSE;
1217 pTask = (TDB *)GlobalLock( lpte->hNext );
1218 if (!pTask || pTask->magic != TDB_MAGIC) return FALSE;
1219 pInstData = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( pTask->hInstance, 0 );
1220 lpte->hTask = lpte->hNext;
1221 lpte->hTaskParent = pTask->hParent;
1222 lpte->hInst = pTask->hInstance;
1223 lpte->hModule = pTask->hModule;
1224 lpte->wSS = pTask->ss;
1225 lpte->wSP = pTask->sp;
1226 lpte->wStackTop = pInstData->stacktop;
1227 lpte->wStackMinimum = pInstData->stackmin;
1228 lpte->wStackBottom = pInstData->stackbottom;
1229 lpte->wcEvents = pTask->nEvents;
1230 lpte->hQueue = pTask->hQueue;
1231 strncpy( lpte->szModule, pTask->module_name, 8 );
1232 lpte->szModule[8] = '\0';
1233 lpte->wPSPOffset = 0x100; /*??*/
1234 lpte->hNext = pTask->hNext;
1235 return TRUE;
1239 /***********************************************************************
1240 * TaskFindHandle (TOOLHELP.65)
1242 BOOL TaskFindHandle( TASKENTRY *lpte, HTASK hTask )
1244 lpte->hNext = hTask;
1245 return TaskNext( lpte );