Release 960225
[wine.git] / loader / task.c
blob08d730c04d562538218736367ea8b052817c1f14
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, 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 * BYTE 0
92 * WORD 1
93 * ASCIIZ program name (e.g. C:\WINDOWS\SYSTEM\KRNL386.EXE)
96 /* First compute the size of the fixed part of the environment */
98 for (i = winpathlen = 0; ; i++)
100 int len = DIR_GetDosPath( i, NULL, 0 );
101 if (!len) break;
102 winpathlen += len + 1;
104 if (!winpathlen) winpathlen = 1;
105 sysdirlen = GetSystemDirectory( NULL, 0 ) + 1;
106 initial_size = 5 + winpathlen + /* PATH=xxxx */
107 1 + /* BYTE 0 at end */
108 sizeof(WORD) + /* WORD 1 */
109 sysdirlen + /* program directory */
110 strlen(program_name) + 1; /* program name */
112 /* Compute the total size of the Unix environment (except path) */
114 for (e = environ, size = initial_size; *e; e++)
116 if (lstrncmpi(*e, "path=", 5))
118 int len = strlen(*e) + 1;
119 if (size + len >= 32767)
121 fprintf( stderr, "Warning: environment larger than 32k.\n" );
122 break;
124 size += len;
129 /* Now allocate the environment */
131 if (!(handle = GlobalAlloc( GMEM_FIXED, size ))) return 0;
132 p = (char *)GlobalLock( handle );
134 /* And fill it with the Unix environment */
136 for (e = environ, size = initial_size; *e; e++)
138 if (lstrncmpi(*e, "path=", 5))
140 int len = strlen(*e) + 1;
141 if (size + len >= 32767) break;
142 strcpy( p, *e );
143 size += len;
144 p += len;
148 /* Now add the path */
150 strcpy( p, "PATH=" );
151 for (i = 0, p += 5; ; i++)
153 if (!DIR_GetDosPath( i, p, winpathlen )) break;
154 p += strlen(p);
155 *p++ = ';';
157 if (p[-1] == ';') p[-1] = '\0';
158 else p++;
160 /* Now add the program name */
162 *p++ = '\0';
163 *(WORD *)p = 1;
164 p += sizeof(WORD);
165 GetSystemDirectory( p, sysdirlen );
166 strcat( p, "\\" );
167 strcat( p, program_name );
169 /* Display it */
171 p = (char *) GlobalLock( handle );
172 dprintf_task(stddeb, "Master DOS environment at %p\n", p);
173 for (; *p; p += strlen(p) + 1) dprintf_task(stddeb, " %s\n", p);
174 dprintf_task( stddeb, "Progname: %s\n", p+3 );
176 return handle;
180 /***********************************************************************
181 * TASK_LinkTask
183 static void TASK_LinkTask( HTASK hTask )
185 HTASK *prevTask;
186 TDB *pTask;
188 if (!(pTask = (TDB *)GlobalLock( hTask ))) return;
189 prevTask = &hFirstTask;
190 while (*prevTask)
192 TDB *prevTaskPtr = (TDB *)GlobalLock( *prevTask );
193 if (prevTaskPtr->priority >= pTask->priority) break;
194 prevTask = &prevTaskPtr->hNext;
196 pTask->hNext = *prevTask;
197 *prevTask = hTask;
198 nTaskCount++;
202 /***********************************************************************
203 * TASK_UnlinkTask
205 static void TASK_UnlinkTask( HTASK hTask )
207 HTASK *prevTask;
208 TDB *pTask;
210 prevTask = &hFirstTask;
211 while (*prevTask && (*prevTask != hTask))
213 pTask = (TDB *)GlobalLock( *prevTask );
214 prevTask = &pTask->hNext;
216 if (*prevTask)
218 pTask = (TDB *)GlobalLock( *prevTask );
219 *prevTask = pTask->hNext;
220 pTask->hNext = 0;
221 nTaskCount--;
226 /***********************************************************************
227 * TASK_CreateThunks
229 * Create a thunk free-list in segment 'handle', starting from offset 'offset'
230 * and containing 'count' entries.
232 static void TASK_CreateThunks( HGLOBAL handle, WORD offset, WORD count )
234 int i;
235 WORD free;
236 THUNKS *pThunk;
238 pThunk = (THUNKS *)((BYTE *)GlobalLock( handle ) + offset);
239 pThunk->next = 0;
240 pThunk->magic = THUNK_MAGIC;
241 pThunk->free = (int)&pThunk->thunks - (int)pThunk;
242 free = pThunk->free;
243 for (i = 0; i < count-1; i++)
245 free += 8; /* Offset of next thunk */
246 pThunk->thunks[4*i] = free;
248 pThunk->thunks[4*i] = 0; /* Last thunk */
252 /***********************************************************************
253 * TASK_AllocThunk
255 * Allocate a thunk for MakeProcInstance().
257 #ifndef WINELIB32
258 static SEGPTR TASK_AllocThunk( HTASK hTask )
260 TDB *pTask;
261 THUNKS *pThunk;
262 WORD sel, base;
264 if (!(pTask = (TDB *)GlobalLock( hTask ))) return 0;
265 sel = pTask->hCSAlias;
266 pThunk = &pTask->thunks;
267 base = (int)pThunk - (int)pTask;
268 while (!pThunk->free)
270 sel = pThunk->next;
271 if (!sel) /* Allocate a new segment */
273 sel = GLOBAL_Alloc( GMEM_FIXED, sizeof(THUNKS) + (MIN_THUNKS-1)*8,
274 pTask->hPDB, TRUE, FALSE, FALSE );
275 if (!sel) return (SEGPTR)0;
276 TASK_CreateThunks( sel, 0, MIN_THUNKS );
277 pThunk->next = sel;
279 pThunk = (THUNKS *)GlobalLock( sel );
280 base = 0;
282 base += pThunk->free;
283 pThunk->free = *(WORD *)((BYTE *)pThunk + pThunk->free);
284 return MAKELONG( base, sel );
286 #endif
289 /***********************************************************************
290 * TASK_FreeThunk
292 * Free a MakeProcInstance() thunk.
294 #ifndef WINELIB32
295 static BOOL TASK_FreeThunk( HTASK hTask, SEGPTR thunk )
297 TDB *pTask;
298 THUNKS *pThunk;
299 WORD sel, base;
301 if (!(pTask = (TDB *)GlobalLock( hTask ))) return 0;
302 sel = pTask->hCSAlias;
303 pThunk = &pTask->thunks;
304 base = (int)pThunk - (int)pTask;
305 while (sel && (sel != HIWORD(thunk)))
307 sel = pThunk->next;
308 pThunk = (THUNKS *)GlobalLock( sel );
309 base = 0;
311 if (!sel) return FALSE;
312 *(WORD *)((BYTE *)pThunk + LOWORD(thunk) - base) = pThunk->free;
313 pThunk->free = LOWORD(thunk) - base;
314 return TRUE;
316 #endif
319 /***********************************************************************
320 * TASK_CallToStart
322 * 32-bit entry point for a new task. This function is responsible for
323 * setting up the registers and jumping to the 16-bit entry point.
325 #ifndef WINELIB
326 static void TASK_CallToStart(void)
328 int cs_reg, ds_reg, ip_reg;
329 TDB *pTask = (TDB *)GlobalLock( hCurrentTask );
330 NE_MODULE *pModule = (NE_MODULE *)GlobalLock( pTask->hModule );
331 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
333 /* Registers at initialization must be:
334 * ax zero
335 * bx stack size in bytes
336 * cx heap size in bytes
337 * si previous app instance
338 * di current app instance
339 * bp zero
340 * es selector to the PSP
341 * ds dgroup of the application
342 * ss stack selector
343 * sp top of the stack
346 cs_reg = pSegTable[pModule->cs - 1].selector;
347 ip_reg = pModule->ip;
348 ds_reg = pSegTable[pModule->dgroup - 1].selector;
350 IF1632_Saved16_ss = pTask->ss;
351 IF1632_Saved16_sp = pTask->sp;
352 dprintf_task( stddeb, "Starting main program: cs:ip=%04x:%04x ds=%04x ss:sp=%04x:%04x\n",
353 cs_reg, ip_reg, ds_reg,
354 IF1632_Saved16_ss, IF1632_Saved16_sp);
356 CallTo16_regs_( (FARPROC)(cs_reg << 16 | ip_reg), ds_reg,
357 pTask->hPDB /*es*/, 0 /*bp*/, 0 /*ax*/,
358 pModule->stack_size /*bx*/, pModule->heap_size /*cx*/,
359 0 /*dx*/, 0 /*si*/, ds_reg /*di*/ );
361 /* This should never return */
362 fprintf( stderr, "TASK_CallToStart: Main program returned!\n" );
363 TASK_KillCurrentTask( 1 );
365 #endif
368 /***********************************************************************
369 * TASK_CreateTask
371 HTASK TASK_CreateTask( HMODULE hModule, HANDLE hInstance, HANDLE hPrevInstance,
372 HANDLE hEnvironment, char *cmdLine, WORD cmdShow )
374 HTASK hTask;
375 TDB *pTask;
376 HANDLE hParentEnv;
377 NE_MODULE *pModule;
378 SEGTABLEENTRY *pSegTable;
379 LPSTR name;
380 char filename[256];
381 #ifndef WINELIB32
382 char *stack16Top, *stack32Top;
383 STACK16FRAME *frame16;
384 STACK32FRAME *frame32;
385 extern DWORD CALL16_RetAddr_word;
386 #endif
388 if (!(pModule = (NE_MODULE *)GlobalLock( hModule ))) return 0;
389 pSegTable = NE_SEG_TABLE( pModule );
391 /* Allocate the task structure */
393 hTask = GLOBAL_Alloc( GMEM_FIXED | GMEM_ZEROINIT, sizeof(TDB),
394 hModule, FALSE, FALSE, FALSE );
395 if (!hTask) return 0;
396 pTask = (TDB *)GlobalLock( hTask );
398 /* Allocate the new environment block */
400 if (!(hParentEnv = hEnvironment))
402 TDB *pParent = (TDB *)GlobalLock( hCurrentTask );
403 hParentEnv = pParent ? pParent->pdb.environment : hDOSEnvironment;
405 /* FIXME: do we really need to make a copy also when */
406 /* we don't use the parent environment? */
407 if (!(hEnvironment = GlobalAlloc( GMEM_FIXED, GlobalSize( hParentEnv ) )))
409 GlobalFree( hTask );
410 return 0;
412 memcpy( GlobalLock( hEnvironment ), GlobalLock( hParentEnv ),
413 GlobalSize( hParentEnv ) );
415 /* Get current directory */
417 GetModuleFileName( hModule, filename, sizeof(filename) );
418 name = strrchr(filename, '\\');
419 if (name) *(name+1) = 0;
421 /* Fill the task structure */
423 pTask->nEvents = 1; /* So the task can be started */
424 pTask->hSelf = hTask;
425 pTask->flags = 0;
426 pTask->version = pModule->expected_version;
427 pTask->hInstance = hInstance;
428 pTask->hPrevInstance = hPrevInstance;
429 pTask->hModule = hModule;
430 pTask->hParent = hCurrentTask;
431 #ifdef WINELIB
432 pTask->curdrive = 'C' - 'A' + 0x80;
433 strcpy( pTask->curdir, "\\" );
434 #else
435 pTask->curdrive = filename[0] - 'A' + 0x80;
436 strcpy( pTask->curdir, filename+2 );
437 #endif
438 pTask->magic = TDB_MAGIC;
439 pTask->nCmdShow = cmdShow;
441 /* Create the thunks block */
443 TASK_CreateThunks( hTask, (int)&pTask->thunks - (int)pTask, 7 );
445 /* Copy the module name */
447 name = MODULE_GetModuleName( hModule );
448 strncpy( pTask->module_name, name, sizeof(pTask->module_name) );
450 /* Allocate a selector for the PDB */
452 pTask->hPDB = GLOBAL_CreateBlock( GMEM_FIXED, &pTask->pdb, sizeof(PDB),
453 hModule, FALSE, FALSE, FALSE, NULL );
455 /* Fill the PDB */
457 pTask->pdb.int20 = 0x20cd;
458 #ifndef WINELIB
459 pTask->pdb.dispatcher[0] = 0x9a; /* ljmp */
460 *(DWORD *)&pTask->pdb.dispatcher[1] = MODULE_GetEntryPoint( GetModuleHandle("KERNEL"), 102 ); /* KERNEL.102 is DOS3Call() */
461 pTask->pdb.savedint22 = INT_GetHandler( 0x22 );
462 pTask->pdb.savedint23 = INT_GetHandler( 0x23 );
463 pTask->pdb.savedint24 = INT_GetHandler( 0x24 );
464 pTask->pdb.fileHandlesPtr = (SEGPTR)MAKELONG( 0x18,
465 GlobalHandleToSel(pTask->hPDB) );
466 #else
467 pTask->pdb.fileHandlesPtr = pTask->pdb.fileHandles;
468 #endif
469 memset( pTask->pdb.fileHandles, 0xff, sizeof(pTask->pdb.fileHandles) );
470 pTask->pdb.environment = hEnvironment;
471 pTask->pdb.nbFiles = 20;
472 lstrcpyn( pTask->pdb.cmdLine + 1, cmdLine, 127 );
473 pTask->pdb.cmdLine[0] = strlen( pTask->pdb.cmdLine + 1 );
475 /* Get the compatibility flags */
477 pTask->compat_flags = GetProfileInt( name, "Compatibility", 0 );
479 /* Allocate a code segment alias for the TDB */
481 pTask->hCSAlias = GLOBAL_CreateBlock( GMEM_FIXED, (void *)pTask,
482 sizeof(TDB), pTask->hPDB, TRUE,
483 FALSE, FALSE, NULL );
485 /* Set the owner of the environment block */
487 FarSetOwner( pTask->pdb.environment, pTask->hPDB );
489 /* Default DTA overwrites command-line */
491 pTask->dta = MAKELONG( (int)&pTask->pdb.cmdLine - (int)&pTask->pdb,
492 pTask->hPDB );
494 /* Allocate the 32-bit stack */
496 #ifndef WINELIB
497 pTask->hStack32 = GLOBAL_Alloc( GMEM_FIXED, STACK32_SIZE, pTask->hPDB,
498 FALSE, FALSE, FALSE );
500 /* Create the 32-bit stack frame */
502 *(DWORD *)GlobalLock(pTask->hStack32) = 0xDEADBEEF;
503 stack32Top = (char*)GlobalLock(pTask->hStack32) + STACK32_SIZE;
504 frame32 = (STACK32FRAME *)stack32Top - 1;
505 frame32->saved_esp = (DWORD)stack32Top;
506 frame32->edi = 0;
507 frame32->esi = 0;
508 frame32->edx = 0;
509 frame32->ecx = 0;
510 frame32->ebx = 0;
511 frame32->ebp = 0;
512 frame32->retaddr = (DWORD)TASK_CallToStart;
513 frame32->codeselector = WINE_CODE_SELECTOR;
514 pTask->esp = (DWORD)frame32;
516 /* Create the 16-bit stack frame */
518 pTask->ss = hInstance;
519 pTask->sp = ((pModule->sp != 0) ? pModule->sp :
520 pSegTable[pModule->ss-1].minsize + pModule->stack_size) & ~1;
521 stack16Top = (char *)PTR_SEG_OFF_TO_LIN( pTask->ss, pTask->sp );
522 frame16 = (STACK16FRAME *)stack16Top - 1;
523 frame16->saved_ss = 0; /*pTask->ss;*/
524 frame16->saved_sp = 0; /*pTask->sp;*/
525 frame16->ds = frame16->es = pTask->hInstance;
526 frame16->entry_point = 0;
527 frame16->ordinal_number = 24; /* WINPROCS.24 is TASK_Reschedule */
528 frame16->dll_id = 24; /* WINPROCS */
529 frame16->bp = 0;
530 frame16->ip = LOWORD( CALL16_RetAddr_word );
531 frame16->cs = HIWORD( CALL16_RetAddr_word );
532 pTask->sp -= sizeof(STACK16FRAME);
534 /* If there's no 16-bit stack yet, use a part of the new task stack */
535 /* This is only needed to have a stack to switch from on the first */
536 /* call to DirectedYield(). */
538 if (!IF1632_Saved16_ss)
540 IF1632_Saved16_ss = pTask->ss;
541 IF1632_Saved16_sp = pTask->sp;
544 /* Add a breakpoint at the start of the task */
546 if (Options.debug)
548 DBG_ADDR addr = { pSegTable[pModule->cs-1].selector, pModule->ip };
549 fprintf( stderr, "Task '%s': ", name );
550 DEBUG_AddBreakpoint( &addr );
552 #endif
554 /* Add the task to the linked list */
556 TASK_LinkTask( hTask );
558 dprintf_task( stddeb, "CreateTask: module='%s' cmdline='%s' task="NPFMT"\n",
559 name, cmdLine, hTask );
561 return hTask;
565 /***********************************************************************
566 * TASK_DeleteTask
568 static void TASK_DeleteTask( HTASK hTask )
570 TDB *pTask;
571 HANDLE hPDB;
573 if (!(pTask = (TDB *)GlobalLock( hTask ))) return;
574 hPDB = pTask->hPDB;
576 /* Free the task module */
578 FreeModule( pTask->hModule );
580 /* Close all open files of this task */
582 FILE_CloseAllFiles( pTask->hPDB );
584 /* Free the message queue */
586 MSG_DeleteMsgQueue( pTask->hQueue );
588 /* Free the selector aliases */
590 GLOBAL_FreeBlock( pTask->hCSAlias );
591 GLOBAL_FreeBlock( pTask->hPDB );
593 /* Free the task structure itself */
595 GlobalFree( hTask );
597 /* Free all memory used by this task (including the 32-bit stack, */
598 /* the environment block and the thunk segments). */
600 GlobalFreeAll( hPDB );
604 /***********************************************************************
605 * TASK_KillCurrentTask
607 * Kill the currently running task. As it's not possible to kill the
608 * current task like this, it is simply marked for destruction, and will
609 * be killed when either TASK_Reschedule or this function is called again
610 * in the context of another task.
612 void TASK_KillCurrentTask( int exitCode )
614 if (hTaskToKill && (hTaskToKill != hCurrentTask))
616 /* If another task is already marked for destruction, */
617 /* we call kill it now, as we are in another context. */
618 TASK_DeleteTask( hTaskToKill );
621 if (nTaskCount <= 1)
623 dprintf_task( stddeb, "Killing the last task, exiting\n" );
624 ExitWindows( 0, 0 );
627 /* Remove the task from the list to be sure we never switch back to it */
628 TASK_UnlinkTask( hCurrentTask );
630 hTaskToKill = hCurrentTask;
631 hLockedTask = 0;
633 Yield();
634 /* We should never return from this Yield() */
636 fprintf(stderr,"It's alive! Alive!!!\n");
637 exit(1);
641 /***********************************************************************
642 * TASK_Reschedule
644 * This is where all the magic of task-switching happens!
646 * This function should only be called via the TASK_SCHEDULE() macro, to make
647 * sure that all the context is saved correctly.
649 void TASK_Reschedule(void)
651 TDB *pOldTask = NULL, *pNewTask;
652 HTASK hTask = 0;
654 #ifdef CONFIG_IPC
655 dde_reschedule();
656 #endif
657 /* First check if there's a task to kill */
659 if (hTaskToKill && (hTaskToKill != hCurrentTask))
661 TASK_DeleteTask( hTaskToKill );
662 hTaskToKill = 0;
665 /* If current task is locked, simply return */
667 if (hLockedTask) return;
669 /* Find a task to yield to */
671 pOldTask = (TDB *)GlobalLock( hCurrentTask );
672 if (pOldTask && pOldTask->hYieldTo)
674 /* If a task is stored in hYieldTo of the current task (put there */
675 /* by DirectedYield), yield to it only if it has events pending. */
676 hTask = pOldTask->hYieldTo;
677 if (!(pNewTask = (TDB *)GlobalLock( hTask )) || !pNewTask->nEvents)
678 hTask = 0;
681 if (!hTask)
683 hTask = hFirstTask;
684 while (hTask)
686 pNewTask = (TDB *)GlobalLock( hTask );
687 if (pNewTask->nEvents && (hTask != hCurrentTask)) break;
688 hTask = pNewTask->hNext;
692 /* If there's a task to kill, switch to any other task, */
693 /* even if it doesn't have events pending. */
695 if (!hTask && hTaskToKill) hTask = hFirstTask;
697 if (!hTask) return; /* Do nothing */
699 pNewTask = (TDB *)GlobalLock( hTask );
700 dprintf_task( stddeb, "Switching to task "NPFMT" (%.8s)\n",
701 hTask, pNewTask->module_name );
703 /* Save the stacks of the previous task (if any) */
705 #ifndef WINELIB /* FIXME: JBP: IF1632 not allowed in libwine.a */
706 if (pOldTask)
708 pOldTask->ss = IF1632_Saved16_ss;
709 pOldTask->sp = IF1632_Saved16_sp;
710 pOldTask->esp = IF1632_Saved32_esp;
712 else IF1632_Original32_esp = IF1632_Saved32_esp;
713 #endif
715 /* Make the task the last in the linked list (round-robin scheduling) */
717 pNewTask->priority++;
718 TASK_UnlinkTask( hTask );
719 TASK_LinkTask( hTask );
720 pNewTask->priority--;
722 /* Switch to the new stack */
724 hCurrentTask = hTask;
725 #ifndef WINELIB /* FIXME: JBP: IF1632 not allowed in libwine.a */
726 IF1632_Saved16_ss = pNewTask->ss;
727 IF1632_Saved16_sp = pNewTask->sp;
728 IF1632_Saved32_esp = pNewTask->esp;
729 IF1632_Stack32_base = WIN16_GlobalLock( pNewTask->hStack32 );
730 #endif
734 /***********************************************************************
735 * InitTask (KERNEL.91)
737 #ifdef WINELIB
738 void InitTask(void)
739 #else
740 void InitTask( struct sigcontext_struct context )
741 #endif
743 static int firstTask = 1;
744 TDB *pTask;
745 NE_MODULE *pModule;
746 SEGTABLEENTRY *pSegTable;
747 INSTANCEDATA *pinstance;
748 LONG stacklow, stackhi;
750 #ifndef WINELIB
751 EAX_reg(&context) = 0;
752 #endif
753 if (!(pTask = (TDB *)GlobalLock( hCurrentTask ))) return;
754 if (!(pModule = (NE_MODULE *)GlobalLock( pTask->hModule ))) return;
756 if (firstTask)
758 extern BOOL WIDGETS_Init(void);
759 extern BOOL WIN_CreateDesktopWindow(void);
761 /* Perform global initialisations that need a task context */
763 /* Initialize built-in window classes */
764 if (!WIDGETS_Init()) return;
766 /* Create desktop window */
767 if (!WIN_CreateDesktopWindow()) return;
769 firstTask = 0;
772 #ifndef WINELIB
773 NE_InitializeDLLs( pTask->hModule );
775 /* Registers on return are:
776 * ax 1 if OK, 0 on error
777 * cx stack limit in bytes
778 * dx cmdShow parameter
779 * si instance handle of the previous instance
780 * di instance handle of the new task
781 * es:bx pointer to command-line inside PSP
783 EAX_reg(&context) = 1;
784 EBX_reg(&context) = 0x81;
785 ECX_reg(&context) = pModule->stack_size;
786 EDX_reg(&context) = pTask->nCmdShow;
787 ESI_reg(&context) = (DWORD)pTask->hPrevInstance;
788 EDI_reg(&context) = (DWORD)pTask->hInstance;
789 ES_reg (&context) = (WORD)pTask->hPDB;
791 /* Initialize the local heap */
792 if ( pModule->heap_size )
794 LocalInit( pTask->hInstance, 0, pModule->heap_size );
796 #endif
799 /* Initialize the INSTANCEDATA structure */
800 pSegTable = NE_SEG_TABLE( pModule );
801 stacklow = pSegTable[pModule->ss - 1].minsize;
802 stackhi = stacklow + pModule->stack_size;
803 if (stackhi > 0xffff) stackhi = 0xffff;
804 pinstance = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN(CURRENT_DS, 0);
805 pinstance->stackbottom = stackhi; /* yup, that's right. Confused me too. */
806 pinstance->stacktop = stacklow;
807 #ifndef WINELIB
808 pinstance->stackmin = IF1632_Saved16_sp;
809 #endif
813 /***********************************************************************
814 * WaitEvent (KERNEL.30)
816 BOOL WaitEvent( HTASK hTask )
818 TDB *pTask;
820 if (!hTask) hTask = hCurrentTask;
821 pTask = (TDB *)GlobalLock( hTask );
822 if (pTask->nEvents > 0)
824 pTask->nEvents--;
825 return FALSE;
827 TASK_SCHEDULE();
828 /* When we get back here, we have an event (or the task is the only one) */
829 if (pTask->nEvents > 0) pTask->nEvents--;
830 return TRUE;
834 /***********************************************************************
835 * PostEvent (KERNEL.31)
837 void PostEvent( HTASK hTask )
839 TDB *pTask;
841 if (!hTask) hTask = hCurrentTask;
842 if (!(pTask = (TDB *)GlobalLock( hTask ))) return;
843 pTask->nEvents++;
847 /***********************************************************************
848 * SetPriority (KERNEL.32)
850 void SetPriority( HTASK hTask, int delta )
852 TDB *pTask;
853 int newpriority;
855 if (!hTask) hTask = hCurrentTask;
856 if (!(pTask = (TDB *)GlobalLock( hTask ))) return;
857 newpriority = pTask->priority + delta;
858 if (newpriority < -32) newpriority = -32;
859 else if (newpriority > 15) newpriority = 15;
861 pTask->priority = newpriority + 1;
862 TASK_UnlinkTask( hTask );
863 TASK_LinkTask( hTask );
864 pTask->priority--;
868 /***********************************************************************
869 * LockCurrentTask (KERNEL.33)
871 HTASK LockCurrentTask( BOOL bLock )
873 if (bLock) hLockedTask = hCurrentTask;
874 else hLockedTask = 0;
875 return hLockedTask;
879 /***********************************************************************
880 * IsTaskLocked (KERNEL.122)
882 HTASK IsTaskLocked(void)
884 return hLockedTask;
888 /***********************************************************************
889 * OldYield (KERNEL.117)
891 void OldYield(void)
893 TDB *pCurTask;
895 pCurTask = (TDB *)GlobalLock( hCurrentTask );
896 if (pCurTask) pCurTask->nEvents++; /* Make sure we get back here */
897 TASK_SCHEDULE();
898 if (pCurTask) pCurTask->nEvents--;
902 /***********************************************************************
903 * DirectedYield (KERNEL.150)
905 void DirectedYield( HTASK hTask )
907 TDB *pCurTask;
909 if ((pCurTask = (TDB *)GlobalLock( hCurrentTask )) != NULL)
910 pCurTask->hYieldTo = hTask;
912 OldYield();
916 /***********************************************************************
917 * Yield (KERNEL.29)
919 void Yield(void)
921 DirectedYield( 0 );
925 /***********************************************************************
926 * MakeProcInstance (KERNEL.51)
928 FARPROC MakeProcInstance( FARPROC func, HANDLE hInstance )
930 #ifdef WINELIB32
931 return func; /* func can be called directly in Win32 */
932 #else
933 BYTE *thunk;
934 SEGPTR thunkaddr;
936 thunkaddr = TASK_AllocThunk( hCurrentTask );
937 if (!thunkaddr) return (FARPROC)0;
938 thunk = PTR_SEG_TO_LIN( thunkaddr );
940 dprintf_task( stddeb, "MakeProcInstance("SPFMT","NPFMT"): got thunk "SPFMT"\n",
941 (SEGPTR)func, hInstance, (SEGPTR)thunkaddr );
943 *thunk++ = 0xb8; /* movw instance, %ax */
944 *thunk++ = (BYTE)(hInstance & 0xff);
945 *thunk++ = (BYTE)(hInstance >> 8);
946 *thunk++ = 0xea; /* ljmp func */
947 *(DWORD *)thunk = (DWORD)func;
948 return (FARPROC)thunkaddr;
949 #endif
953 /***********************************************************************
954 * FreeProcInstance (KERNEL.52)
956 void FreeProcInstance( FARPROC func )
958 #ifndef WINELIB32
959 dprintf_task( stddeb, "FreeProcInstance("SPFMT")\n", (SEGPTR)func );
960 TASK_FreeThunk( hCurrentTask, (SEGPTR)func );
961 #endif
965 /**********************************************************************
966 * GetCodeHandle (KERNEL.93)
968 HANDLE GetCodeHandle( FARPROC proc )
970 HANDLE handle;
971 BYTE *thunk = (BYTE *)PTR_SEG_TO_LIN( proc );
973 /* Return the code segment containing 'proc'. */
974 /* Not sure if this is really correct (shouldn't matter that much). */
976 /* Check if it is really a thunk */
977 if ((thunk[0] == 0xb8) && (thunk[3] == 0xea))
978 handle = GlobalHandle( thunk[6] + (thunk[7] << 8) );
979 else
980 handle = GlobalHandle( HIWORD(proc) );
982 return handle;
986 /***********************************************************************
987 * SetTaskQueue (KERNEL.34)
989 HGLOBAL SetTaskQueue( HANDLE hTask, HGLOBAL hQueue )
991 HGLOBAL hPrev;
992 TDB *pTask;
994 if (!hTask) hTask = hCurrentTask;
995 if (!(pTask = (TDB *)GlobalLock( hTask ))) return 0;
996 hPrev = pTask->hQueue;
997 pTask->hQueue = hQueue;
998 return hPrev;
1002 /***********************************************************************
1003 * GetTaskQueue (KERNEL.35)
1005 HGLOBAL GetTaskQueue( HANDLE hTask )
1007 TDB *pTask;
1009 if (!hTask) hTask = hCurrentTask;
1010 if (!(pTask = (TDB *)GlobalLock( hTask ))) return 0;
1011 return pTask->hQueue;
1015 /***********************************************************************
1016 * GetTaskQueueDS (KERNEL.118)
1018 #ifndef WINELIB
1019 void GetTaskQueueDS( struct sigcontext_struct context )
1021 DS_reg(&context) = GlobalHandleToSel( GetTaskQueue(0) );
1023 #endif /* WINELIB */
1026 /***********************************************************************
1027 * GetTaskQueueES (KERNEL.119)
1029 #ifndef WINELIB
1030 void GetTaskQueueES( struct sigcontext_struct context )
1032 ES_reg(&context) = GlobalHandleToSel( GetTaskQueue(0) );
1034 #endif /* WINELIB */
1037 /***********************************************************************
1038 * GetCurrentTask (KERNEL.36)
1040 HTASK GetCurrentTask(void)
1042 /* Undocumented: first task is returned in high word */
1043 #ifdef WINELIB32
1044 return hCurrentTask;
1045 #else
1046 return MAKELONG( hCurrentTask, hFirstTask );
1047 #endif
1051 /***********************************************************************
1052 * GetCurrentPDB (KERNEL.37)
1054 HANDLE GetCurrentPDB(void)
1056 TDB *pTask;
1058 if (!(pTask = (TDB *)GlobalLock( hCurrentTask ))) return 0;
1059 return pTask->hPDB;
1063 /***********************************************************************
1064 * GetInstanceData (KERNEL.54)
1066 int GetInstanceData( HANDLE instance, WORD buffer, int len )
1068 char *ptr = (char *)GlobalLock( instance );
1069 if (!ptr || !len) return 0;
1070 if ((int)buffer + len >= 0x10000) len = 0x10000 - buffer;
1071 memcpy( ptr + buffer, (char *)GlobalLock( CURRENT_DS ) + buffer, len );
1072 return len;
1076 /***********************************************************************
1077 * SetErrorMode (KERNEL.107)
1079 UINT SetErrorMode( UINT mode )
1081 TDB *pTask;
1082 UINT oldMode;
1084 if (!(pTask = (TDB *)GlobalLock( hCurrentTask ))) return 0;
1085 oldMode = pTask->error_mode;
1086 pTask->error_mode = mode;
1087 return oldMode;
1091 /***********************************************************************
1092 * GetDOSEnvironment (KERNEL.131)
1094 SEGPTR GetDOSEnvironment(void)
1096 TDB *pTask;
1098 if (!(pTask = (TDB *)GlobalLock( hCurrentTask ))) return 0;
1099 return (SEGPTR)WIN16_GlobalLock( pTask->pdb.environment );
1103 /***********************************************************************
1104 * GetNumTasks (KERNEL.152)
1106 WORD GetNumTasks(void)
1108 return nTaskCount;
1112 /***********************************************************************
1113 * GetTaskDS (KERNEL.155)
1115 HINSTANCE GetTaskDS(void)
1117 TDB *pTask;
1119 if (!(pTask = (TDB *)GlobalLock( hCurrentTask ))) return 0;
1120 return pTask->hInstance;
1124 /***********************************************************************
1125 * IsTask (KERNEL.320)
1127 BOOL IsTask( HTASK hTask )
1129 TDB *pTask;
1131 if (!(pTask = (TDB *)GlobalLock( hTask ))) return FALSE;
1132 if (GlobalSize( hTask ) < sizeof(TDB)) return FALSE;
1133 return (pTask->magic == TDB_MAGIC);
1137 /***********************************************************************
1138 * GetExePtr (KERNEL.133)
1140 HMODULE GetExePtr( HANDLE handle )
1142 char *ptr;
1143 HTASK hTask;
1144 HANDLE owner;
1146 /* Check for module handle */
1148 if (!(ptr = GlobalLock( handle ))) return 0;
1149 if (((NE_MODULE *)ptr)->magic == NE_SIGNATURE) return handle;
1150 /* Fake modules describing PE modules have a PE signature */
1151 if (((NE_MODULE *)ptr)->magic == PE_SIGNATURE) return handle;
1153 /* Check the owner for module handle */
1155 #ifndef WINELIB
1156 owner = FarGetOwner( handle );
1157 #else
1158 owner = NULL;
1159 #endif
1160 if (!(ptr = GlobalLock( owner ))) return 0;
1161 if (((NE_MODULE *)ptr)->magic == NE_SIGNATURE) return owner;
1162 if (((NE_MODULE *)ptr)->magic == PE_SIGNATURE) return owner;
1164 /* Search for this handle and its owner inside all tasks */
1166 hTask = hFirstTask;
1167 while (hTask)
1169 TDB *pTask = (TDB *)GlobalLock( hTask );
1170 if ((hTask == handle) ||
1171 (pTask->hInstance == handle) ||
1172 (pTask->hQueue == handle) ||
1173 (pTask->hPDB == handle)) return pTask->hModule;
1174 if ((hTask == owner) ||
1175 (pTask->hInstance == owner) ||
1176 (pTask->hQueue == owner) ||
1177 (pTask->hPDB == owner)) return pTask->hModule;
1178 hTask = pTask->hNext;
1180 return 0;
1184 /***********************************************************************
1185 * TaskFirst (TOOLHELP.63)
1187 BOOL TaskFirst( TASKENTRY *lpte )
1189 lpte->hNext = hFirstTask;
1190 return TaskNext( lpte );
1194 /***********************************************************************
1195 * TaskNext (TOOLHELP.64)
1197 BOOL TaskNext( TASKENTRY *lpte )
1199 TDB *pTask;
1200 INSTANCEDATA *pInstData;
1202 dprintf_toolhelp( stddeb, "TaskNext(%p): task="NPFMT"\n", lpte, lpte->hNext );
1203 if (!lpte->hNext) return FALSE;
1204 pTask = (TDB *)GlobalLock( lpte->hNext );
1205 if (!pTask || pTask->magic != TDB_MAGIC) return FALSE;
1206 pInstData = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( pTask->hInstance, 0 );
1207 lpte->hTask = lpte->hNext;
1208 lpte->hTaskParent = pTask->hParent;
1209 lpte->hInst = pTask->hInstance;
1210 lpte->hModule = pTask->hModule;
1211 lpte->wSS = pTask->ss;
1212 lpte->wSP = pTask->sp;
1213 lpte->wStackTop = pInstData->stacktop;
1214 lpte->wStackMinimum = pInstData->stackmin;
1215 lpte->wStackBottom = pInstData->stackbottom;
1216 lpte->wcEvents = pTask->nEvents;
1217 lpte->hQueue = pTask->hQueue;
1218 strncpy( lpte->szModule, pTask->module_name, 8 );
1219 lpte->szModule[8] = '\0';
1220 lpte->wPSPOffset = 0x100; /*??*/
1221 lpte->hNext = pTask->hNext;
1222 return TRUE;
1226 /***********************************************************************
1227 * TaskFindHandle (TOOLHELP.65)
1229 BOOL TaskFindHandle( TASKENTRY *lpte, HTASK hTask )
1231 lpte->hNext = hTask;
1232 return TaskNext( lpte );