Release 960114
[wine/gsoc-2012-control.git] / loader / task.c
blob5e45faf3decdcd1965135caa80da9881dc42fbf8
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 "message.h"
20 #include "miscemu.h"
21 #include "module.h"
22 #include "neexe.h"
23 #include "options.h"
24 #include "selectors.h"
25 #include "toolhelp.h"
26 #include "stddebug.h"
27 #include "debug.h"
28 #include "dde_proc.h"
30 /* Min. number of thunks allocated when creating a new segment */
31 #define MIN_THUNKS 32
33 /* 32-bit stack size for each task */
34 /* Must not be greater than 64k, or MAKE_SEGPTR won't work */
35 #define STACK32_SIZE 0x10000
37 /* ------ Internal variables ------ */
39 static HTASK hFirstTask = 0;
40 static HTASK hCurrentTask = 0;
41 static HTASK hTaskToKill = 0;
42 static HTASK hLockedTask = 0;
43 static WORD nTaskCount = 0;
44 static HANDLE hDOSEnvironment = 0;
46 /* ------ Internal declarations ------ */
48 /* TASK_Reschedule() 16-bit entry point */
49 static FARPROC TASK_RescheduleProc;
51 #ifdef WINELIB
52 #define TASK_SCHEDULE() TASK_Reschedule();
53 #else
54 #define TASK_SCHEDULE() CallTo16_word_(TASK_RescheduleProc,0)
55 #endif
57 static HANDLE TASK_CreateDOSEnvironment(void);
59 /***********************************************************************
60 * TASK_Init
62 BOOL TASK_Init(void)
64 TASK_RescheduleProc = (FARPROC)GetWndProcEntry16( "TASK_Reschedule" );
65 if (!(hDOSEnvironment = TASK_CreateDOSEnvironment()))
66 fprintf( stderr, "Not enough memory for DOS Environment\n" );
67 return (hDOSEnvironment != 0);
71 /***********************************************************************
72 * TASK_CreateDOSEnvironment
74 * Create the original DOS environment.
76 static HANDLE TASK_CreateDOSEnvironment(void)
78 static const char program_name[] = "KRNL386.EXE";
79 char **e, *p;
80 int initial_size, size, i, winpathlen, windirlen, sysdirlen;
81 HANDLE handle;
83 extern char **environ;
85 /* DOS environment format:
86 * ASCIIZ string 1
87 * ASCIIZ string 2
88 * ...
89 * ASCIIZ string n
90 * ASCIIZ PATH=xxx
91 * ASCIIZ windir=xxx
92 * BYTE 0
93 * WORD 1
94 * ASCIIZ program name (e.g. C:\WINDOWS\SYSTEM\KRNL386.EXE)
97 /* First compute the size of the fixed part of the environment */
99 for (i = winpathlen = 0; ; i++)
101 int len = DIR_GetDosPath( i, NULL, 0 );
102 if (!len) break;
103 winpathlen += len + 1;
105 if (!winpathlen) winpathlen = 1;
106 windirlen = GetWindowsDirectory( NULL, 0 ) + 1;
107 sysdirlen = GetSystemDirectory( NULL, 0 ) + 1;
108 initial_size = 5 + winpathlen + /* PATH=xxxx */
109 7 + windirlen + /* windir=xxxx */
110 1 + /* BYTE 0 at end */
111 sizeof(WORD) + /* WORD 1 */
112 sysdirlen + /* program directory */
113 strlen(program_name) + 1; /* program name */
115 /* Compute the total size of the Unix environment (except path) */
117 for (e = environ, size = initial_size; *e; e++)
119 if (lstrncmpi(*e, "path=", 5))
121 int len = strlen(*e) + 1;
122 if (size + len >= 32767)
124 fprintf( stderr, "Warning: environment larger than 32k.\n" );
125 break;
127 size += len;
132 /* Now allocate the environment */
134 if (!(handle = GlobalAlloc( GMEM_FIXED, size ))) return 0;
135 p = (char *)GlobalLock( handle );
137 /* And fill it with the Unix environment */
139 for (e = environ, size = initial_size; *e; e++)
141 if (lstrncmpi(*e, "path=", 5))
143 int len = strlen(*e) + 1;
144 if (size + len >= 32767) break;
145 strcpy( p, *e );
146 size += len;
147 p += len;
151 /* Now add the path and Windows directory */
153 strcpy( p, "PATH=" );
154 for (i = 0, p += 5; ; i++)
156 if (!DIR_GetDosPath( i, p, winpathlen )) break;
157 p += strlen(p);
158 *p++ = ';';
160 if (p[-1] == ';') p[-1] = '\0';
161 else p++;
163 strcpy( p, "windir=" );
164 GetWindowsDirectory( p + 7, windirlen );
165 p += 7 + windirlen;
167 /* Now add the program name */
169 *p++ = '\0';
170 *(WORD *)p = 1;
171 p += sizeof(WORD);
172 GetSystemDirectory( p, sysdirlen );
173 strcat( p, "\\" );
174 strcat( p, program_name );
176 /* Display it */
178 p = (char *) GlobalLock( handle );
179 dprintf_task(stddeb, "Master DOS environment at %p\n", p);
180 for (; *p; p += strlen(p) + 1) dprintf_task(stddeb, " %s\n", p);
181 dprintf_task( stddeb, "Progname: %s\n", p+3 );
183 return handle;
187 /***********************************************************************
188 * TASK_LinkTask
190 static void TASK_LinkTask( HTASK hTask )
192 HTASK *prevTask;
193 TDB *pTask;
195 if (!(pTask = (TDB *)GlobalLock( hTask ))) return;
196 prevTask = &hFirstTask;
197 while (*prevTask)
199 TDB *prevTaskPtr = (TDB *)GlobalLock( *prevTask );
200 if (prevTaskPtr->priority >= pTask->priority) break;
201 prevTask = &prevTaskPtr->hNext;
203 pTask->hNext = *prevTask;
204 *prevTask = hTask;
205 nTaskCount++;
209 /***********************************************************************
210 * TASK_UnlinkTask
212 static void TASK_UnlinkTask( HTASK hTask )
214 HTASK *prevTask;
215 TDB *pTask;
217 prevTask = &hFirstTask;
218 while (*prevTask && (*prevTask != hTask))
220 pTask = (TDB *)GlobalLock( *prevTask );
221 prevTask = &pTask->hNext;
223 if (*prevTask)
225 pTask = (TDB *)GlobalLock( *prevTask );
226 *prevTask = pTask->hNext;
227 pTask->hNext = 0;
228 nTaskCount--;
233 /***********************************************************************
234 * TASK_CreateThunks
236 * Create a thunk free-list in segment 'handle', starting from offset 'offset'
237 * and containing 'count' entries.
239 static void TASK_CreateThunks( HGLOBAL handle, WORD offset, WORD count )
241 int i;
242 WORD free;
243 THUNKS *pThunk;
245 pThunk = (THUNKS *)((BYTE *)GlobalLock( handle ) + offset);
246 pThunk->next = 0;
247 pThunk->magic = THUNK_MAGIC;
248 pThunk->free = (int)&pThunk->thunks - (int)pThunk;
249 free = pThunk->free;
250 for (i = 0; i < count-1; i++)
252 free += 8; /* Offset of next thunk */
253 pThunk->thunks[4*i] = free;
255 pThunk->thunks[4*i] = 0; /* Last thunk */
259 /***********************************************************************
260 * TASK_AllocThunk
262 * Allocate a thunk for MakeProcInstance().
264 #ifndef WINELIB32
265 static SEGPTR TASK_AllocThunk( HTASK hTask )
267 TDB *pTask;
268 THUNKS *pThunk;
269 WORD sel, base;
271 if (!(pTask = (TDB *)GlobalLock( hTask ))) return 0;
272 sel = pTask->hCSAlias;
273 pThunk = &pTask->thunks;
274 base = (int)pThunk - (int)pTask;
275 while (!pThunk->free)
277 sel = pThunk->next;
278 if (!sel) /* Allocate a new segment */
280 sel = GLOBAL_Alloc( GMEM_FIXED, sizeof(THUNKS) + (MIN_THUNKS-1)*8,
281 pTask->hPDB, TRUE, FALSE, FALSE );
282 if (!sel) return (SEGPTR)0;
283 TASK_CreateThunks( sel, 0, MIN_THUNKS );
284 pThunk->next = sel;
286 pThunk = (THUNKS *)GlobalLock( sel );
287 base = 0;
289 base += pThunk->free;
290 pThunk->free = *(WORD *)((BYTE *)pThunk + pThunk->free);
291 return MAKELONG( base, sel );
293 #endif
296 /***********************************************************************
297 * TASK_FreeThunk
299 * Free a MakeProcInstance() thunk.
301 #ifndef WINELIB32
302 static BOOL TASK_FreeThunk( HTASK hTask, SEGPTR thunk )
304 TDB *pTask;
305 THUNKS *pThunk;
306 WORD sel, base;
308 if (!(pTask = (TDB *)GlobalLock( hTask ))) return 0;
309 sel = pTask->hCSAlias;
310 pThunk = &pTask->thunks;
311 base = (int)pThunk - (int)pTask;
312 while (sel && (sel != HIWORD(thunk)))
314 sel = pThunk->next;
315 pThunk = (THUNKS *)GlobalLock( sel );
316 base = 0;
318 if (!sel) return FALSE;
319 *(WORD *)((BYTE *)pThunk + LOWORD(thunk) - base) = pThunk->free;
320 pThunk->free = LOWORD(thunk) - base;
321 return TRUE;
323 #endif
326 /***********************************************************************
327 * TASK_CallToStart
329 * 32-bit entry point for a new task. This function is responsible for
330 * setting up the registers and jumping to the 16-bit entry point.
332 #ifndef WINELIB
333 static void TASK_CallToStart(void)
335 int cs_reg, ds_reg, ip_reg;
336 TDB *pTask = (TDB *)GlobalLock( hCurrentTask );
337 NE_MODULE *pModule = (NE_MODULE *)GlobalLock( pTask->hModule );
338 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
340 /* Registers at initialization must be:
341 * ax zero
342 * bx stack size in bytes
343 * cx heap size in bytes
344 * si previous app instance
345 * di current app instance
346 * bp zero
347 * es selector to the PSP
348 * ds dgroup of the application
349 * ss stack selector
350 * sp top of the stack
353 cs_reg = pSegTable[pModule->cs - 1].selector;
354 ip_reg = pModule->ip;
355 ds_reg = pSegTable[pModule->dgroup - 1].selector;
357 IF1632_Saved16_ss = pTask->ss;
358 IF1632_Saved16_sp = pTask->sp;
359 dprintf_task( stddeb, "Starting main program: cs:ip=%04x:%04x ds=%04x ss:sp=%04x:%04x\n",
360 cs_reg, ip_reg, ds_reg,
361 IF1632_Saved16_ss, IF1632_Saved16_sp);
363 CallTo16_regs_( (FARPROC)(cs_reg << 16 | ip_reg), ds_reg,
364 pTask->hPDB /*es*/, 0 /*bp*/, 0 /*ax*/,
365 pModule->stack_size /*bx*/, pModule->heap_size /*cx*/,
366 0 /*dx*/, 0 /*si*/, ds_reg /*di*/ );
368 /* This should never return */
369 fprintf( stderr, "TASK_CallToStart: Main program returned!\n" );
370 TASK_KillCurrentTask( 1 );
372 #endif
375 /***********************************************************************
376 * TASK_CreateTask
378 HTASK TASK_CreateTask( HMODULE hModule, HANDLE hInstance, HANDLE hPrevInstance,
379 HANDLE hEnvironment, char *cmdLine, WORD cmdShow )
381 HTASK hTask;
382 TDB *pTask;
383 HANDLE hParentEnv;
384 NE_MODULE *pModule;
385 SEGTABLEENTRY *pSegTable;
386 LPSTR name;
387 char filename[256];
388 #ifndef WINELIB32
389 char *stack16Top, *stack32Top;
390 STACK16FRAME *frame16;
391 STACK32FRAME *frame32;
392 extern DWORD CALL16_RetAddr_word;
393 #endif
395 if (!(pModule = (NE_MODULE *)GlobalLock( hModule ))) return 0;
396 pSegTable = NE_SEG_TABLE( pModule );
398 /* Allocate the task structure */
400 hTask = GLOBAL_Alloc( GMEM_FIXED | GMEM_ZEROINIT, sizeof(TDB),
401 hModule, FALSE, FALSE, FALSE );
402 if (!hTask) return 0;
403 pTask = (TDB *)GlobalLock( hTask );
405 /* Allocate the new environment block */
407 if (!(hParentEnv = hEnvironment))
409 TDB *pParent = (TDB *)GlobalLock( hCurrentTask );
410 hParentEnv = pParent ? pParent->pdb.environment : hDOSEnvironment;
412 /* FIXME: do we really need to make a copy also when */
413 /* we don't use the parent environment? */
414 if (!(hEnvironment = GlobalAlloc( GMEM_FIXED, GlobalSize( hParentEnv ) )))
416 GlobalFree( hTask );
417 return 0;
419 memcpy( GlobalLock( hEnvironment ), GlobalLock( hParentEnv ),
420 GlobalSize( hParentEnv ) );
422 /* Get current directory */
424 GetModuleFileName( hModule, filename, sizeof(filename) );
425 name = strrchr(filename, '\\');
426 if (name) *(name+1) = 0;
428 /* Fill the task structure */
430 pTask->nEvents = 1; /* So the task can be started */
431 pTask->hSelf = hTask;
432 pTask->flags = 0;
433 pTask->version = pModule->expected_version;
434 pTask->hInstance = hInstance;
435 pTask->hPrevInstance = hPrevInstance;
436 pTask->hModule = hModule;
437 pTask->hParent = hCurrentTask;
438 #ifdef WINELIB
439 pTask->curdrive = 'C' - 'A' + 0x80;
440 strcpy( pTask->curdir, "\\" );
441 #else
442 pTask->curdrive = filename[0] - 'A' + 0x80;
443 strcpy( pTask->curdir, filename+2 );
444 #endif
445 pTask->magic = TDB_MAGIC;
446 pTask->nCmdShow = cmdShow;
448 /* Create the thunks block */
450 TASK_CreateThunks( hTask, (int)&pTask->thunks - (int)pTask, 7 );
452 /* Copy the module name */
454 name = MODULE_GetModuleName( hModule );
455 strncpy( pTask->module_name, name, sizeof(pTask->module_name) );
457 /* Allocate a selector for the PDB */
459 pTask->hPDB = GLOBAL_CreateBlock( GMEM_FIXED, &pTask->pdb, sizeof(PDB),
460 hModule, FALSE, FALSE, FALSE, NULL );
462 /* Fill the PDB */
464 pTask->pdb.int20 = 0x20cd;
465 #ifndef WINELIB
466 pTask->pdb.dispatcher[0] = 0x9a; /* ljmp */
467 *(DWORD *)&pTask->pdb.dispatcher[1] = MODULE_GetEntryPoint( GetModuleHandle("KERNEL"), 102 ); /* KERNEL.102 is DOS3Call() */
468 pTask->pdb.savedint22 = INT_GetHandler( 0x22 );
469 pTask->pdb.savedint23 = INT_GetHandler( 0x23 );
470 pTask->pdb.savedint24 = INT_GetHandler( 0x24 );
471 pTask->pdb.fileHandlesPtr = (SEGPTR)MAKELONG( 0x18,
472 GlobalHandleToSel(pTask->hPDB) );
473 #else
474 pTask->pdb.fileHandlesPtr = pTask->pdb.fileHandles;
475 #endif
476 memset( pTask->pdb.fileHandles, 0xff, sizeof(pTask->pdb.fileHandles) );
477 pTask->pdb.environment = hEnvironment;
478 pTask->pdb.nbFiles = 20;
479 lstrcpyn( pTask->pdb.cmdLine + 1, cmdLine, 127 );
480 pTask->pdb.cmdLine[0] = strlen( pTask->pdb.cmdLine + 1 );
482 /* Get the compatibility flags */
484 pTask->compat_flags = GetProfileInt( name, "Compatibility", 0 );
486 /* Allocate a code segment alias for the TDB */
488 pTask->hCSAlias = GLOBAL_CreateBlock( GMEM_FIXED, (void *)pTask,
489 sizeof(TDB), pTask->hPDB, TRUE,
490 FALSE, FALSE, NULL );
492 /* Set the owner of the environment block */
494 FarSetOwner( pTask->pdb.environment, pTask->hPDB );
496 /* Default DTA overwrites command-line */
498 pTask->dta = MAKELONG( (int)&pTask->pdb.cmdLine - (int)&pTask->pdb,
499 pTask->hPDB );
501 /* Allocate the 32-bit stack */
503 #ifndef WINELIB
504 pTask->hStack32 = GLOBAL_Alloc( GMEM_FIXED, STACK32_SIZE, pTask->hPDB,
505 FALSE, FALSE, FALSE );
507 /* Create the 32-bit stack frame */
509 *(DWORD *)GlobalLock(pTask->hStack32) = 0xDEADBEEF;
510 stack32Top = (char*)GlobalLock(pTask->hStack32) + STACK32_SIZE;
511 frame32 = (STACK32FRAME *)stack32Top - 1;
512 frame32->saved_esp = (DWORD)stack32Top;
513 frame32->edi = 0;
514 frame32->esi = 0;
515 frame32->edx = 0;
516 frame32->ecx = 0;
517 frame32->ebx = 0;
518 frame32->ebp = 0;
519 frame32->retaddr = (DWORD)TASK_CallToStart;
520 frame32->codeselector = WINE_CODE_SELECTOR;
521 pTask->esp = (DWORD)frame32;
523 /* Create the 16-bit stack frame */
525 pTask->ss = hInstance;
526 pTask->sp = ((pModule->sp != 0) ? pModule->sp :
527 pSegTable[pModule->ss-1].minsize + pModule->stack_size) & ~1;
528 stack16Top = (char *)PTR_SEG_OFF_TO_LIN( pTask->ss, pTask->sp );
529 frame16 = (STACK16FRAME *)stack16Top - 1;
530 frame16->saved_ss = 0; /*pTask->ss;*/
531 frame16->saved_sp = 0; /*pTask->sp;*/
532 frame16->ds = frame16->es = pTask->hInstance;
533 frame16->entry_point = 0;
534 frame16->ordinal_number = 24; /* WINPROCS.24 is TASK_Reschedule */
535 frame16->dll_id = 24; /* WINPROCS */
536 frame16->bp = 0;
537 frame16->ip = LOWORD( CALL16_RetAddr_word );
538 frame16->cs = HIWORD( CALL16_RetAddr_word );
539 pTask->sp -= sizeof(STACK16FRAME);
541 /* If there's no 16-bit stack yet, use a part of the new task stack */
542 /* This is only needed to have a stack to switch from on the first */
543 /* call to DirectedYield(). */
545 if (!IF1632_Saved16_ss)
547 IF1632_Saved16_ss = pTask->ss;
548 IF1632_Saved16_sp = pTask->sp;
551 /* Add a breakpoint at the start of the task */
553 if (Options.debug)
555 DBG_ADDR addr = { pSegTable[pModule->cs-1].selector, pModule->ip };
556 fprintf( stderr, "Task '%s': ", name );
557 DEBUG_AddBreakpoint( &addr );
559 #endif
561 /* Add the task to the linked list */
563 TASK_LinkTask( hTask );
565 dprintf_task( stddeb, "CreateTask: module='%s' cmdline='%s' task="NPFMT"\n",
566 name, cmdLine, hTask );
568 return hTask;
572 /***********************************************************************
573 * TASK_DeleteTask
575 static void TASK_DeleteTask( HTASK hTask )
577 TDB *pTask;
579 if (!(pTask = (TDB *)GlobalLock( hTask ))) return;
581 /* Close all open files of this task */
583 FILE_CloseAllFiles( pTask->hPDB );
585 /* Free the task module */
587 FreeModule( pTask->hModule );
589 /* Free all memory used by this task (including the 32-bit stack, */
590 /* the environment block and the thunk segments). */
592 GlobalFreeAll( pTask->hPDB );
594 /* Free message queue */
596 MSG_DeleteMsgQueue( pTask->hQueue );
598 /* Free the selector aliases */
600 GLOBAL_FreeBlock( pTask->hCSAlias );
601 GLOBAL_FreeBlock( pTask->hPDB );
603 /* Free the task structure itself */
605 GlobalFree( hTask );
609 /***********************************************************************
610 * TASK_KillCurrentTask
612 * Kill the currently running task. As it's not possible to kill the
613 * current task like this, it is simply marked for destruction, and will
614 * be killed when either TASK_Reschedule or this function is called again
615 * in the context of another task.
617 void TASK_KillCurrentTask( int exitCode )
619 if (hTaskToKill && (hTaskToKill != hCurrentTask))
621 /* If another task is already marked for destruction, */
622 /* we call kill it now, as we are in another context. */
623 TASK_DeleteTask( hTaskToKill );
626 if (nTaskCount <= 1)
628 dprintf_task( stddeb, "Killing the last task, exiting\n" );
629 exit(0);
632 /* Remove the task from the list to be sure we never switch back to it */
633 TASK_UnlinkTask( hCurrentTask );
635 hTaskToKill = hCurrentTask;
636 hLockedTask = 0;
638 Yield();
639 /* We should never return from this Yield() */
641 fprintf(stderr,"It's alive! Alive!!!\n");
642 exit(1);
646 /***********************************************************************
647 * TASK_Reschedule
649 * This is where all the magic of task-switching happens!
651 * This function should only be called via the TASK_SCHEDULE() macro, to make
652 * sure that all the context is saved correctly.
654 void TASK_Reschedule(void)
656 TDB *pOldTask = NULL, *pNewTask;
657 HTASK hTask = 0;
659 #ifdef CONFIG_IPC
660 dde_reschedule();
661 #endif
662 /* First check if there's a task to kill */
664 if (hTaskToKill && (hTaskToKill != hCurrentTask))
666 TASK_DeleteTask( hTaskToKill );
667 hTaskToKill = 0;
670 /* If current task is locked, simply return */
672 if (hLockedTask) return;
674 /* Find a task to yield to */
676 pOldTask = (TDB *)GlobalLock( hCurrentTask );
677 if (pOldTask && pOldTask->hYieldTo)
679 /* If a task is stored in hYieldTo of the current task (put there */
680 /* by DirectedYield), yield to it only if it has events pending. */
681 hTask = pOldTask->hYieldTo;
682 if (!(pNewTask = (TDB *)GlobalLock( hTask )) || !pNewTask->nEvents)
683 hTask = 0;
686 if (!hTask)
688 hTask = hFirstTask;
689 while (hTask)
691 pNewTask = (TDB *)GlobalLock( hTask );
692 if (pNewTask->nEvents && (hTask != hCurrentTask)) break;
693 hTask = pNewTask->hNext;
697 /* If there's a task to kill, switch to any other task, */
698 /* even if it doesn't have events pending. */
700 if (!hTask && hTaskToKill) hTask = hFirstTask;
702 if (!hTask) return; /* Do nothing */
704 pNewTask = (TDB *)GlobalLock( hTask );
705 dprintf_task( stddeb, "Switching to task "NPFMT" (%.8s)\n",
706 hTask, pNewTask->module_name );
708 /* Save the stacks of the previous task (if any) */
710 #ifndef WINELIB /* FIXME: JBP: IF1632 not allowed in libwine.a */
711 if (pOldTask)
713 pOldTask->ss = IF1632_Saved16_ss;
714 pOldTask->sp = IF1632_Saved16_sp;
715 pOldTask->esp = IF1632_Saved32_esp;
717 else IF1632_Original32_esp = IF1632_Saved32_esp;
718 #endif
720 /* Make the task the last in the linked list (round-robin scheduling) */
722 pNewTask->priority++;
723 TASK_UnlinkTask( hTask );
724 TASK_LinkTask( hTask );
725 pNewTask->priority--;
727 /* Switch to the new stack */
729 hCurrentTask = hTask;
730 #ifndef WINELIB /* FIXME: JBP: IF1632 not allowed in libwine.a */
731 IF1632_Saved16_ss = pNewTask->ss;
732 IF1632_Saved16_sp = pNewTask->sp;
733 IF1632_Saved32_esp = pNewTask->esp;
734 IF1632_Stack32_base = WIN16_GlobalLock( pNewTask->hStack32 );
735 #endif
739 /***********************************************************************
740 * InitTask (KERNEL.91)
742 #ifdef WINELIB
743 void InitTask(void)
744 #else
745 void InitTask( struct sigcontext_struct context )
746 #endif
748 static int firstTask = 1;
749 TDB *pTask;
750 NE_MODULE *pModule;
751 SEGTABLEENTRY *pSegTable;
752 INSTANCEDATA *pinstance;
753 LONG stacklow, stackhi;
755 #ifndef WINELIB
756 EAX_reg(&context) = 0;
757 #endif
758 if (!(pTask = (TDB *)GlobalLock( hCurrentTask ))) return;
759 if (!(pModule = (NE_MODULE *)GlobalLock( pTask->hModule ))) return;
761 if (firstTask)
763 extern BOOL WIDGETS_Init(void);
764 extern BOOL WIN_CreateDesktopWindow(void);
766 /* Perform global initialisations that need a task context */
768 /* Initialize built-in window classes */
769 if (!WIDGETS_Init()) return;
771 /* Create desktop window */
772 if (!WIN_CreateDesktopWindow()) return;
774 firstTask = 0;
777 #ifndef WINELIB
778 NE_InitializeDLLs( pTask->hModule );
780 /* Registers on return are:
781 * ax 1 if OK, 0 on error
782 * cx stack limit in bytes
783 * dx cmdShow parameter
784 * si instance handle of the previous instance
785 * di instance handle of the new task
786 * es:bx pointer to command-line inside PSP
788 EAX_reg(&context) = 1;
789 EBX_reg(&context) = 0x81;
790 ECX_reg(&context) = pModule->stack_size;
791 EDX_reg(&context) = pTask->nCmdShow;
792 ESI_reg(&context) = (DWORD)pTask->hPrevInstance;
793 EDI_reg(&context) = (DWORD)pTask->hInstance;
794 ES_reg (&context) = (WORD)pTask->hPDB;
795 #endif
797 /* Initialize the local heap */
798 if ( pModule->heap_size )
800 LocalInit( pTask->hInstance, 0, pModule->heap_size );
804 /* Initialize the INSTANCEDATA structure */
805 pSegTable = NE_SEG_TABLE( pModule );
806 stacklow = pSegTable[pModule->ss - 1].minsize;
807 stackhi = stacklow + pModule->stack_size;
808 if (stackhi > 0xffff) stackhi = 0xffff;
809 pinstance = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN(CURRENT_DS, 0);
810 pinstance->stackbottom = stackhi; /* yup, that's right. Confused me too. */
811 pinstance->stacktop = stacklow;
812 #ifndef WINELIB
813 pinstance->stackmin = IF1632_Saved16_sp;
814 #endif
818 /***********************************************************************
819 * WaitEvent (KERNEL.30)
821 BOOL WaitEvent( HTASK hTask )
823 TDB *pTask;
825 if (!hTask) hTask = hCurrentTask;
826 pTask = (TDB *)GlobalLock( hTask );
827 if (pTask->nEvents > 0)
829 pTask->nEvents--;
830 return FALSE;
832 TASK_SCHEDULE();
833 /* When we get back here, we have an event (or the task is the only one) */
834 if (pTask->nEvents > 0) pTask->nEvents--;
835 return TRUE;
839 /***********************************************************************
840 * PostEvent (KERNEL.31)
842 void PostEvent( HTASK hTask )
844 TDB *pTask;
846 if (!hTask) hTask = hCurrentTask;
847 if (!(pTask = (TDB *)GlobalLock( hTask ))) return;
848 pTask->nEvents++;
852 /***********************************************************************
853 * SetPriority (KERNEL.32)
855 void SetPriority( HTASK hTask, int delta )
857 TDB *pTask;
858 int newpriority;
860 if (!hTask) hTask = hCurrentTask;
861 if (!(pTask = (TDB *)GlobalLock( hTask ))) return;
862 newpriority = pTask->priority + delta;
863 if (newpriority < -32) newpriority = -32;
864 else if (newpriority > 15) newpriority = 15;
866 pTask->priority = newpriority + 1;
867 TASK_UnlinkTask( hTask );
868 TASK_LinkTask( hTask );
869 pTask->priority--;
873 /***********************************************************************
874 * LockCurrentTask (KERNEL.33)
876 HTASK LockCurrentTask( BOOL bLock )
878 if (bLock) hLockedTask = hCurrentTask;
879 else hLockedTask = 0;
880 return hLockedTask;
884 /***********************************************************************
885 * IsTaskLocked (KERNEL.122)
887 HTASK IsTaskLocked(void)
889 return hLockedTask;
893 /***********************************************************************
894 * OldYield (KERNEL.117)
896 void OldYield(void)
898 TDB *pCurTask;
900 pCurTask = (TDB *)GlobalLock( hCurrentTask );
901 if (pCurTask) pCurTask->nEvents++; /* Make sure we get back here */
902 TASK_SCHEDULE();
903 if (pCurTask) pCurTask->nEvents--;
907 /***********************************************************************
908 * DirectedYield (KERNEL.150)
910 void DirectedYield( HTASK hTask )
912 TDB *pCurTask;
914 if ((pCurTask = (TDB *)GlobalLock( hCurrentTask )) != NULL)
915 pCurTask->hYieldTo = hTask;
917 OldYield();
921 /***********************************************************************
922 * Yield (KERNEL.29)
924 void Yield(void)
926 DirectedYield( 0 );
930 /***********************************************************************
931 * MakeProcInstance (KERNEL.51)
933 FARPROC MakeProcInstance( FARPROC func, HANDLE hInstance )
935 #ifdef WINELIB32
936 return func; /* func can be called directly in Win32 */
937 #else
938 BYTE *thunk;
939 SEGPTR thunkaddr;
941 thunkaddr = TASK_AllocThunk( hCurrentTask );
942 if (!thunkaddr) return (FARPROC)0;
943 thunk = PTR_SEG_TO_LIN( thunkaddr );
945 dprintf_task( stddeb, "MakeProcInstance("SPFMT","NPFMT"): got thunk "SPFMT"\n",
946 (SEGPTR)func, hInstance, (SEGPTR)thunkaddr );
948 *thunk++ = 0xb8; /* movw instance, %ax */
949 *thunk++ = (BYTE)(hInstance & 0xff);
950 *thunk++ = (BYTE)(hInstance >> 8);
951 *thunk++ = 0xea; /* ljmp func */
952 *(DWORD *)thunk = (DWORD)func;
953 return (FARPROC)thunkaddr;
954 #endif
958 /***********************************************************************
959 * FreeProcInstance (KERNEL.52)
961 void FreeProcInstance( FARPROC func )
963 #ifndef WINELIB32
964 dprintf_task( stddeb, "FreeProcInstance("SPFMT")\n", (SEGPTR)func );
965 TASK_FreeThunk( hCurrentTask, (SEGPTR)func );
966 #endif
970 /**********************************************************************
971 * GetCodeHandle (KERNEL.93)
973 HANDLE GetCodeHandle( FARPROC proc )
975 HANDLE handle;
976 BYTE *thunk = (BYTE *)PTR_SEG_TO_LIN( proc );
978 /* Return the code segment containing 'proc'. */
979 /* Not sure if this is really correct (shouldn't matter that much). */
981 /* Check if it is really a thunk */
982 if ((thunk[0] == 0xb8) && (thunk[3] == 0xea))
983 handle = GlobalHandle( thunk[6] + (thunk[7] << 8) );
984 else
985 handle = GlobalHandle( HIWORD(proc) );
987 return handle;
991 /***********************************************************************
992 * SetTaskQueue (KERNEL.34)
994 HGLOBAL SetTaskQueue( HANDLE hTask, HGLOBAL hQueue )
996 HGLOBAL hPrev;
997 TDB *pTask;
999 if (!hTask) hTask = hCurrentTask;
1000 if (!(pTask = (TDB *)GlobalLock( hTask ))) return 0;
1001 hPrev = pTask->hQueue;
1002 pTask->hQueue = hQueue;
1003 return hPrev;
1007 /***********************************************************************
1008 * GetTaskQueue (KERNEL.35)
1010 HGLOBAL GetTaskQueue( HANDLE hTask )
1012 TDB *pTask;
1014 if (!hTask) hTask = hCurrentTask;
1015 if (!(pTask = (TDB *)GlobalLock( hTask ))) return 0;
1016 return pTask->hQueue;
1020 /***********************************************************************
1021 * GetTaskQueueDS (KERNEL.118)
1023 #ifndef WINELIB
1024 void GetTaskQueueDS( struct sigcontext_struct context )
1026 DS_reg(&context) = GlobalHandleToSel( GetTaskQueue(0) );
1028 #endif /* WINELIB */
1031 /***********************************************************************
1032 * GetTaskQueueES (KERNEL.119)
1034 #ifndef WINELIB
1035 void GetTaskQueueES( struct sigcontext_struct context )
1037 ES_reg(&context) = GlobalHandleToSel( GetTaskQueue(0) );
1039 #endif /* WINELIB */
1042 /***********************************************************************
1043 * GetCurrentTask (KERNEL.36)
1045 HTASK GetCurrentTask(void)
1047 /* Undocumented: first task is returned in high word */
1048 #ifdef WINELIB32
1049 return hCurrentTask;
1050 #else
1051 return MAKELONG( hCurrentTask, hFirstTask );
1052 #endif
1056 /***********************************************************************
1057 * GetCurrentPDB (KERNEL.37)
1059 HANDLE GetCurrentPDB(void)
1061 TDB *pTask;
1063 if (!(pTask = (TDB *)GlobalLock( hCurrentTask ))) return 0;
1064 return pTask->hPDB;
1068 /***********************************************************************
1069 * GetInstanceData (KERNEL.54)
1071 int GetInstanceData( HANDLE instance, WORD buffer, int len )
1073 char *ptr = (char *)GlobalLock( instance );
1074 if (!ptr || !len) return 0;
1075 if ((int)buffer + len >= 0x10000) len = 0x10000 - buffer;
1076 memcpy( ptr + buffer, (char *)GlobalLock( CURRENT_DS ) + buffer, len );
1077 return len;
1081 /***********************************************************************
1082 * SetErrorMode (KERNEL.107)
1084 UINT SetErrorMode( UINT mode )
1086 TDB *pTask;
1087 UINT oldMode;
1089 if (!(pTask = (TDB *)GlobalLock( hCurrentTask ))) return 0;
1090 oldMode = pTask->error_mode;
1091 pTask->error_mode = mode;
1092 return oldMode;
1096 /***********************************************************************
1097 * GetDOSEnvironment (KERNEL.131)
1099 SEGPTR GetDOSEnvironment(void)
1101 TDB *pTask;
1103 if (!(pTask = (TDB *)GlobalLock( hCurrentTask ))) return 0;
1104 return (SEGPTR)WIN16_GlobalLock( pTask->pdb.environment );
1108 /***********************************************************************
1109 * GetNumTasks (KERNEL.152)
1111 WORD GetNumTasks(void)
1113 return nTaskCount;
1117 /***********************************************************************
1118 * GetTaskDS (KERNEL.155)
1120 HINSTANCE GetTaskDS(void)
1122 TDB *pTask;
1124 if (!(pTask = (TDB *)GlobalLock( hCurrentTask ))) return 0;
1125 return pTask->hInstance;
1129 /***********************************************************************
1130 * IsTask (KERNEL.320)
1132 BOOL IsTask( HTASK hTask )
1134 TDB *pTask;
1136 if (!(pTask = (TDB *)GlobalLock( hTask ))) return FALSE;
1137 if (GlobalSize( hTask ) < sizeof(TDB)) return FALSE;
1138 return (pTask->magic == TDB_MAGIC);
1142 /***********************************************************************
1143 * GetExePtr (KERNEL.133)
1145 HMODULE GetExePtr( HANDLE handle )
1147 char *ptr;
1148 HTASK hTask;
1149 HANDLE owner;
1151 /* Check for module handle */
1153 if (!(ptr = GlobalLock( handle ))) return 0;
1154 if (((NE_MODULE *)ptr)->magic == NE_SIGNATURE) return handle;
1155 /* Fake modules describing PE modules have a PE signature */
1156 if (((NE_MODULE *)ptr)->magic == PE_SIGNATURE) return handle;
1158 /* Check the owner for module handle */
1160 #ifndef WINELIB
1161 owner = FarGetOwner( handle );
1162 #else
1163 owner = NULL;
1164 #endif
1165 if (!(ptr = GlobalLock( owner ))) return 0;
1166 if (((NE_MODULE *)ptr)->magic == NE_SIGNATURE) return owner;
1167 if (((NE_MODULE *)ptr)->magic == PE_SIGNATURE) return owner;
1169 /* Search for this handle and its owner inside all tasks */
1171 hTask = hFirstTask;
1172 while (hTask)
1174 TDB *pTask = (TDB *)GlobalLock( hTask );
1175 if ((hTask == handle) ||
1176 (pTask->hInstance == handle) ||
1177 (pTask->hQueue == handle) ||
1178 (pTask->hPDB == handle)) return pTask->hModule;
1179 if ((hTask == owner) ||
1180 (pTask->hInstance == owner) ||
1181 (pTask->hQueue == owner) ||
1182 (pTask->hPDB == owner)) return pTask->hModule;
1183 hTask = pTask->hNext;
1185 return 0;
1189 /***********************************************************************
1190 * TaskFirst (TOOLHELP.63)
1192 BOOL TaskFirst( TASKENTRY *lpte )
1194 lpte->hNext = hFirstTask;
1195 return TaskNext( lpte );
1199 /***********************************************************************
1200 * TaskNext (TOOLHELP.64)
1202 BOOL TaskNext( TASKENTRY *lpte )
1204 TDB *pTask;
1205 INSTANCEDATA *pInstData;
1207 dprintf_toolhelp( stddeb, "TaskNext(%p): task="NPFMT"\n", lpte, lpte->hNext );
1208 if (!lpte->hNext) return FALSE;
1209 pTask = (TDB *)GlobalLock( lpte->hNext );
1210 if (!pTask || pTask->magic != TDB_MAGIC) return FALSE;
1211 pInstData = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( pTask->hInstance, 0 );
1212 lpte->hTask = lpte->hNext;
1213 lpte->hTaskParent = pTask->hParent;
1214 lpte->hInst = pTask->hInstance;
1215 lpte->hModule = pTask->hModule;
1216 lpte->wSS = pTask->ss;
1217 lpte->wSP = pTask->sp;
1218 lpte->wStackTop = pInstData->stacktop;
1219 lpte->wStackMinimum = pInstData->stackmin;
1220 lpte->wStackBottom = pInstData->stackbottom;
1221 lpte->wcEvents = pTask->nEvents;
1222 lpte->hQueue = pTask->hQueue;
1223 strncpy( lpte->szModule, pTask->module_name, 8 );
1224 lpte->szModule[8] = '\0';
1225 lpte->wPSPOffset = 0x100; /*??*/
1226 lpte->hNext = pTask->hNext;
1227 return TRUE;
1231 /***********************************************************************
1232 * TaskFindHandle (TOOLHELP.65)
1234 BOOL TaskFindHandle( TASKENTRY *lpte, HTASK hTask )
1236 lpte->hNext = hTask;
1237 return TaskNext( lpte );