More Escapes for the PostScript driver.
[wine.git] / loader / task.c
blobca7c9a7a556fcbc0b20ce7ea82ad5b1cafd41e9f
1 /*
2 * Task functions
4 * Copyright 1995 Alexandre Julliard
5 */
7 #include <stdlib.h>
8 #include <string.h>
9 #include <assert.h>
10 #include <unistd.h>
12 #include "wine/winbase16.h"
13 #include "user.h"
14 #include "callback.h"
15 #include "drive.h"
16 #include "file.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 "peexe.h"
24 #include "pe_image.h"
25 #include "process.h"
26 #include "queue.h"
27 #include "selectors.h"
28 #include "stackframe.h"
29 #include "task.h"
30 #include "thread.h"
31 #include "toolhelp.h"
32 #include "winnt.h"
33 #include "winsock.h"
34 #include "thread.h"
35 #include "syslevel.h"
36 #include "debug.h"
37 #include "dosexe.h"
38 #include "dde_proc.h"
39 #include "server.h"
41 /* Min. number of thunks allocated when creating a new segment */
42 #define MIN_THUNKS 32
44 /* Pointer to function to switch to a larger stack */
45 int (*IF1632_CallLargeStack)( int (*func)(), void *arg ) = NULL;
47 /* Pointer to debugger callback routine */
48 void (*TASK_AddTaskEntryBreakpoint)( HTASK16 hTask ) = NULL;
50 static THHOOK DefaultThhook = { 0 };
51 THHOOK *pThhook = &DefaultThhook;
53 #define hCurrentTask (pThhook->CurTDB)
54 #define hFirstTask (pThhook->HeadTDB)
55 #define hLockedTask (pThhook->LockTDB)
57 static HTASK16 hTaskToKill = 0;
58 static UINT16 nTaskCount = 0;
60 static void TASK_YieldToSystem( void );
62 extern BOOL THREAD_InitDone;
65 /***********************************************************************
66 * TASK_InstallTHHook
68 void TASK_InstallTHHook( THHOOK *pNewThhook )
70 THHOOK *pOldThhook = pThhook;
72 pThhook = pNewThhook? pNewThhook : &DefaultThhook;
74 *pThhook = *pOldThhook;
77 /***********************************************************************
78 * TASK_GetNextTask
80 HTASK16 TASK_GetNextTask( HTASK16 hTask )
82 TDB* pTask = (TDB*)GlobalLock16(hTask);
84 if (pTask->hNext) return pTask->hNext;
85 return (hFirstTask != hTask) ? hFirstTask : 0;
88 /***********************************************************************
89 * TASK_LinkTask
91 static void TASK_LinkTask( HTASK16 hTask )
93 HTASK16 *prevTask;
94 TDB *pTask;
96 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
97 prevTask = &hFirstTask;
98 while (*prevTask)
100 TDB *prevTaskPtr = (TDB *)GlobalLock16( *prevTask );
101 if (prevTaskPtr->priority >= pTask->priority) break;
102 prevTask = &prevTaskPtr->hNext;
104 pTask->hNext = *prevTask;
105 *prevTask = hTask;
106 nTaskCount++;
110 /***********************************************************************
111 * TASK_UnlinkTask
113 static void TASK_UnlinkTask( HTASK16 hTask )
115 HTASK16 *prevTask;
116 TDB *pTask;
118 prevTask = &hFirstTask;
119 while (*prevTask && (*prevTask != hTask))
121 pTask = (TDB *)GlobalLock16( *prevTask );
122 prevTask = &pTask->hNext;
124 if (*prevTask)
126 pTask = (TDB *)GlobalLock16( *prevTask );
127 *prevTask = pTask->hNext;
128 pTask->hNext = 0;
129 nTaskCount--;
134 /***********************************************************************
135 * TASK_CreateThunks
137 * Create a thunk free-list in segment 'handle', starting from offset 'offset'
138 * and containing 'count' entries.
140 static void TASK_CreateThunks( HGLOBAL16 handle, WORD offset, WORD count )
142 int i;
143 WORD free;
144 THUNKS *pThunk;
146 pThunk = (THUNKS *)((BYTE *)GlobalLock16( handle ) + offset);
147 pThunk->next = 0;
148 pThunk->magic = THUNK_MAGIC;
149 pThunk->free = (int)&pThunk->thunks - (int)pThunk;
150 free = pThunk->free;
151 for (i = 0; i < count-1; i++)
153 free += 8; /* Offset of next thunk */
154 pThunk->thunks[4*i] = free;
156 pThunk->thunks[4*i] = 0; /* Last thunk */
160 /***********************************************************************
161 * TASK_AllocThunk
163 * Allocate a thunk for MakeProcInstance().
165 static SEGPTR TASK_AllocThunk( HTASK16 hTask )
167 TDB *pTask;
168 THUNKS *pThunk;
169 WORD sel, base;
171 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
172 sel = pTask->hCSAlias;
173 pThunk = &pTask->thunks;
174 base = (int)pThunk - (int)pTask;
175 while (!pThunk->free)
177 sel = pThunk->next;
178 if (!sel) /* Allocate a new segment */
180 sel = GLOBAL_Alloc( GMEM_FIXED, sizeof(THUNKS) + (MIN_THUNKS-1)*8,
181 pTask->hPDB, TRUE, FALSE, FALSE );
182 if (!sel) return (SEGPTR)0;
183 TASK_CreateThunks( sel, 0, MIN_THUNKS );
184 pThunk->next = sel;
186 pThunk = (THUNKS *)GlobalLock16( sel );
187 base = 0;
189 base += pThunk->free;
190 pThunk->free = *(WORD *)((BYTE *)pThunk + pThunk->free);
191 return PTR_SEG_OFF_TO_SEGPTR( sel, base );
195 /***********************************************************************
196 * TASK_FreeThunk
198 * Free a MakeProcInstance() thunk.
200 static BOOL TASK_FreeThunk( HTASK16 hTask, SEGPTR thunk )
202 TDB *pTask;
203 THUNKS *pThunk;
204 WORD sel, base;
206 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
207 sel = pTask->hCSAlias;
208 pThunk = &pTask->thunks;
209 base = (int)pThunk - (int)pTask;
210 while (sel && (sel != HIWORD(thunk)))
212 sel = pThunk->next;
213 pThunk = (THUNKS *)GlobalLock16( sel );
214 base = 0;
216 if (!sel) return FALSE;
217 *(WORD *)((BYTE *)pThunk + LOWORD(thunk) - base) = pThunk->free;
218 pThunk->free = LOWORD(thunk) - base;
219 return TRUE;
223 /***********************************************************************
224 * TASK_CallToStart
226 * 32-bit entry point for a new task. This function is responsible for
227 * setting up the registers and jumping to the 16-bit entry point.
229 static void TASK_CallToStart(void)
231 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
232 NE_MODULE *pModule = NE_GetPtr( pTask->hModule );
233 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
235 SET_CUR_THREAD( pTask->thdb );
236 CLIENT_InitThread();
238 /* Terminate the stack frame chain */
239 memset(THREAD_STACK16( pTask->thdb ), '\0', sizeof(STACK16FRAME));
241 if (pModule->flags & NE_FFLAGS_WIN32)
243 ERR( task, "Called for Win32 task!\n" );
244 ExitProcess( 1 );
246 else if (pModule->dos_image)
248 DOSVM_Enter( NULL );
249 ExitProcess( 0 );
251 else
253 /* Registers at initialization must be:
254 * ax zero
255 * bx stack size in bytes
256 * cx heap size in bytes
257 * si previous app instance
258 * di current app instance
259 * bp zero
260 * es selector to the PSP
261 * ds dgroup of the application
262 * ss stack selector
263 * sp top of the stack
265 CONTEXT context;
267 memset( &context, 0, sizeof(context) );
268 CS_reg(&context) = GlobalHandleToSel16(pSegTable[pModule->cs - 1].hSeg);
269 DS_reg(&context) = GlobalHandleToSel16(pSegTable[pModule->dgroup - 1].hSeg);
270 ES_reg(&context) = pTask->hPDB;
271 EIP_reg(&context) = pModule->ip;
272 EBX_reg(&context) = pModule->stack_size;
273 ECX_reg(&context) = pModule->heap_size;
274 EDI_reg(&context) = context.SegDs;
276 TRACE(task, "Starting main program: cs:ip=%04lx:%04x ds=%04lx ss:sp=%04x:%04x\n",
277 CS_reg(&context), IP_reg(&context), DS_reg(&context),
278 SELECTOROF(pTask->thdb->cur_stack),
279 OFFSETOF(pTask->thdb->cur_stack) );
281 Callbacks->CallRegisterShortProc( &context, 0 );
282 /* This should never return */
283 ERR( task, "Main program returned! (should never happen)\n" );
284 ExitProcess( 1 );
289 /***********************************************************************
290 * TASK_Create
292 * NOTE: This routine might be called by a Win32 thread. We don't have
293 * any real problems with that, since we operated merely on a private
294 * TDB structure that is not yet linked into the task list.
296 BOOL TASK_Create( THDB *thdb, NE_MODULE *pModule, HINSTANCE16 hInstance,
297 HINSTANCE16 hPrevInstance, UINT16 cmdShow)
299 HTASK16 hTask;
300 TDB *pTask, *pInitialTask;
301 LPSTR cmd_line;
302 WORD sp;
303 char *stack32Top;
304 char name[10];
305 STACK16FRAME *frame16;
306 STACK32FRAME *frame32;
307 PDB *pdb32 = thdb->process;
308 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
310 /* Allocate the task structure */
312 hTask = GLOBAL_Alloc( GMEM_FIXED | GMEM_ZEROINIT, sizeof(TDB),
313 pModule->self, FALSE, FALSE, FALSE );
314 if (!hTask) return FALSE;
315 pTask = (TDB *)GlobalLock16( hTask );
317 /* Fill the task structure */
319 pTask->nEvents = 0;
320 pTask->hSelf = hTask;
321 pTask->flags = 0;
323 if (pModule->flags & NE_FFLAGS_WIN32)
324 pTask->flags |= TDBF_WIN32;
325 if (pModule->lpDosTask)
326 pTask->flags |= TDBF_WINOLDAP;
328 pTask->version = pModule->expected_version;
329 pTask->hInstance = hInstance? hInstance : pModule->self;
330 pTask->hPrevInstance = hPrevInstance;
331 pTask->hModule = pModule->self;
332 pTask->hParent = GetCurrentTask();
333 pTask->magic = TDB_MAGIC;
334 pTask->nCmdShow = cmdShow;
335 pTask->thdb = thdb;
336 pTask->curdrive = DRIVE_GetCurrentDrive() | 0x80;
337 strcpy( pTask->curdir, "\\" );
338 lstrcpynA( pTask->curdir + 1, DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() ),
339 sizeof(pTask->curdir) - 1 );
341 /* Create the thunks block */
343 TASK_CreateThunks( hTask, (int)&pTask->thunks - (int)pTask, 7 );
345 /* Copy the module name */
347 GetModuleName16( pModule->self, name, sizeof(name) );
348 strncpy( pTask->module_name, name, sizeof(pTask->module_name) );
350 /* Allocate a selector for the PDB */
352 pTask->hPDB = GLOBAL_CreateBlock( GMEM_FIXED, &pTask->pdb, sizeof(PDB16),
353 pModule->self, FALSE, FALSE, FALSE, NULL );
355 /* Fill the PDB */
357 pTask->pdb.int20 = 0x20cd;
358 pTask->pdb.dispatcher[0] = 0x9a; /* ljmp */
359 PUT_DWORD(&pTask->pdb.dispatcher[1], (DWORD)NE_GetEntryPoint(
360 GetModuleHandle16("KERNEL"), 102 )); /* KERNEL.102 is DOS3Call() */
361 pTask->pdb.savedint22 = INT_GetPMHandler( 0x22 );
362 pTask->pdb.savedint23 = INT_GetPMHandler( 0x23 );
363 pTask->pdb.savedint24 = INT_GetPMHandler( 0x24 );
364 pTask->pdb.fileHandlesPtr =
365 PTR_SEG_OFF_TO_SEGPTR( GlobalHandleToSel16(pTask->hPDB),
366 (int)&((PDB16 *)0)->fileHandles );
367 pTask->pdb.hFileHandles = 0;
368 memset( pTask->pdb.fileHandles, 0xff, sizeof(pTask->pdb.fileHandles) );
369 pTask->pdb.environment = pdb32->env_db->env_sel;
370 pTask->pdb.nbFiles = 20;
372 /* Fill the command line */
374 cmd_line = pdb32->env_db->cmd_line;
375 while (*cmd_line && (*cmd_line != ' ') && (*cmd_line != '\t')) cmd_line++;
376 while ((*cmd_line == ' ') || (*cmd_line == '\t')) cmd_line++;
377 lstrcpynA( pTask->pdb.cmdLine+1, cmd_line, sizeof(pTask->pdb.cmdLine)-1);
378 pTask->pdb.cmdLine[0] = strlen( pTask->pdb.cmdLine + 1 );
380 /* Get the compatibility flags */
382 pTask->compat_flags = GetProfileIntA( "Compatibility", name, 0 );
384 /* Allocate a code segment alias for the TDB */
386 pTask->hCSAlias = GLOBAL_CreateBlock( GMEM_FIXED, (void *)pTask,
387 sizeof(TDB), pTask->hPDB, TRUE,
388 FALSE, FALSE, NULL );
390 /* Set the owner of the environment block */
392 FarSetOwner16( pTask->pdb.environment, pTask->hPDB );
394 /* Default DTA overwrites command-line */
396 pTask->dta = PTR_SEG_OFF_TO_SEGPTR( pTask->hPDB,
397 (int)&pTask->pdb.cmdLine - (int)&pTask->pdb );
399 /* Inherit default UserSignalHandler from initial process */
401 pInitialTask = (TDB *)GlobalLock16( PROCESS_Initial()->task );
402 if ( pInitialTask )
403 pTask->userhandler = pInitialTask->userhandler;
405 /* If we have a DGROUP/hInstance, use it for 16-bit stack */
407 if ( hInstance )
409 if (!(sp = pModule->sp))
410 sp = pSegTable[pModule->ss-1].minsize + pModule->stack_size;
411 sp &= ~1; sp -= sizeof(STACK16FRAME);
412 pTask->thdb->cur_stack = PTR_SEG_OFF_TO_SEGPTR( hInstance, sp );
415 /* Create the 16-bit stack frame */
417 pTask->thdb->cur_stack -= sizeof(STACK16FRAME);
418 frame16 = (STACK16FRAME *)PTR_SEG_TO_LIN( pTask->thdb->cur_stack );
419 frame16->ebp = OFFSETOF( pTask->thdb->cur_stack ) + (int)&((STACK16FRAME *)0)->bp;
420 frame16->bp = LOWORD(frame16->ebp);
421 frame16->ds = frame16->es = hInstance;
422 frame16->fs = 0;
423 frame16->entry_point = 0;
424 frame16->entry_cs = 0;
425 frame16->mutex_count = 1; /* TASK_Reschedule is called from 16-bit code */
426 /* The remaining fields will be initialized in TASK_Reschedule */
428 /* Create the 32-bit stack frame */
430 stack32Top = (char*)pTask->thdb->teb.stack_top;
431 frame16->frame32 = frame32 = (STACK32FRAME *)stack32Top - 1;
432 frame32->frame16 = pTask->thdb->cur_stack + sizeof(STACK16FRAME);
433 frame32->edi = 0;
434 frame32->esi = 0;
435 frame32->edx = 0;
436 frame32->ecx = 0;
437 frame32->ebx = 0;
438 frame32->retaddr = (DWORD)TASK_CallToStart;
439 /* The remaining fields will be initialized in TASK_Reschedule */
441 /* Enter task handle into thread and process */
443 pTask->thdb->teb.htask16 = pTask->thdb->process->task = hTask;
445 TRACE(task, "module='%s' cmdline='%s' task=%04x\n",
446 name, cmd_line, hTask );
448 return TRUE;
451 /***********************************************************************
452 * TASK_StartTask
454 * NOTE: This routine might be called by a Win32 thread. Thus, we need
455 * to be careful to protect global data structures. We do this
456 * by entering the Win16Lock while linking the task into the
457 * global task list.
459 void TASK_StartTask( HTASK16 hTask )
461 TDB *pTask = (TDB *)GlobalLock16( hTask );
462 if ( !pTask ) return;
464 /* Add the task to the linked list */
466 SYSLEVEL_EnterWin16Lock();
467 TASK_LinkTask( hTask );
468 SYSLEVEL_LeaveWin16Lock();
470 TRACE(task, "linked task %04x\n", hTask );
472 /* If requested, add entry point breakpoint */
474 if ( TASK_AddTaskEntryBreakpoint )
475 TASK_AddTaskEntryBreakpoint( hTask );
477 /* Get the task up and running. */
479 if ( THREAD_IsWin16( pTask->thdb ) )
481 pTask->nEvents++;
483 /* If we ourselves are a 16-bit task, we simply Yield().
484 If we are 32-bit however, we need to signal the scheduler. */
486 if ( THREAD_IsWin16( THREAD_Current() ) )
487 OldYield16();
488 else
489 EVENT_WakeUp();
491 else
493 /* To start a 32-bit task, we spawn its initial thread. */
495 SYSDEPS_SpawnThread( pTask->thdb );
500 /***********************************************************************
501 * TASK_DeleteTask
503 static void TASK_DeleteTask( HTASK16 hTask )
505 TDB *pTask;
506 HGLOBAL16 hPDB;
508 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
509 hPDB = pTask->hPDB;
511 pTask->magic = 0xdead; /* invalidate signature */
513 /* Delete the Win32 part of the task */
515 /* PROCESS_FreePDB( pTask->thdb->process ); FIXME */
516 /* K32OBJ_DecCount( &pTask->thdb->header ); FIXME */
518 /* Free the selector aliases */
520 GLOBAL_FreeBlock( pTask->hCSAlias );
521 GLOBAL_FreeBlock( pTask->hPDB );
523 /* Free the task module */
525 FreeModule16( pTask->hModule );
527 /* Free the task structure itself */
529 GlobalFree16( hTask );
531 /* Free all memory used by this task (including the 32-bit stack, */
532 /* the environment block and the thunk segments). */
534 GlobalFreeAll16( hPDB );
537 /***********************************************************************
538 * TASK_KillTask
540 void TASK_KillTask( HTASK16 hTask )
542 TDB *pTask;
544 /* Enter the Win16Lock to protect global data structures */
545 SYSLEVEL_EnterWin16Lock();
547 if ( !hTask ) hTask = GetCurrentTask();
548 pTask = (TDB *)GlobalLock16( hTask );
549 if ( !pTask )
551 SYSLEVEL_LeaveWin16Lock();
552 return;
555 TRACE(task, "Killing task %04x\n", hTask );
557 /* Delete active sockets */
559 if( pTask->pwsi )
560 WINSOCK_DeleteTaskWSI( pTask, pTask->pwsi );
562 #ifdef MZ_SUPPORTED
564 /* Kill DOS VM task */
565 NE_MODULE *pModule = NE_GetPtr( pTask->hModule );
566 if ( pModule->lpDosTask )
567 MZ_KillModule( pModule->lpDosTask );
569 #endif
571 /* Perform USER cleanup */
573 if (pTask->userhandler)
574 pTask->userhandler( hCurrentTask, USIG_TERMINATION, 0,
575 pTask->hInstance, pTask->hQueue );
577 if (nTaskCount <= 1)
579 TRACE(task, "this is the last task, exiting\n" );
580 USER_ExitWindows();
583 /* FIXME: Hack! Send a message to the initial task so that
584 * the GetMessage wakes up and the initial task can check whether
585 * it is the only remaining one and terminate itself ...
586 * The initial task should probably install hooks or something
587 * to get informed about task termination :-/
589 Callout.PostAppMessage16( PROCESS_Initial()->task, WM_NULL, 0, 0 );
591 /* Remove the task from the list to be sure we never switch back to it */
592 TASK_UnlinkTask( hTask );
593 if( nTaskCount )
595 TDB* p = (TDB *)GlobalLock16( hFirstTask );
596 while( p )
598 if( p->hYieldTo == hTask ) p->hYieldTo = 0;
599 p = (TDB *)GlobalLock16( p->hNext );
603 pTask->nEvents = 0;
605 if ( hLockedTask == hTask )
606 hLockedTask = 0;
608 if ( hTaskToKill && ( hTaskToKill != hCurrentTask ) )
610 /* If another task is already marked for destruction, */
611 /* we can kill it now, as we are in another context. */
612 TASK_DeleteTask( hTaskToKill );
613 hTaskToKill = 0;
617 * If hTask is not the task currently scheduled by the Win16
618 * scheduler, we simply delete it; otherwise we mark it for
619 * destruction. Note that if the current task is a 32-bit
620 * one, hCurrentTask is *different* from GetCurrentTask()!
622 if ( hTask == hCurrentTask )
624 assert( hTaskToKill == 0 || hTaskToKill == hCurrentTask );
625 hTaskToKill = hCurrentTask;
627 else
628 TASK_DeleteTask( hTask );
630 SYSLEVEL_LeaveWin16Lock();
634 /***********************************************************************
635 * TASK_KillCurrentTask
637 * Kill the currently running task. As it's not possible to kill the
638 * current task like this, it is simply marked for destruction, and will
639 * be killed when either TASK_Reschedule or this function is called again
640 * in the context of another task.
642 void TASK_KillCurrentTask( INT16 exitCode )
644 if ( !THREAD_IsWin16( THREAD_Current() ) )
646 FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
647 return;
650 assert(hCurrentTask == GetCurrentTask());
652 TRACE(task, "Killing current task %04x\n", hCurrentTask );
654 TASK_KillTask( 0 );
656 TASK_YieldToSystem();
658 /* We should never return from this Yield() */
660 ERR(task,"Return of the living dead %04x!!!\n", hCurrentTask);
661 exit(1);
664 /***********************************************************************
665 * TASK_Reschedule
667 * This is where all the magic of task-switching happens!
669 * Note: This function should only be called via the TASK_YieldToSystem()
670 * wrapper, to make sure that all the context is saved correctly.
672 * It must not call functions that may yield control.
674 BOOL TASK_Reschedule(void)
676 TDB *pOldTask = NULL, *pNewTask;
677 HTASK16 hTask = 0;
678 STACK16FRAME *newframe16;
679 BOOL pending = FALSE;
681 /* Get the initial task up and running */
682 if (!hCurrentTask && GetCurrentTask())
684 /* We need to remove one pair of stackframes (exept for Winelib) */
685 STACK16FRAME *oldframe16 = CURRENT_STACK16;
686 STACK32FRAME *oldframe32 = oldframe16->frame32;
687 STACK16FRAME *newframe16 = PTR_SEG_TO_LIN( oldframe32->frame16 );
688 STACK32FRAME *newframe32 = newframe16->frame32;
689 if (newframe32)
691 newframe16->entry_ip = oldframe16->entry_ip;
692 newframe16->entry_cs = oldframe16->entry_cs;
693 newframe16->ip = oldframe16->ip;
694 newframe16->cs = oldframe16->cs;
695 newframe32->ebp = oldframe32->ebp;
696 newframe32->restore_addr = oldframe32->restore_addr;
697 newframe32->codeselector = oldframe32->codeselector;
699 THREAD_Current()->cur_stack = oldframe32->frame16;
702 hCurrentTask = GetCurrentTask();
703 pNewTask = (TDB *)GlobalLock16( hCurrentTask );
704 pNewTask->ss_sp = pNewTask->thdb->cur_stack;
705 return FALSE;
708 /* NOTE: As we are entered from 16-bit code, we hold the Win16Lock.
709 We hang onto it thoughout most of this routine, so that accesses
710 to global variables (most notably the task list) are protected. */
711 assert(hCurrentTask == GetCurrentTask());
713 TRACE(task, "entered with hTask %04x (pid %d)\n", hCurrentTask, getpid());
715 #ifdef CONFIG_IPC
716 /* FIXME: What about the Win16Lock ??? */
717 dde_reschedule();
718 #endif
719 /* First check if there's a task to kill */
721 if (hTaskToKill && (hTaskToKill != hCurrentTask))
723 TASK_DeleteTask( hTaskToKill );
724 hTaskToKill = 0;
727 /* Find a task to yield to */
729 pOldTask = (TDB *)GlobalLock16( hCurrentTask );
730 if (pOldTask && pOldTask->hYieldTo)
732 /* check for DirectedYield() */
734 hTask = pOldTask->hYieldTo;
735 pNewTask = (TDB *)GlobalLock16( hTask );
736 if( !pNewTask || !pNewTask->nEvents) hTask = 0;
737 pOldTask->hYieldTo = 0;
740 /* extract hardware events only! */
742 if (!hTask) pending = EVENT_WaitNetEvent( FALSE, TRUE );
744 while (!hTask)
746 /* Find a task that has an event pending */
748 hTask = hFirstTask;
749 while (hTask)
751 pNewTask = (TDB *)GlobalLock16( hTask );
753 TRACE(task, "\ttask = %04x, events = %i\n", hTask, pNewTask->nEvents);
755 if (pNewTask->nEvents) break;
756 hTask = pNewTask->hNext;
758 if (hLockedTask && (hTask != hLockedTask)) hTask = 0;
759 if (hTask) break;
761 /* If a non-hardware event is pending, return to TASK_YieldToSystem
762 temporarily to process it safely */
763 if (pending) return TRUE;
765 /* No task found, wait for some events to come in */
767 /* NOTE: We release the Win16Lock while waiting for events. This is to enable
768 Win32 threads to thunk down to 16-bit temporarily. Since Win16
769 tasks won't execute and Win32 threads are not allowed to enter
770 TASK_Reschedule anyway, there should be no re-entrancy problem ... */
772 SYSLEVEL_ReleaseWin16Lock();
773 pending = EVENT_WaitNetEvent( TRUE, TRUE );
774 SYSLEVEL_RestoreWin16Lock();
777 if (hTask == hCurrentTask)
779 TRACE(task, "returning to the current task(%04x)\n", hTask );
780 return FALSE; /* Nothing to do */
782 pNewTask = (TDB *)GlobalLock16( hTask );
783 TRACE(task, "Switching to task %04x (%.8s)\n",
784 hTask, pNewTask->module_name );
786 /* Make the task the last in the linked list (round-robin scheduling) */
788 pNewTask->priority++;
789 TASK_UnlinkTask( hTask );
790 TASK_LinkTask( hTask );
791 pNewTask->priority--;
793 /* Finish initializing the new task stack if necessary */
795 newframe16 = THREAD_STACK16( pNewTask->thdb );
796 if (!newframe16->entry_cs)
798 STACK16FRAME *oldframe16 = CURRENT_STACK16;
799 STACK32FRAME *oldframe32 = oldframe16->frame32;
800 STACK32FRAME *newframe32 = newframe16->frame32;
801 newframe16->entry_ip = oldframe16->entry_ip;
802 newframe16->entry_cs = oldframe16->entry_cs;
803 newframe16->ip = oldframe16->ip;
804 newframe16->cs = oldframe16->cs;
805 newframe32->ebp = oldframe32->ebp;
806 newframe32->restore_addr = oldframe32->restore_addr;
807 newframe32->codeselector = oldframe32->codeselector;
810 /* Switch to the new stack */
812 /* NOTE: We need to release/restore the Win16Lock, as the task
813 switched to might be at another recursion level than
814 the old task ... */
816 SYSLEVEL_ReleaseWin16Lock();
818 hCurrentTask = hTask;
819 SET_CUR_THREAD( pNewTask->thdb );
820 pNewTask->ss_sp = pNewTask->thdb->cur_stack;
822 SYSLEVEL_RestoreWin16Lock();
824 return FALSE;
828 /***********************************************************************
829 * TASK_YieldToSystem
831 * Scheduler interface, this way we ensure that all "unsafe" events are
832 * processed outside the scheduler.
834 static void TASK_YieldToSystem( void )
836 if ( !THREAD_IsWin16( THREAD_Current() ) )
838 FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
839 return;
842 if ( Callbacks->CallTaskRescheduleProc() )
844 /* NOTE: We get here only when no task has an event. This means also
845 the current task, so we shouldn't actually return to the
846 caller here. But, we need to do so, as the EVENT_WaitNetEvent
847 call could lead to a complex series of inter-task SendMessage
848 calls which might leave this task in a state where it again
849 has no event, but where its queue's wakeMask is also reset
850 to zero. Reentering TASK_Reschedule in this state would be
851 suicide. Hence, we do return to the caller after processing
852 non-hardware events. Actually, this should not hurt anyone,
853 as the caller must be WaitEvent, and thus the QUEUE_WaitBits
854 loop in USER. Should there actually be no message pending
855 for this task after processing non-hardware events, that loop
856 will simply return to WaitEvent. */
858 EVENT_WaitNetEvent( FALSE, FALSE );
863 /***********************************************************************
864 * InitTask (KERNEL.91)
866 * Called by the application startup code.
868 void WINAPI InitTask16( CONTEXT *context )
870 TDB *pTask;
871 NE_MODULE *pModule;
872 SEGTABLEENTRY *pSegTable;
873 INSTANCEDATA *pinstance;
874 LONG stacklow, stackhi;
876 if (context) EAX_reg(context) = 0;
877 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
878 if (!(pModule = NE_GetPtr( pTask->hModule ))) return;
880 /* Initialize implicitly loaded DLLs */
881 NE_InitializeDLLs( pTask->hModule );
883 if (context)
885 /* Registers on return are:
886 * ax 1 if OK, 0 on error
887 * cx stack limit in bytes
888 * dx cmdShow parameter
889 * si instance handle of the previous instance
890 * di instance handle of the new task
891 * es:bx pointer to command-line inside PSP
893 * 0 (=%bp) is pushed on the stack
895 SEGPTR ptr = STACK16_PUSH( pTask->thdb, sizeof(WORD) );
896 *(WORD *)PTR_SEG_TO_LIN(ptr) = 0;
897 SP_reg(context) -= 2;
899 EAX_reg(context) = 1;
901 if (!pTask->pdb.cmdLine[0]) EBX_reg(context) = 0x80;
902 else
904 LPBYTE p = &pTask->pdb.cmdLine[1];
905 while ((*p == ' ') || (*p == '\t')) p++;
906 EBX_reg(context) = 0x80 + (p - pTask->pdb.cmdLine);
908 ECX_reg(context) = pModule->stack_size;
909 EDX_reg(context) = pTask->nCmdShow;
910 ESI_reg(context) = (DWORD)pTask->hPrevInstance;
911 EDI_reg(context) = (DWORD)pTask->hInstance;
912 ES_reg (context) = (WORD)pTask->hPDB;
915 /* Initialize the local heap */
916 if ( pModule->heap_size )
918 LocalInit16( pTask->hInstance, 0, pModule->heap_size );
921 /* Initialize the INSTANCEDATA structure */
922 pSegTable = NE_SEG_TABLE( pModule );
923 stacklow = pSegTable[pModule->ss - 1].minsize;
924 stackhi = stacklow + pModule->stack_size;
925 if (stackhi > 0xffff) stackhi = 0xffff;
926 pinstance = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN(CURRENT_DS, 0);
927 pinstance->stackbottom = stackhi; /* yup, that's right. Confused me too. */
928 pinstance->stacktop = stacklow;
929 pinstance->stackmin = OFFSETOF( pTask->thdb->cur_stack );
933 /***********************************************************************
934 * WaitEvent (KERNEL.30)
936 BOOL16 WINAPI WaitEvent16( HTASK16 hTask )
938 TDB *pTask;
940 if (!hTask) hTask = GetCurrentTask();
941 pTask = (TDB *)GlobalLock16( hTask );
943 if ( !THREAD_IsWin16( THREAD_Current() ) )
945 FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
946 return TRUE;
949 if (pTask->nEvents > 0)
951 pTask->nEvents--;
952 return FALSE;
954 TASK_YieldToSystem();
956 /* When we get back here, we have an event */
958 if (pTask->nEvents > 0) pTask->nEvents--;
959 return TRUE;
963 /***********************************************************************
964 * PostEvent (KERNEL.31)
966 void WINAPI PostEvent16( HTASK16 hTask )
968 TDB *pTask;
970 if (!hTask) hTask = GetCurrentTask();
971 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
973 if ( !THREAD_IsWin16( pTask->thdb ) )
975 FIXME( task, "called for Win32 thread (%04x)!\n", pTask->thdb->teb_sel );
976 return;
979 pTask->nEvents++;
981 if ( !THREAD_IsWin16( THREAD_Current() ) )
983 /* wake-up the scheduler waiting in EVENT_WaitNetEvent */
984 EVENT_WakeUp();
989 /***********************************************************************
990 * SetPriority (KERNEL.32)
992 void WINAPI SetPriority16( HTASK16 hTask, INT16 delta )
994 TDB *pTask;
995 INT16 newpriority;
997 if (!hTask) hTask = GetCurrentTask();
998 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
999 newpriority = pTask->priority + delta;
1000 if (newpriority < -32) newpriority = -32;
1001 else if (newpriority > 15) newpriority = 15;
1003 pTask->priority = newpriority + 1;
1004 TASK_UnlinkTask( hTask );
1005 TASK_LinkTask( hTask );
1006 pTask->priority--;
1010 /***********************************************************************
1011 * LockCurrentTask (KERNEL.33)
1013 HTASK16 WINAPI LockCurrentTask16( BOOL16 bLock )
1015 if (bLock) hLockedTask = GetCurrentTask();
1016 else hLockedTask = 0;
1017 return hLockedTask;
1021 /***********************************************************************
1022 * IsTaskLocked (KERNEL.122)
1024 HTASK16 WINAPI IsTaskLocked16(void)
1026 return hLockedTask;
1030 /***********************************************************************
1031 * OldYield (KERNEL.117)
1033 void WINAPI OldYield16(void)
1035 TDB *pCurTask = (TDB *)GlobalLock16( GetCurrentTask() );
1037 if ( !THREAD_IsWin16( THREAD_Current() ) )
1039 FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
1040 return;
1043 if (pCurTask) pCurTask->nEvents++; /* Make sure we get back here */
1044 TASK_YieldToSystem();
1045 if (pCurTask) pCurTask->nEvents--;
1049 /***********************************************************************
1050 * DirectedYield (KERNEL.150)
1052 void WINAPI DirectedYield16( HTASK16 hTask )
1054 TDB *pCurTask = (TDB *)GlobalLock16( GetCurrentTask() );
1056 if ( !THREAD_IsWin16( THREAD_Current() ) )
1058 FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
1059 return;
1062 TRACE(task, "%04x: DirectedYield(%04x)\n", pCurTask->hSelf, hTask );
1064 pCurTask->hYieldTo = hTask;
1065 OldYield16();
1067 TRACE(task, "%04x: back from DirectedYield(%04x)\n", pCurTask->hSelf, hTask );
1070 /***********************************************************************
1071 * Yield16 (KERNEL.29)
1073 void WINAPI Yield16(void)
1075 TDB *pCurTask = (TDB *)GlobalLock16( GetCurrentTask() );
1077 if (pCurTask) pCurTask->hYieldTo = 0;
1078 if (pCurTask && pCurTask->hQueue) Callout.UserYield16();
1079 else OldYield16();
1082 /***********************************************************************
1083 * KERNEL_490 (KERNEL.490)
1085 HTASK16 WINAPI KERNEL_490( HTASK16 someTask )
1087 if ( !someTask ) return 0;
1089 FIXME( task, "(%04x): stub\n", someTask );
1090 return 0;
1093 /***********************************************************************
1094 * MakeProcInstance16 (KERNEL.51)
1096 FARPROC16 WINAPI MakeProcInstance16( FARPROC16 func, HANDLE16 hInstance )
1098 BYTE *thunk,*lfunc;
1099 SEGPTR thunkaddr;
1101 if (!func) {
1102 ERR(task, "Ouch ! MakeProcInstance called with func == NULL !\n");
1103 return (FARPROC16)0; /* Windows seems to do the same */
1105 if (!hInstance) hInstance = CURRENT_DS;
1106 thunkaddr = TASK_AllocThunk( GetCurrentTask() );
1107 if (!thunkaddr) return (FARPROC16)0;
1108 thunk = PTR_SEG_TO_LIN( thunkaddr );
1109 lfunc = PTR_SEG_TO_LIN( func );
1111 TRACE(task, "(%08lx,%04x): got thunk %08lx\n",
1112 (DWORD)func, hInstance, (DWORD)thunkaddr );
1113 if (((lfunc[0]==0x8c) && (lfunc[1]==0xd8)) ||
1114 ((lfunc[0]==0x1e) && (lfunc[1]==0x58))
1116 FIXME(task,"thunk would be useless for %p, overwriting with nop;nop;\n", func );
1117 lfunc[0]=0x90; /* nop */
1118 lfunc[1]=0x90; /* nop */
1121 *thunk++ = 0xb8; /* movw instance, %ax */
1122 *thunk++ = (BYTE)(hInstance & 0xff);
1123 *thunk++ = (BYTE)(hInstance >> 8);
1124 *thunk++ = 0xea; /* ljmp func */
1125 *(DWORD *)thunk = (DWORD)func;
1126 return (FARPROC16)thunkaddr;
1130 /***********************************************************************
1131 * FreeProcInstance16 (KERNEL.52)
1133 void WINAPI FreeProcInstance16( FARPROC16 func )
1135 TRACE(task, "(%08lx)\n", (DWORD)func );
1136 TASK_FreeThunk( GetCurrentTask(), (SEGPTR)func );
1140 /**********************************************************************
1141 * GetCodeHandle (KERNEL.93)
1143 HANDLE16 WINAPI GetCodeHandle16( FARPROC16 proc )
1145 HANDLE16 handle;
1146 BYTE *thunk = (BYTE *)PTR_SEG_TO_LIN( proc );
1148 /* Return the code segment containing 'proc'. */
1149 /* Not sure if this is really correct (shouldn't matter that much). */
1151 /* Check if it is really a thunk */
1152 if ((thunk[0] == 0xb8) && (thunk[3] == 0xea))
1153 handle = GlobalHandle16( thunk[6] + (thunk[7] << 8) );
1154 else
1155 handle = GlobalHandle16( HIWORD(proc) );
1157 return handle;
1160 /**********************************************************************
1161 * GetCodeInfo (KERNEL.104)
1163 VOID WINAPI GetCodeInfo16( FARPROC16 proc, SEGINFO *segInfo )
1165 BYTE *thunk = (BYTE *)PTR_SEG_TO_LIN( proc );
1166 NE_MODULE *pModule = NULL;
1167 SEGTABLEENTRY *pSeg = NULL;
1168 WORD segNr;
1170 /* proc is either a thunk, or else a pair of module handle
1171 and segment number. In the first case, we also need to
1172 extract module and segment number. */
1174 if ((thunk[0] == 0xb8) && (thunk[3] == 0xea))
1176 WORD selector = thunk[6] + (thunk[7] << 8);
1177 pModule = NE_GetPtr( GlobalHandle16( selector ) );
1178 pSeg = pModule? NE_SEG_TABLE( pModule ) : NULL;
1180 if ( pModule )
1181 for ( segNr = 0; segNr < pModule->seg_count; segNr++, pSeg++ )
1182 if ( GlobalHandleToSel16(pSeg->hSeg) == selector )
1183 break;
1185 if ( pModule && segNr >= pModule->seg_count )
1186 pSeg = NULL;
1188 else
1190 pModule = NE_GetPtr( HIWORD( proc ) );
1191 segNr = LOWORD( proc );
1193 if ( pModule && segNr < pModule->seg_count )
1194 pSeg = NE_SEG_TABLE( pModule ) + segNr;
1197 /* fill in segment information */
1199 segInfo->offSegment = pSeg? pSeg->filepos : 0;
1200 segInfo->cbSegment = pSeg? pSeg->size : 0;
1201 segInfo->flags = pSeg? pSeg->flags : 0;
1202 segInfo->cbAlloc = pSeg? pSeg->minsize : 0;
1203 segInfo->h = pSeg? pSeg->hSeg : 0;
1204 segInfo->alignShift = pModule? pModule->alignment : 0;
1208 /**********************************************************************
1209 * DefineHandleTable16 (KERNEL.94)
1211 BOOL16 WINAPI DefineHandleTable16( WORD wOffset )
1213 return TRUE; /* FIXME */
1217 /***********************************************************************
1218 * SetTaskQueue (KERNEL.34)
1220 HQUEUE16 WINAPI SetTaskQueue16( HTASK16 hTask, HQUEUE16 hQueue )
1222 HQUEUE16 hPrev;
1223 TDB *pTask;
1225 if (!hTask) hTask = GetCurrentTask();
1226 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
1228 hPrev = pTask->hQueue;
1229 pTask->hQueue = hQueue;
1231 TIMER_SwitchQueue( hPrev, hQueue );
1233 return hPrev;
1237 /***********************************************************************
1238 * GetTaskQueue (KERNEL.35)
1240 HQUEUE16 WINAPI GetTaskQueue16( HTASK16 hTask )
1242 TDB *pTask;
1244 if (!hTask) hTask = GetCurrentTask();
1245 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
1246 return pTask->hQueue;
1249 /***********************************************************************
1250 * SetThreadQueue (KERNEL.463)
1252 HQUEUE16 WINAPI SetThreadQueue16( DWORD thread, HQUEUE16 hQueue )
1254 THDB *thdb = thread? THREAD_IdToTHDB( thread ) : THREAD_Current();
1255 HQUEUE16 oldQueue = thdb? thdb->teb.queue : 0;
1257 if ( thdb )
1259 thdb->teb.queue = hQueue;
1261 if ( GetTaskQueue16( thdb->process->task ) == oldQueue )
1262 SetTaskQueue16( thdb->process->task, hQueue );
1265 return oldQueue;
1268 /***********************************************************************
1269 * GetThreadQueue (KERNEL.464)
1271 HQUEUE16 WINAPI GetThreadQueue16( DWORD thread )
1273 THDB *thdb = NULL;
1274 if ( !thread )
1275 thdb = THREAD_Current();
1276 else if ( HIWORD(thread) )
1277 thdb = THREAD_IdToTHDB( thread );
1278 else if ( IsTask16( (HTASK16)thread ) )
1279 thdb = ((TDB *)GlobalLock16( (HANDLE16)thread ))->thdb;
1281 return (HQUEUE16)(thdb? thdb->teb.queue : 0);
1284 /***********************************************************************
1285 * SetFastQueue (KERNEL.624)
1287 VOID WINAPI SetFastQueue16( DWORD thread, HANDLE hQueue )
1289 THDB *thdb = NULL;
1290 if ( !thread )
1291 thdb = THREAD_Current();
1292 else if ( HIWORD(thread) )
1293 thdb = THREAD_IdToTHDB( thread );
1294 else if ( IsTask16( (HTASK16)thread ) )
1295 thdb = ((TDB *)GlobalLock16( (HANDLE16)thread ))->thdb;
1297 if ( thdb ) thdb->teb.queue = (HQUEUE16) hQueue;
1300 /***********************************************************************
1301 * GetFastQueue (KERNEL.625)
1303 HANDLE WINAPI GetFastQueue16( void )
1305 THDB *thdb = THREAD_Current();
1306 if (!thdb) return 0;
1308 if (!thdb->teb.queue)
1309 Callout.InitThreadInput16( 0, THREAD_IsWin16(thdb)? 4 : 5 );
1311 if (!thdb->teb.queue)
1312 FIXME( task, "(): should initialize thread-local queue, expect failure!\n" );
1314 return (HANDLE)thdb->teb.queue;
1317 /***********************************************************************
1318 * SwitchStackTo (KERNEL.108)
1320 void WINAPI SwitchStackTo16( WORD seg, WORD ptr, WORD top )
1322 TDB *pTask;
1323 STACK16FRAME *oldFrame, *newFrame;
1324 INSTANCEDATA *pData;
1325 UINT16 copySize;
1327 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
1328 if (!(pData = (INSTANCEDATA *)GlobalLock16( seg ))) return;
1329 TRACE(task, "old=%04x:%04x new=%04x:%04x\n",
1330 SELECTOROF( pTask->thdb->cur_stack ),
1331 OFFSETOF( pTask->thdb->cur_stack ), seg, ptr );
1333 /* Save the old stack */
1335 oldFrame = THREAD_STACK16( pTask->thdb );
1336 /* pop frame + args and push bp */
1337 pData->old_ss_sp = pTask->thdb->cur_stack + sizeof(STACK16FRAME)
1338 + 2 * sizeof(WORD);
1339 *(WORD *)PTR_SEG_TO_LIN(pData->old_ss_sp) = oldFrame->bp;
1340 pData->stacktop = top;
1341 pData->stackmin = ptr;
1342 pData->stackbottom = ptr;
1344 /* Switch to the new stack */
1346 /* Note: we need to take the 3 arguments into account; otherwise,
1347 * the stack will underflow upon return from this function.
1349 copySize = oldFrame->bp - OFFSETOF(pData->old_ss_sp);
1350 copySize += 3 * sizeof(WORD) + sizeof(STACK16FRAME);
1351 pTask->thdb->cur_stack = PTR_SEG_OFF_TO_SEGPTR( seg, ptr - copySize );
1352 newFrame = THREAD_STACK16( pTask->thdb );
1354 /* Copy the stack frame and the local variables to the new stack */
1356 memmove( newFrame, oldFrame, copySize );
1357 newFrame->bp = ptr;
1358 *(WORD *)PTR_SEG_OFF_TO_LIN( seg, ptr ) = 0; /* clear previous bp */
1362 /***********************************************************************
1363 * SwitchStackBack (KERNEL.109)
1365 void WINAPI SwitchStackBack16( CONTEXT *context )
1367 TDB *pTask;
1368 STACK16FRAME *oldFrame, *newFrame;
1369 INSTANCEDATA *pData;
1371 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
1372 if (!(pData = (INSTANCEDATA *)GlobalLock16(SELECTOROF(pTask->thdb->cur_stack))))
1373 return;
1374 if (!pData->old_ss_sp)
1376 WARN( task, "No previous SwitchStackTo\n" );
1377 return;
1379 TRACE(task, "restoring stack %04x:%04x\n",
1380 SELECTOROF(pData->old_ss_sp), OFFSETOF(pData->old_ss_sp) );
1382 oldFrame = THREAD_STACK16( pTask->thdb );
1384 /* Pop bp from the previous stack */
1386 BP_reg(context) = *(WORD *)PTR_SEG_TO_LIN(pData->old_ss_sp);
1387 pData->old_ss_sp += sizeof(WORD);
1389 /* Switch back to the old stack */
1391 pTask->thdb->cur_stack = pData->old_ss_sp - sizeof(STACK16FRAME);
1392 SS_reg(context) = SELECTOROF(pData->old_ss_sp);
1393 ESP_reg(context) = OFFSETOF(pData->old_ss_sp) - sizeof(DWORD); /*ret addr*/
1394 pData->old_ss_sp = 0;
1396 /* Build a stack frame for the return */
1398 newFrame = THREAD_STACK16( pTask->thdb );
1399 newFrame->frame32 = oldFrame->frame32;
1400 if (TRACE_ON(relay))
1402 newFrame->entry_ip = oldFrame->entry_ip;
1403 newFrame->entry_cs = oldFrame->entry_cs;
1408 /***********************************************************************
1409 * GetTaskQueueDS (KERNEL.118)
1411 void WINAPI GetTaskQueueDS16( CONTEXT *context )
1413 DS_reg(context) = GlobalHandleToSel16( GetTaskQueue16(0) );
1417 /***********************************************************************
1418 * GetTaskQueueES (KERNEL.119)
1420 void WINAPI GetTaskQueueES16( CONTEXT *context )
1422 ES_reg(context) = GlobalHandleToSel16( GetTaskQueue16(0) );
1426 /***********************************************************************
1427 * GetCurrentTask (KERNEL.36)
1429 HTASK16 WINAPI GetCurrentTask(void)
1431 return THREAD_InitDone? PROCESS_Current()->task : 0;
1434 DWORD WINAPI WIN16_GetCurrentTask(void)
1436 /* This is the version used by relay code; the first task is */
1437 /* returned in the high word of the result */
1438 return MAKELONG( GetCurrentTask(), hFirstTask );
1442 /***********************************************************************
1443 * GetCurrentPDB (KERNEL.37)
1445 * UNDOC: returns PSP of KERNEL in high word
1447 DWORD WINAPI GetCurrentPDB16(void)
1449 TDB *pTask;
1451 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1452 return MAKELONG(pTask->hPDB, 0); /* FIXME */
1456 /***********************************************************************
1457 * GetInstanceData (KERNEL.54)
1459 INT16 WINAPI GetInstanceData16( HINSTANCE16 instance, WORD buffer, INT16 len )
1461 char *ptr = (char *)GlobalLock16( instance );
1462 if (!ptr || !len) return 0;
1463 if ((int)buffer + len >= 0x10000) len = 0x10000 - buffer;
1464 memcpy( (char *)GlobalLock16(CURRENT_DS) + buffer, ptr + buffer, len );
1465 return len;
1469 /***********************************************************************
1470 * GetExeVersion (KERNEL.105)
1472 WORD WINAPI GetExeVersion16(void)
1474 TDB *pTask;
1476 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1477 return pTask->version;
1481 /***********************************************************************
1482 * SetErrorMode16 (KERNEL.107)
1484 UINT16 WINAPI SetErrorMode16( UINT16 mode )
1486 TDB *pTask;
1487 UINT16 oldMode;
1489 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1490 oldMode = pTask->error_mode;
1491 pTask->error_mode = mode;
1492 pTask->thdb->process->error_mode = mode;
1493 return oldMode;
1497 /***********************************************************************
1498 * SetErrorMode32 (KERNEL32.486)
1500 UINT WINAPI SetErrorMode( UINT mode )
1502 return SetErrorMode16( (UINT16)mode );
1506 /***********************************************************************
1507 * GetDOSEnvironment (KERNEL.131)
1509 SEGPTR WINAPI GetDOSEnvironment16(void)
1511 TDB *pTask;
1513 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1514 return PTR_SEG_OFF_TO_SEGPTR( pTask->pdb.environment, 0 );
1518 /***********************************************************************
1519 * GetNumTasks (KERNEL.152)
1521 UINT16 WINAPI GetNumTasks16(void)
1523 return nTaskCount;
1527 /***********************************************************************
1528 * GetTaskDS (KERNEL.155)
1530 * Note: this function apparently returns a DWORD with LOWORD == HIWORD.
1531 * I don't think we need to bother with this.
1533 HINSTANCE16 WINAPI GetTaskDS16(void)
1535 TDB *pTask;
1537 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1538 return pTask->hInstance;
1541 /***********************************************************************
1542 * GetDummyModuleHandleDS (KERNEL.602)
1544 VOID WINAPI GetDummyModuleHandleDS16( CONTEXT *context )
1546 TDB *pTask;
1547 WORD selector;
1549 AX_reg( context ) = 0;
1550 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
1551 if (!(pTask->flags & TDBF_WIN32)) return;
1553 selector = GlobalHandleToSel16( pTask->hModule );
1554 DS_reg( context ) = selector;
1555 AX_reg( context ) = selector;
1558 /***********************************************************************
1559 * IsTask (KERNEL.320)
1561 BOOL16 WINAPI IsTask16( HTASK16 hTask )
1563 TDB *pTask;
1565 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return FALSE;
1566 if (GlobalSize16( hTask ) < sizeof(TDB)) return FALSE;
1567 return (pTask->magic == TDB_MAGIC);
1571 /***********************************************************************
1572 * SetTaskSignalProc (KERNEL.38)
1574 * Real 16-bit interface is provided by the THUNK_SetTaskSignalProc.
1576 FARPROC16 WINAPI SetTaskSignalProc( HTASK16 hTask, FARPROC16 proc )
1578 TDB *pTask;
1579 FARPROC16 oldProc;
1581 if (!hTask) hTask = GetCurrentTask();
1582 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return NULL;
1583 oldProc = (FARPROC16)pTask->userhandler;
1584 pTask->userhandler = (USERSIGNALPROC)proc;
1585 return oldProc;
1589 /***********************************************************************
1590 * SetSigHandler (KERNEL.140)
1592 WORD WINAPI SetSigHandler16( FARPROC16 newhandler, FARPROC16* oldhandler,
1593 UINT16 *oldmode, UINT16 newmode, UINT16 flag )
1595 FIXME(task,"(%p,%p,%p,%d,%d), unimplemented.\n",
1596 newhandler,oldhandler,oldmode,newmode,flag );
1598 if (flag != 1) return 0;
1599 if (!newmode) newhandler = NULL; /* Default handler */
1600 if (newmode != 4)
1602 TDB *pTask;
1604 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1605 if (oldmode) *oldmode = pTask->signal_flags;
1606 pTask->signal_flags = newmode;
1607 if (oldhandler) *oldhandler = pTask->sighandler;
1608 pTask->sighandler = newhandler;
1610 return 0;
1614 /***********************************************************************
1615 * GlobalNotify (KERNEL.154)
1617 * Note that GlobalNotify does _not_ return the old NotifyProc
1618 * -- contrary to LocalNotify !!
1620 VOID WINAPI GlobalNotify16( FARPROC16 proc )
1622 TDB *pTask;
1624 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
1625 pTask->discardhandler = proc;
1629 /***********************************************************************
1630 * GetExePtr (KERNEL.133)
1632 static HMODULE16 GetExePtrHelper( HANDLE16 handle, HTASK16 *hTask )
1634 char *ptr;
1635 HANDLE16 owner;
1637 /* Check for module handle */
1639 if (!(ptr = GlobalLock16( handle ))) return 0;
1640 if (((NE_MODULE *)ptr)->magic == IMAGE_OS2_SIGNATURE) return handle;
1642 /* Search for this handle inside all tasks */
1644 *hTask = hFirstTask;
1645 while (*hTask)
1647 TDB *pTask = (TDB *)GlobalLock16( *hTask );
1648 if ((*hTask == handle) ||
1649 (pTask->hInstance == handle) ||
1650 (pTask->hQueue == handle) ||
1651 (pTask->hPDB == handle)) return pTask->hModule;
1652 *hTask = pTask->hNext;
1655 /* Check the owner for module handle */
1657 owner = FarGetOwner16( handle );
1658 if (!(ptr = GlobalLock16( owner ))) return 0;
1659 if (((NE_MODULE *)ptr)->magic == IMAGE_OS2_SIGNATURE) return owner;
1661 /* Search for the owner inside all tasks */
1663 *hTask = hFirstTask;
1664 while (*hTask)
1666 TDB *pTask = (TDB *)GlobalLock16( *hTask );
1667 if ((*hTask == owner) ||
1668 (pTask->hInstance == owner) ||
1669 (pTask->hQueue == owner) ||
1670 (pTask->hPDB == owner)) return pTask->hModule;
1671 *hTask = pTask->hNext;
1674 return 0;
1677 HMODULE16 WINAPI GetExePtr( HANDLE16 handle )
1679 HTASK16 dummy;
1680 return GetExePtrHelper( handle, &dummy );
1683 void WINAPI WIN16_GetExePtr( CONTEXT *context )
1685 WORD *stack = PTR_SEG_OFF_TO_LIN(SS_reg(context), SP_reg(context));
1686 HANDLE16 handle = (HANDLE16)stack[2];
1687 HTASK16 hTask = 0;
1688 HMODULE16 hModule;
1690 hModule = GetExePtrHelper( handle, &hTask );
1692 AX_reg(context) = CX_reg(context) = hModule;
1693 if (hTask) ES_reg(context) = hTask;
1696 /***********************************************************************
1697 * TaskFirst (TOOLHELP.63)
1699 BOOL16 WINAPI TaskFirst16( TASKENTRY *lpte )
1701 lpte->hNext = hFirstTask;
1702 return TaskNext16( lpte );
1706 /***********************************************************************
1707 * TaskNext (TOOLHELP.64)
1709 BOOL16 WINAPI TaskNext16( TASKENTRY *lpte )
1711 TDB *pTask;
1712 INSTANCEDATA *pInstData;
1714 TRACE(toolhelp, "(%p): task=%04x\n", lpte, lpte->hNext );
1715 if (!lpte->hNext) return FALSE;
1716 pTask = (TDB *)GlobalLock16( lpte->hNext );
1717 if (!pTask || pTask->magic != TDB_MAGIC) return FALSE;
1718 pInstData = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( pTask->hInstance, 0 );
1719 lpte->hTask = lpte->hNext;
1720 lpte->hTaskParent = pTask->hParent;
1721 lpte->hInst = pTask->hInstance;
1722 lpte->hModule = pTask->hModule;
1723 lpte->wSS = SELECTOROF( pTask->thdb->cur_stack );
1724 lpte->wSP = OFFSETOF( pTask->thdb->cur_stack );
1725 lpte->wStackTop = pInstData->stacktop;
1726 lpte->wStackMinimum = pInstData->stackmin;
1727 lpte->wStackBottom = pInstData->stackbottom;
1728 lpte->wcEvents = pTask->nEvents;
1729 lpte->hQueue = pTask->hQueue;
1730 strncpy( lpte->szModule, pTask->module_name, 8 );
1731 lpte->szModule[8] = '\0';
1732 lpte->wPSPOffset = 0x100; /*??*/
1733 lpte->hNext = pTask->hNext;
1734 return TRUE;
1738 /***********************************************************************
1739 * TaskFindHandle (TOOLHELP.65)
1741 BOOL16 WINAPI TaskFindHandle16( TASKENTRY *lpte, HTASK16 hTask )
1743 lpte->hNext = hTask;
1744 return TaskNext16( lpte );
1748 /***********************************************************************
1749 * GetAppCompatFlags16 (KERNEL.354)
1751 DWORD WINAPI GetAppCompatFlags16( HTASK16 hTask )
1753 return GetAppCompatFlags( hTask );
1757 /***********************************************************************
1758 * GetAppCompatFlags32 (USER32.206)
1760 DWORD WINAPI GetAppCompatFlags( HTASK hTask )
1762 TDB *pTask;
1764 if (!hTask) hTask = GetCurrentTask();
1765 if (!(pTask=(TDB *)GlobalLock16( (HTASK16)hTask ))) return 0;
1766 if (GlobalSize16(hTask) < sizeof(TDB)) return 0;
1767 return pTask->compat_flags;