Make all structures packed and removal of some WINE_PACKED.
[wine/testsucceed.git] / loader / task.c
blob97f2ab5c3ff84839525d25abedc0ca85383d0185
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 DECLARE_DEBUG_CHANNEL(relay)
42 DECLARE_DEBUG_CHANNEL(task)
43 DECLARE_DEBUG_CHANNEL(toolhelp)
45 /* Min. number of thunks allocated when creating a new segment */
46 #define MIN_THUNKS 32
48 /* Pointer to function to switch to a larger stack */
49 int (*IF1632_CallLargeStack)( int (*func)(), void *arg ) = NULL;
51 /* Pointer to debugger callback routine */
52 void (*TASK_AddTaskEntryBreakpoint)( HTASK16 hTask ) = NULL;
54 static THHOOK DefaultThhook = { 0 };
55 THHOOK *pThhook = &DefaultThhook;
57 #define hCurrentTask (pThhook->CurTDB)
58 #define hFirstTask (pThhook->HeadTDB)
59 #define hLockedTask (pThhook->LockTDB)
61 static HTASK16 hTaskToKill = 0;
62 static UINT16 nTaskCount = 0;
64 static void TASK_YieldToSystem( void );
66 extern BOOL THREAD_InitDone;
69 /***********************************************************************
70 * TASK_InstallTHHook
72 void TASK_InstallTHHook( THHOOK *pNewThhook )
74 THHOOK *pOldThhook = pThhook;
76 pThhook = pNewThhook? pNewThhook : &DefaultThhook;
78 *pThhook = *pOldThhook;
81 /***********************************************************************
82 * TASK_GetNextTask
84 HTASK16 TASK_GetNextTask( HTASK16 hTask )
86 TDB* pTask = (TDB*)GlobalLock16(hTask);
88 if (pTask->hNext) return pTask->hNext;
89 return (hFirstTask != hTask) ? hFirstTask : 0;
92 /***********************************************************************
93 * TASK_LinkTask
95 static void TASK_LinkTask( HTASK16 hTask )
97 HTASK16 *prevTask;
98 TDB *pTask;
100 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
101 prevTask = &hFirstTask;
102 while (*prevTask)
104 TDB *prevTaskPtr = (TDB *)GlobalLock16( *prevTask );
105 if (prevTaskPtr->priority >= pTask->priority) break;
106 prevTask = &prevTaskPtr->hNext;
108 pTask->hNext = *prevTask;
109 *prevTask = hTask;
110 nTaskCount++;
114 /***********************************************************************
115 * TASK_UnlinkTask
117 static void TASK_UnlinkTask( HTASK16 hTask )
119 HTASK16 *prevTask;
120 TDB *pTask;
122 prevTask = &hFirstTask;
123 while (*prevTask && (*prevTask != hTask))
125 pTask = (TDB *)GlobalLock16( *prevTask );
126 prevTask = &pTask->hNext;
128 if (*prevTask)
130 pTask = (TDB *)GlobalLock16( *prevTask );
131 *prevTask = pTask->hNext;
132 pTask->hNext = 0;
133 nTaskCount--;
138 /***********************************************************************
139 * TASK_CreateThunks
141 * Create a thunk free-list in segment 'handle', starting from offset 'offset'
142 * and containing 'count' entries.
144 static void TASK_CreateThunks( HGLOBAL16 handle, WORD offset, WORD count )
146 int i;
147 WORD free;
148 THUNKS *pThunk;
150 pThunk = (THUNKS *)((BYTE *)GlobalLock16( handle ) + offset);
151 pThunk->next = 0;
152 pThunk->magic = THUNK_MAGIC;
153 pThunk->free = (int)&pThunk->thunks - (int)pThunk;
154 free = pThunk->free;
155 for (i = 0; i < count-1; i++)
157 free += 8; /* Offset of next thunk */
158 pThunk->thunks[4*i] = free;
160 pThunk->thunks[4*i] = 0; /* Last thunk */
164 /***********************************************************************
165 * TASK_AllocThunk
167 * Allocate a thunk for MakeProcInstance().
169 static SEGPTR TASK_AllocThunk( HTASK16 hTask )
171 TDB *pTask;
172 THUNKS *pThunk;
173 WORD sel, base;
175 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
176 sel = pTask->hCSAlias;
177 pThunk = &pTask->thunks;
178 base = (int)pThunk - (int)pTask;
179 while (!pThunk->free)
181 sel = pThunk->next;
182 if (!sel) /* Allocate a new segment */
184 sel = GLOBAL_Alloc( GMEM_FIXED, sizeof(THUNKS) + (MIN_THUNKS-1)*8,
185 pTask->hPDB, TRUE, FALSE, FALSE );
186 if (!sel) return (SEGPTR)0;
187 TASK_CreateThunks( sel, 0, MIN_THUNKS );
188 pThunk->next = sel;
190 pThunk = (THUNKS *)GlobalLock16( sel );
191 base = 0;
193 base += pThunk->free;
194 pThunk->free = *(WORD *)((BYTE *)pThunk + pThunk->free);
195 return PTR_SEG_OFF_TO_SEGPTR( sel, base );
199 /***********************************************************************
200 * TASK_FreeThunk
202 * Free a MakeProcInstance() thunk.
204 static BOOL TASK_FreeThunk( HTASK16 hTask, SEGPTR thunk )
206 TDB *pTask;
207 THUNKS *pThunk;
208 WORD sel, base;
210 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
211 sel = pTask->hCSAlias;
212 pThunk = &pTask->thunks;
213 base = (int)pThunk - (int)pTask;
214 while (sel && (sel != HIWORD(thunk)))
216 sel = pThunk->next;
217 pThunk = (THUNKS *)GlobalLock16( sel );
218 base = 0;
220 if (!sel) return FALSE;
221 *(WORD *)((BYTE *)pThunk + LOWORD(thunk) - base) = pThunk->free;
222 pThunk->free = LOWORD(thunk) - base;
223 return TRUE;
227 /***********************************************************************
228 * TASK_CallToStart
230 * 32-bit entry point for a new task. This function is responsible for
231 * setting up the registers and jumping to the 16-bit entry point.
233 static void TASK_CallToStart(void)
235 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
236 NE_MODULE *pModule = NE_GetPtr( pTask->hModule );
237 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
239 SET_CUR_THREAD( pTask->thdb );
240 CLIENT_InitThread();
242 /* Terminate the stack frame chain */
243 memset(THREAD_STACK16( pTask->thdb ), '\0', sizeof(STACK16FRAME));
245 /* Call USER signal proc */
246 PROCESS_CallUserSignalProc( USIG_THREAD_INIT, 0, 0 ); /* for initial thread */
247 PROCESS_CallUserSignalProc( USIG_PROCESS_INIT, 0, 0 );
248 PROCESS_CallUserSignalProc( USIG_PROCESS_LOADED, 0, 0 );
249 PROCESS_CallUserSignalProc( USIG_PROCESS_RUNNING, 0, 0 );
251 if (pModule->flags & NE_FFLAGS_WIN32)
253 ERR( task, "Called for Win32 task!\n" );
254 ExitProcess( 1 );
256 else if (pModule->dos_image)
258 DOSVM_Enter( NULL );
259 ExitProcess( 0 );
261 else
263 /* Registers at initialization must be:
264 * ax zero
265 * bx stack size in bytes
266 * cx heap size in bytes
267 * si previous app instance
268 * di current app instance
269 * bp zero
270 * es selector to the PSP
271 * ds dgroup of the application
272 * ss stack selector
273 * sp top of the stack
275 CONTEXT context;
277 memset( &context, 0, sizeof(context) );
278 CS_reg(&context) = GlobalHandleToSel16(pSegTable[pModule->cs - 1].hSeg);
279 DS_reg(&context) = GlobalHandleToSel16(pSegTable[pModule->dgroup - 1].hSeg);
280 ES_reg(&context) = pTask->hPDB;
281 EIP_reg(&context) = pModule->ip;
282 EBX_reg(&context) = pModule->stack_size;
283 ECX_reg(&context) = pModule->heap_size;
284 EDI_reg(&context) = context.SegDs;
286 TRACE(task, "Starting main program: cs:ip=%04lx:%04x ds=%04lx ss:sp=%04x:%04x\n",
287 CS_reg(&context), IP_reg(&context), DS_reg(&context),
288 SELECTOROF(pTask->thdb->cur_stack),
289 OFFSETOF(pTask->thdb->cur_stack) );
291 Callbacks->CallRegisterShortProc( &context, 0 );
292 /* This should never return */
293 ERR( task, "Main program returned! (should never happen)\n" );
294 ExitProcess( 1 );
299 /***********************************************************************
300 * TASK_Create
302 * NOTE: This routine might be called by a Win32 thread. We don't have
303 * any real problems with that, since we operated merely on a private
304 * TDB structure that is not yet linked into the task list.
306 BOOL TASK_Create( THDB *thdb, NE_MODULE *pModule, HINSTANCE16 hInstance,
307 HINSTANCE16 hPrevInstance, UINT16 cmdShow)
309 HTASK16 hTask;
310 TDB *pTask;
311 LPSTR cmd_line;
312 WORD sp;
313 char *stack32Top;
314 char name[10];
315 STACK16FRAME *frame16;
316 STACK32FRAME *frame32;
317 PDB *pdb32 = thdb->process;
318 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
320 /* Allocate the task structure */
322 hTask = GLOBAL_Alloc( GMEM_FIXED | GMEM_ZEROINIT, sizeof(TDB),
323 pModule->self, FALSE, FALSE, FALSE );
324 if (!hTask) return FALSE;
325 pTask = (TDB *)GlobalLock16( hTask );
327 /* Fill the task structure */
329 pTask->nEvents = 0;
330 pTask->hSelf = hTask;
331 pTask->flags = 0;
333 if (pModule->flags & NE_FFLAGS_WIN32)
334 pTask->flags |= TDBF_WIN32;
335 if (pModule->lpDosTask)
336 pTask->flags |= TDBF_WINOLDAP;
338 pTask->version = pModule->expected_version;
339 pTask->hInstance = hInstance? hInstance : pModule->self;
340 pTask->hPrevInstance = hPrevInstance;
341 pTask->hModule = pModule->self;
342 pTask->hParent = GetCurrentTask();
343 pTask->magic = TDB_MAGIC;
344 pTask->nCmdShow = cmdShow;
345 pTask->thdb = thdb;
346 pTask->curdrive = DRIVE_GetCurrentDrive() | 0x80;
347 strcpy( pTask->curdir, "\\" );
348 lstrcpynA( pTask->curdir + 1, DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() ),
349 sizeof(pTask->curdir) - 1 );
351 /* Create the thunks block */
353 TASK_CreateThunks( hTask, (int)&pTask->thunks - (int)pTask, 7 );
355 /* Copy the module name */
357 GetModuleName16( pModule->self, name, sizeof(name) );
358 strncpy( pTask->module_name, name, sizeof(pTask->module_name) );
360 /* Allocate a selector for the PDB */
362 pTask->hPDB = GLOBAL_CreateBlock( GMEM_FIXED, &pTask->pdb, sizeof(PDB16),
363 pModule->self, FALSE, FALSE, FALSE, NULL );
365 /* Fill the PDB */
367 pTask->pdb.int20 = 0x20cd;
368 pTask->pdb.dispatcher[0] = 0x9a; /* ljmp */
369 PUT_DWORD(&pTask->pdb.dispatcher[1], (DWORD)NE_GetEntryPoint(
370 GetModuleHandle16("KERNEL"), 102 )); /* KERNEL.102 is DOS3Call() */
371 pTask->pdb.savedint22 = INT_GetPMHandler( 0x22 );
372 pTask->pdb.savedint23 = INT_GetPMHandler( 0x23 );
373 pTask->pdb.savedint24 = INT_GetPMHandler( 0x24 );
374 pTask->pdb.fileHandlesPtr =
375 PTR_SEG_OFF_TO_SEGPTR( GlobalHandleToSel16(pTask->hPDB),
376 (int)&((PDB16 *)0)->fileHandles );
377 pTask->pdb.hFileHandles = 0;
378 memset( pTask->pdb.fileHandles, 0xff, sizeof(pTask->pdb.fileHandles) );
379 pTask->pdb.environment = pdb32->env_db->env_sel;
380 pTask->pdb.nbFiles = 20;
382 /* Fill the command line */
384 cmd_line = pdb32->env_db->cmd_line;
385 while (*cmd_line && (*cmd_line != ' ') && (*cmd_line != '\t')) cmd_line++;
386 while ((*cmd_line == ' ') || (*cmd_line == '\t')) cmd_line++;
387 lstrcpynA( pTask->pdb.cmdLine+1, cmd_line, sizeof(pTask->pdb.cmdLine)-1);
388 pTask->pdb.cmdLine[0] = strlen( pTask->pdb.cmdLine + 1 );
390 /* Get the compatibility flags */
392 pTask->compat_flags = GetProfileIntA( "Compatibility", name, 0 );
394 /* Allocate a code segment alias for the TDB */
396 pTask->hCSAlias = GLOBAL_CreateBlock( GMEM_FIXED, (void *)pTask,
397 sizeof(TDB), pTask->hPDB, TRUE,
398 FALSE, FALSE, NULL );
400 /* Set the owner of the environment block */
402 FarSetOwner16( pTask->pdb.environment, pTask->hPDB );
404 /* Default DTA overwrites command-line */
406 pTask->dta = PTR_SEG_OFF_TO_SEGPTR( pTask->hPDB,
407 (int)&pTask->pdb.cmdLine - (int)&pTask->pdb );
409 /* If we have a DGROUP/hInstance, use it for 16-bit stack */
411 if ( hInstance )
413 if (!(sp = pModule->sp))
414 sp = pSegTable[pModule->ss-1].minsize + pModule->stack_size;
415 sp &= ~1; sp -= sizeof(STACK16FRAME);
416 pTask->thdb->cur_stack = PTR_SEG_OFF_TO_SEGPTR( hInstance, sp );
419 /* Create the 16-bit stack frame */
421 pTask->thdb->cur_stack -= sizeof(STACK16FRAME);
422 frame16 = (STACK16FRAME *)PTR_SEG_TO_LIN( pTask->thdb->cur_stack );
423 frame16->ebp = OFFSETOF( pTask->thdb->cur_stack ) + (int)&((STACK16FRAME *)0)->bp;
424 frame16->bp = LOWORD(frame16->ebp);
425 frame16->ds = frame16->es = hInstance;
426 frame16->fs = 0;
427 frame16->entry_point = 0;
428 frame16->entry_cs = 0;
429 frame16->mutex_count = 1; /* TASK_Reschedule is called from 16-bit code */
430 /* The remaining fields will be initialized in TASK_Reschedule */
432 /* Create the 32-bit stack frame */
434 stack32Top = (char*)pTask->thdb->teb.stack_top;
435 frame16->frame32 = frame32 = (STACK32FRAME *)stack32Top - 1;
436 frame32->frame16 = pTask->thdb->cur_stack + sizeof(STACK16FRAME);
437 frame32->edi = 0;
438 frame32->esi = 0;
439 frame32->edx = 0;
440 frame32->ecx = 0;
441 frame32->ebx = 0;
442 frame32->retaddr = (DWORD)TASK_CallToStart;
443 /* The remaining fields will be initialized in TASK_Reschedule */
445 /* Enter task handle into thread and process */
447 pTask->thdb->teb.htask16 = pTask->thdb->process->task = hTask;
449 TRACE(task, "module='%s' cmdline='%s' task=%04x\n",
450 name, cmd_line, hTask );
452 return TRUE;
455 /***********************************************************************
456 * TASK_StartTask
458 * NOTE: This routine might be called by a Win32 thread. Thus, we need
459 * to be careful to protect global data structures. We do this
460 * by entering the Win16Lock while linking the task into the
461 * global task list.
463 void TASK_StartTask( HTASK16 hTask )
465 TDB *pTask = (TDB *)GlobalLock16( hTask );
466 if ( !pTask ) return;
468 /* Add the task to the linked list */
470 SYSLEVEL_EnterWin16Lock();
471 TASK_LinkTask( hTask );
472 SYSLEVEL_LeaveWin16Lock();
474 TRACE(task, "linked task %04x\n", hTask );
476 /* If requested, add entry point breakpoint */
478 if ( TASK_AddTaskEntryBreakpoint )
479 TASK_AddTaskEntryBreakpoint( hTask );
481 /* Get the task up and running. */
483 if ( THREAD_IsWin16( pTask->thdb ) )
485 pTask->nEvents++;
487 /* If we ourselves are a 16-bit task, we simply Yield().
488 If we are 32-bit however, we need to signal the scheduler. */
490 if ( THREAD_IsWin16( THREAD_Current() ) )
491 OldYield16();
492 else
493 EVENT_WakeUp();
495 else
497 /* To start a 32-bit task, we spawn its initial thread. */
499 SYSDEPS_SpawnThread( pTask->thdb );
504 /***********************************************************************
505 * TASK_DeleteTask
507 static void TASK_DeleteTask( HTASK16 hTask )
509 TDB *pTask;
510 HGLOBAL16 hPDB;
512 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
513 hPDB = pTask->hPDB;
515 pTask->magic = 0xdead; /* invalidate signature */
517 /* Delete the Win32 part of the task */
519 /* PROCESS_FreePDB( pTask->thdb->process ); FIXME */
520 /* K32OBJ_DecCount( &pTask->thdb->header ); FIXME */
522 /* Free the selector aliases */
524 GLOBAL_FreeBlock( pTask->hCSAlias );
525 GLOBAL_FreeBlock( pTask->hPDB );
527 /* Free the task module */
529 FreeModule16( pTask->hModule );
531 /* Free the task structure itself */
533 GlobalFree16( hTask );
535 /* Free all memory used by this task (including the 32-bit stack, */
536 /* the environment block and the thunk segments). */
538 GlobalFreeAll16( hPDB );
541 /***********************************************************************
542 * TASK_KillTask
544 void TASK_KillTask( HTASK16 hTask )
546 TDB *pTask;
548 /* Enter the Win16Lock to protect global data structures */
549 SYSLEVEL_EnterWin16Lock();
551 if ( !hTask ) hTask = GetCurrentTask();
552 pTask = (TDB *)GlobalLock16( hTask );
553 if ( !pTask )
555 SYSLEVEL_LeaveWin16Lock();
556 return;
559 TRACE(task, "Killing task %04x\n", hTask );
561 /* Delete active sockets */
563 if( pTask->pwsi )
564 WINSOCK_DeleteTaskWSI( pTask, pTask->pwsi );
566 #ifdef MZ_SUPPORTED
568 /* Kill DOS VM task */
569 NE_MODULE *pModule = NE_GetPtr( pTask->hModule );
570 if ( pModule->lpDosTask )
571 MZ_KillModule( pModule->lpDosTask );
573 #endif
575 /* Perform USER cleanup */
577 if (pTask->userhandler)
578 pTask->userhandler( hTask, USIG16_TERMINATION, 0,
579 pTask->hInstance, pTask->hQueue );
581 PROCESS_CallUserSignalProc( USIG_PROCESS_EXIT, 0, 0 );
582 PROCESS_CallUserSignalProc( USIG_THREAD_EXIT, 0, 0 ); /* FIXME */
583 PROCESS_CallUserSignalProc( USIG_PROCESS_DESTROY, 0, 0 );
585 if (nTaskCount <= 1)
587 TRACE(task, "this is the last task, exiting\n" );
588 USER_ExitWindows();
591 /* FIXME: Hack! Send a message to the initial task so that
592 * the GetMessage wakes up and the initial task can check whether
593 * it is the only remaining one and terminate itself ...
594 * The initial task should probably install hooks or something
595 * to get informed about task termination :-/
597 Callout.PostAppMessage16( PROCESS_Initial()->task, WM_NULL, 0, 0 );
599 /* Remove the task from the list to be sure we never switch back to it */
600 TASK_UnlinkTask( hTask );
601 if( nTaskCount )
603 TDB* p = (TDB *)GlobalLock16( hFirstTask );
604 while( p )
606 if( p->hYieldTo == hTask ) p->hYieldTo = 0;
607 p = (TDB *)GlobalLock16( p->hNext );
611 pTask->nEvents = 0;
613 if ( hLockedTask == hTask )
614 hLockedTask = 0;
616 if ( hTaskToKill && ( hTaskToKill != hCurrentTask ) )
618 /* If another task is already marked for destruction, */
619 /* we can kill it now, as we are in another context. */
620 TASK_DeleteTask( hTaskToKill );
621 hTaskToKill = 0;
625 * If hTask is not the task currently scheduled by the Win16
626 * scheduler, we simply delete it; otherwise we mark it for
627 * destruction. Note that if the current task is a 32-bit
628 * one, hCurrentTask is *different* from GetCurrentTask()!
630 if ( hTask == hCurrentTask )
632 assert( hTaskToKill == 0 || hTaskToKill == hCurrentTask );
633 hTaskToKill = hCurrentTask;
635 else
636 TASK_DeleteTask( hTask );
638 SYSLEVEL_LeaveWin16Lock();
642 /***********************************************************************
643 * TASK_KillCurrentTask
645 * Kill the currently running task. As it's not possible to kill the
646 * current task like this, it is simply marked for destruction, and will
647 * be killed when either TASK_Reschedule or this function is called again
648 * in the context of another task.
650 void TASK_KillCurrentTask( INT16 exitCode )
652 if ( !THREAD_IsWin16( THREAD_Current() ) )
654 FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
655 return;
658 assert(hCurrentTask == GetCurrentTask());
660 TRACE(task, "Killing current task %04x\n", hCurrentTask );
662 TASK_KillTask( 0 );
664 TASK_YieldToSystem();
666 /* We should never return from this Yield() */
668 ERR(task,"Return of the living dead %04x!!!\n", hCurrentTask);
669 exit(1);
672 /***********************************************************************
673 * TASK_Reschedule
675 * This is where all the magic of task-switching happens!
677 * Note: This function should only be called via the TASK_YieldToSystem()
678 * wrapper, to make sure that all the context is saved correctly.
680 * It must not call functions that may yield control.
682 BOOL TASK_Reschedule(void)
684 TDB *pOldTask = NULL, *pNewTask;
685 HTASK16 hTask = 0;
686 STACK16FRAME *newframe16;
687 BOOL pending = FALSE;
689 /* Get the initial task up and running */
690 if (!hCurrentTask && GetCurrentTask())
692 /* We need to remove one pair of stackframes (exept for Winelib) */
693 STACK16FRAME *oldframe16 = CURRENT_STACK16;
694 STACK32FRAME *oldframe32 = oldframe16->frame32;
695 STACK16FRAME *newframe16 = PTR_SEG_TO_LIN( oldframe32->frame16 );
696 STACK32FRAME *newframe32 = newframe16->frame32;
697 if (newframe32)
699 newframe16->entry_ip = oldframe16->entry_ip;
700 newframe16->entry_cs = oldframe16->entry_cs;
701 newframe16->ip = oldframe16->ip;
702 newframe16->cs = oldframe16->cs;
703 newframe32->ebp = oldframe32->ebp;
704 newframe32->restore_addr = oldframe32->restore_addr;
705 newframe32->codeselector = oldframe32->codeselector;
707 THREAD_Current()->cur_stack = oldframe32->frame16;
710 hCurrentTask = GetCurrentTask();
711 pNewTask = (TDB *)GlobalLock16( hCurrentTask );
712 pNewTask->ss_sp = pNewTask->thdb->cur_stack;
713 return FALSE;
716 /* NOTE: As we are entered from 16-bit code, we hold the Win16Lock.
717 We hang onto it thoughout most of this routine, so that accesses
718 to global variables (most notably the task list) are protected. */
719 assert(hCurrentTask == GetCurrentTask());
721 TRACE(task, "entered with hTask %04x (pid %d)\n", hCurrentTask, getpid());
723 #ifdef CONFIG_IPC
724 /* FIXME: What about the Win16Lock ??? */
725 dde_reschedule();
726 #endif
727 /* First check if there's a task to kill */
729 if (hTaskToKill && (hTaskToKill != hCurrentTask))
731 TASK_DeleteTask( hTaskToKill );
732 hTaskToKill = 0;
735 /* Find a task to yield to */
737 pOldTask = (TDB *)GlobalLock16( hCurrentTask );
738 if (pOldTask && pOldTask->hYieldTo)
740 /* check for DirectedYield() */
742 hTask = pOldTask->hYieldTo;
743 pNewTask = (TDB *)GlobalLock16( hTask );
744 if( !pNewTask || !pNewTask->nEvents) hTask = 0;
745 pOldTask->hYieldTo = 0;
748 /* extract hardware events only! */
750 if (!hTask) pending = EVENT_WaitNetEvent( FALSE, TRUE );
752 while (!hTask)
754 /* Find a task that has an event pending */
756 hTask = hFirstTask;
757 while (hTask)
759 pNewTask = (TDB *)GlobalLock16( hTask );
761 TRACE(task, "\ttask = %04x, events = %i\n", hTask, pNewTask->nEvents);
763 if (pNewTask->nEvents) break;
764 hTask = pNewTask->hNext;
766 if (hLockedTask && (hTask != hLockedTask)) hTask = 0;
767 if (hTask) break;
769 /* If a non-hardware event is pending, return to TASK_YieldToSystem
770 temporarily to process it safely */
771 if (pending) return TRUE;
773 /* No task found, wait for some events to come in */
775 /* NOTE: We release the Win16Lock while waiting for events. This is to enable
776 Win32 threads to thunk down to 16-bit temporarily. Since Win16
777 tasks won't execute and Win32 threads are not allowed to enter
778 TASK_Reschedule anyway, there should be no re-entrancy problem ... */
780 SYSLEVEL_ReleaseWin16Lock();
781 pending = EVENT_WaitNetEvent( TRUE, TRUE );
782 SYSLEVEL_RestoreWin16Lock();
785 if (hTask == hCurrentTask)
787 /* Allow Win32 threads to thunk down even while a Win16 task is
788 in a tight PeekMessage() or Yield() loop ... */
789 SYSLEVEL_ReleaseWin16Lock();
790 SYSLEVEL_RestoreWin16Lock();
792 TRACE(task, "returning to the current task(%04x)\n", hTask );
793 return FALSE; /* Nothing to do */
795 pNewTask = (TDB *)GlobalLock16( hTask );
796 TRACE(task, "Switching to task %04x (%.8s)\n",
797 hTask, pNewTask->module_name );
799 /* Make the task the last in the linked list (round-robin scheduling) */
801 pNewTask->priority++;
802 TASK_UnlinkTask( hTask );
803 TASK_LinkTask( hTask );
804 pNewTask->priority--;
806 /* Finish initializing the new task stack if necessary */
808 newframe16 = THREAD_STACK16( pNewTask->thdb );
809 if (!newframe16->entry_cs)
811 STACK16FRAME *oldframe16 = CURRENT_STACK16;
812 STACK32FRAME *oldframe32 = oldframe16->frame32;
813 STACK32FRAME *newframe32 = newframe16->frame32;
814 newframe16->entry_ip = oldframe16->entry_ip;
815 newframe16->entry_cs = oldframe16->entry_cs;
816 newframe16->ip = oldframe16->ip;
817 newframe16->cs = oldframe16->cs;
818 newframe32->ebp = oldframe32->ebp;
819 newframe32->restore_addr = oldframe32->restore_addr;
820 newframe32->codeselector = oldframe32->codeselector;
823 /* Switch to the new stack */
825 /* NOTE: We need to release/restore the Win16Lock, as the task
826 switched to might be at another recursion level than
827 the old task ... */
829 SYSLEVEL_ReleaseWin16Lock();
831 hCurrentTask = hTask;
832 SET_CUR_THREAD( pNewTask->thdb );
833 pNewTask->ss_sp = pNewTask->thdb->cur_stack;
835 SYSLEVEL_RestoreWin16Lock();
837 return FALSE;
841 /***********************************************************************
842 * TASK_YieldToSystem
844 * Scheduler interface, this way we ensure that all "unsafe" events are
845 * processed outside the scheduler.
847 static void TASK_YieldToSystem( void )
849 if ( !THREAD_IsWin16( THREAD_Current() ) )
851 FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
852 return;
855 if ( Callbacks->CallTaskRescheduleProc() )
857 /* NOTE: We get here only when no task has an event. This means also
858 the current task, so we shouldn't actually return to the
859 caller here. But, we need to do so, as the EVENT_WaitNetEvent
860 call could lead to a complex series of inter-task SendMessage
861 calls which might leave this task in a state where it again
862 has no event, but where its queue's wakeMask is also reset
863 to zero. Reentering TASK_Reschedule in this state would be
864 suicide. Hence, we do return to the caller after processing
865 non-hardware events. Actually, this should not hurt anyone,
866 as the caller must be WaitEvent, and thus the QUEUE_WaitBits
867 loop in USER. Should there actually be no message pending
868 for this task after processing non-hardware events, that loop
869 will simply return to WaitEvent. */
871 EVENT_WaitNetEvent( FALSE, FALSE );
876 /***********************************************************************
877 * InitTask (KERNEL.91)
879 * Called by the application startup code.
881 void WINAPI InitTask16( CONTEXT *context )
883 TDB *pTask;
884 NE_MODULE *pModule;
885 SEGTABLEENTRY *pSegTable;
886 INSTANCEDATA *pinstance;
887 LONG stacklow, stackhi;
889 if (context) EAX_reg(context) = 0;
890 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
891 if (!(pModule = NE_GetPtr( pTask->hModule ))) return;
893 /* Initialize implicitly loaded DLLs */
894 NE_InitializeDLLs( pTask->hModule );
896 if (context)
898 /* Registers on return are:
899 * ax 1 if OK, 0 on error
900 * cx stack limit in bytes
901 * dx cmdShow parameter
902 * si instance handle of the previous instance
903 * di instance handle of the new task
904 * es:bx pointer to command-line inside PSP
906 * 0 (=%bp) is pushed on the stack
908 SEGPTR ptr = STACK16_PUSH( pTask->thdb, sizeof(WORD) );
909 *(WORD *)PTR_SEG_TO_LIN(ptr) = 0;
910 SP_reg(context) -= 2;
912 EAX_reg(context) = 1;
914 if (!pTask->pdb.cmdLine[0]) EBX_reg(context) = 0x80;
915 else
917 LPBYTE p = &pTask->pdb.cmdLine[1];
918 while ((*p == ' ') || (*p == '\t')) p++;
919 EBX_reg(context) = 0x80 + (p - pTask->pdb.cmdLine);
921 ECX_reg(context) = pModule->stack_size;
922 EDX_reg(context) = pTask->nCmdShow;
923 ESI_reg(context) = (DWORD)pTask->hPrevInstance;
924 EDI_reg(context) = (DWORD)pTask->hInstance;
925 ES_reg (context) = (WORD)pTask->hPDB;
928 /* Initialize the local heap */
929 if ( pModule->heap_size )
931 LocalInit16( pTask->hInstance, 0, pModule->heap_size );
934 /* Initialize the INSTANCEDATA structure */
935 pSegTable = NE_SEG_TABLE( pModule );
936 stacklow = pSegTable[pModule->ss - 1].minsize;
937 stackhi = stacklow + pModule->stack_size;
938 if (stackhi > 0xffff) stackhi = 0xffff;
939 pinstance = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN(CURRENT_DS, 0);
940 pinstance->stackbottom = stackhi; /* yup, that's right. Confused me too. */
941 pinstance->stacktop = stacklow;
942 pinstance->stackmin = OFFSETOF( pTask->thdb->cur_stack );
946 /***********************************************************************
947 * WaitEvent (KERNEL.30)
949 BOOL16 WINAPI WaitEvent16( HTASK16 hTask )
951 TDB *pTask;
953 if (!hTask) hTask = GetCurrentTask();
954 pTask = (TDB *)GlobalLock16( hTask );
956 if ( !THREAD_IsWin16( THREAD_Current() ) )
958 FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
959 return TRUE;
962 if (pTask->nEvents > 0)
964 pTask->nEvents--;
965 return FALSE;
967 TASK_YieldToSystem();
969 /* When we get back here, we have an event */
971 if (pTask->nEvents > 0) pTask->nEvents--;
972 return TRUE;
976 /***********************************************************************
977 * PostEvent (KERNEL.31)
979 void WINAPI PostEvent16( HTASK16 hTask )
981 TDB *pTask;
983 if (!hTask) hTask = GetCurrentTask();
984 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
986 if ( !THREAD_IsWin16( pTask->thdb ) )
988 FIXME( task, "called for Win32 thread (%04x)!\n", pTask->thdb->teb_sel );
989 return;
992 pTask->nEvents++;
994 if ( !THREAD_IsWin16( THREAD_Current() ) )
996 /* wake-up the scheduler waiting in EVENT_WaitNetEvent */
997 EVENT_WakeUp();
1002 /***********************************************************************
1003 * SetPriority (KERNEL.32)
1005 void WINAPI SetPriority16( HTASK16 hTask, INT16 delta )
1007 TDB *pTask;
1008 INT16 newpriority;
1010 if (!hTask) hTask = GetCurrentTask();
1011 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
1012 newpriority = pTask->priority + delta;
1013 if (newpriority < -32) newpriority = -32;
1014 else if (newpriority > 15) newpriority = 15;
1016 pTask->priority = newpriority + 1;
1017 TASK_UnlinkTask( hTask );
1018 TASK_LinkTask( hTask );
1019 pTask->priority--;
1023 /***********************************************************************
1024 * LockCurrentTask (KERNEL.33)
1026 HTASK16 WINAPI LockCurrentTask16( BOOL16 bLock )
1028 if (bLock) hLockedTask = GetCurrentTask();
1029 else hLockedTask = 0;
1030 return hLockedTask;
1034 /***********************************************************************
1035 * IsTaskLocked (KERNEL.122)
1037 HTASK16 WINAPI IsTaskLocked16(void)
1039 return hLockedTask;
1043 /***********************************************************************
1044 * OldYield (KERNEL.117)
1046 void WINAPI OldYield16(void)
1048 TDB *pCurTask = (TDB *)GlobalLock16( GetCurrentTask() );
1050 if ( !THREAD_IsWin16( THREAD_Current() ) )
1052 FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
1053 return;
1056 if (pCurTask) pCurTask->nEvents++; /* Make sure we get back here */
1057 TASK_YieldToSystem();
1058 if (pCurTask) pCurTask->nEvents--;
1062 /***********************************************************************
1063 * DirectedYield (KERNEL.150)
1065 void WINAPI DirectedYield16( HTASK16 hTask )
1067 TDB *pCurTask = (TDB *)GlobalLock16( GetCurrentTask() );
1069 if ( !THREAD_IsWin16( THREAD_Current() ) )
1071 FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
1072 return;
1075 TRACE(task, "%04x: DirectedYield(%04x)\n", pCurTask->hSelf, hTask );
1077 pCurTask->hYieldTo = hTask;
1078 OldYield16();
1080 TRACE(task, "%04x: back from DirectedYield(%04x)\n", pCurTask->hSelf, hTask );
1083 /***********************************************************************
1084 * Yield16 (KERNEL.29)
1086 void WINAPI Yield16(void)
1088 TDB *pCurTask = (TDB *)GlobalLock16( GetCurrentTask() );
1090 if (pCurTask) pCurTask->hYieldTo = 0;
1091 if (pCurTask && pCurTask->hQueue) Callout.UserYield16();
1092 else OldYield16();
1095 /***********************************************************************
1096 * KERNEL_490 (KERNEL.490)
1098 HTASK16 WINAPI KERNEL_490( HTASK16 someTask )
1100 if ( !someTask ) return 0;
1102 FIXME( task, "(%04x): stub\n", someTask );
1103 return 0;
1106 /***********************************************************************
1107 * MakeProcInstance16 (KERNEL.51)
1109 FARPROC16 WINAPI MakeProcInstance16( FARPROC16 func, HANDLE16 hInstance )
1111 BYTE *thunk,*lfunc;
1112 SEGPTR thunkaddr;
1114 if (!func) {
1115 ERR(task, "Ouch ! MakeProcInstance called with func == NULL !\n");
1116 return (FARPROC16)0; /* Windows seems to do the same */
1118 if (!hInstance) hInstance = CURRENT_DS;
1119 thunkaddr = TASK_AllocThunk( GetCurrentTask() );
1120 if (!thunkaddr) return (FARPROC16)0;
1121 thunk = PTR_SEG_TO_LIN( thunkaddr );
1122 lfunc = PTR_SEG_TO_LIN( func );
1124 TRACE(task, "(%08lx,%04x): got thunk %08lx\n",
1125 (DWORD)func, hInstance, (DWORD)thunkaddr );
1126 if (((lfunc[0]==0x8c) && (lfunc[1]==0xd8)) ||
1127 ((lfunc[0]==0x1e) && (lfunc[1]==0x58))
1129 FIXME(task,"thunk would be useless for %p, overwriting with nop;nop;\n", func );
1130 lfunc[0]=0x90; /* nop */
1131 lfunc[1]=0x90; /* nop */
1134 *thunk++ = 0xb8; /* movw instance, %ax */
1135 *thunk++ = (BYTE)(hInstance & 0xff);
1136 *thunk++ = (BYTE)(hInstance >> 8);
1137 *thunk++ = 0xea; /* ljmp func */
1138 *(DWORD *)thunk = (DWORD)func;
1139 return (FARPROC16)thunkaddr;
1143 /***********************************************************************
1144 * FreeProcInstance16 (KERNEL.52)
1146 void WINAPI FreeProcInstance16( FARPROC16 func )
1148 TRACE(task, "(%08lx)\n", (DWORD)func );
1149 TASK_FreeThunk( GetCurrentTask(), (SEGPTR)func );
1153 /**********************************************************************
1154 * GetCodeHandle (KERNEL.93)
1156 HANDLE16 WINAPI GetCodeHandle16( FARPROC16 proc )
1158 HANDLE16 handle;
1159 BYTE *thunk = (BYTE *)PTR_SEG_TO_LIN( proc );
1161 /* Return the code segment containing 'proc'. */
1162 /* Not sure if this is really correct (shouldn't matter that much). */
1164 /* Check if it is really a thunk */
1165 if ((thunk[0] == 0xb8) && (thunk[3] == 0xea))
1166 handle = GlobalHandle16( thunk[6] + (thunk[7] << 8) );
1167 else
1168 handle = GlobalHandle16( HIWORD(proc) );
1170 return handle;
1173 /**********************************************************************
1174 * GetCodeInfo (KERNEL.104)
1176 VOID WINAPI GetCodeInfo16( FARPROC16 proc, SEGINFO *segInfo )
1178 BYTE *thunk = (BYTE *)PTR_SEG_TO_LIN( proc );
1179 NE_MODULE *pModule = NULL;
1180 SEGTABLEENTRY *pSeg = NULL;
1181 WORD segNr;
1183 /* proc is either a thunk, or else a pair of module handle
1184 and segment number. In the first case, we also need to
1185 extract module and segment number. */
1187 if ((thunk[0] == 0xb8) && (thunk[3] == 0xea))
1189 WORD selector = thunk[6] + (thunk[7] << 8);
1190 pModule = NE_GetPtr( GlobalHandle16( selector ) );
1191 pSeg = pModule? NE_SEG_TABLE( pModule ) : NULL;
1193 if ( pModule )
1194 for ( segNr = 0; segNr < pModule->seg_count; segNr++, pSeg++ )
1195 if ( GlobalHandleToSel16(pSeg->hSeg) == selector )
1196 break;
1198 if ( pModule && segNr >= pModule->seg_count )
1199 pSeg = NULL;
1201 else
1203 pModule = NE_GetPtr( HIWORD( proc ) );
1204 segNr = LOWORD( proc );
1206 if ( pModule && segNr < pModule->seg_count )
1207 pSeg = NE_SEG_TABLE( pModule ) + segNr;
1210 /* fill in segment information */
1212 segInfo->offSegment = pSeg? pSeg->filepos : 0;
1213 segInfo->cbSegment = pSeg? pSeg->size : 0;
1214 segInfo->flags = pSeg? pSeg->flags : 0;
1215 segInfo->cbAlloc = pSeg? pSeg->minsize : 0;
1216 segInfo->h = pSeg? pSeg->hSeg : 0;
1217 segInfo->alignShift = pModule? pModule->alignment : 0;
1221 /**********************************************************************
1222 * DefineHandleTable16 (KERNEL.94)
1224 BOOL16 WINAPI DefineHandleTable16( WORD wOffset )
1226 return TRUE; /* FIXME */
1230 /***********************************************************************
1231 * SetTaskQueue (KERNEL.34)
1233 HQUEUE16 WINAPI SetTaskQueue16( HTASK16 hTask, HQUEUE16 hQueue )
1235 HQUEUE16 hPrev;
1236 TDB *pTask;
1238 if (!hTask) hTask = GetCurrentTask();
1239 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
1241 hPrev = pTask->hQueue;
1242 pTask->hQueue = hQueue;
1244 TIMER_SwitchQueue( hPrev, hQueue );
1246 return hPrev;
1250 /***********************************************************************
1251 * GetTaskQueue (KERNEL.35)
1253 HQUEUE16 WINAPI GetTaskQueue16( HTASK16 hTask )
1255 TDB *pTask;
1257 if (!hTask) hTask = GetCurrentTask();
1258 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
1259 return pTask->hQueue;
1262 /***********************************************************************
1263 * SetThreadQueue (KERNEL.463)
1265 HQUEUE16 WINAPI SetThreadQueue16( DWORD thread, HQUEUE16 hQueue )
1267 THDB *thdb = thread? THREAD_IdToTHDB( thread ) : THREAD_Current();
1268 HQUEUE16 oldQueue = thdb? thdb->teb.queue : 0;
1270 if ( thdb )
1272 thdb->teb.queue = hQueue;
1274 if ( GetTaskQueue16( thdb->process->task ) == oldQueue )
1275 SetTaskQueue16( thdb->process->task, hQueue );
1278 return oldQueue;
1281 /***********************************************************************
1282 * GetThreadQueue (KERNEL.464)
1284 HQUEUE16 WINAPI GetThreadQueue16( DWORD thread )
1286 THDB *thdb = NULL;
1287 if ( !thread )
1288 thdb = THREAD_Current();
1289 else if ( HIWORD(thread) )
1290 thdb = THREAD_IdToTHDB( thread );
1291 else if ( IsTask16( (HTASK16)thread ) )
1292 thdb = ((TDB *)GlobalLock16( (HANDLE16)thread ))->thdb;
1294 return (HQUEUE16)(thdb? thdb->teb.queue : 0);
1297 /***********************************************************************
1298 * SetFastQueue (KERNEL.624)
1300 VOID WINAPI SetFastQueue16( DWORD thread, HANDLE hQueue )
1302 THDB *thdb = NULL;
1303 if ( !thread )
1304 thdb = THREAD_Current();
1305 else if ( HIWORD(thread) )
1306 thdb = THREAD_IdToTHDB( thread );
1307 else if ( IsTask16( (HTASK16)thread ) )
1308 thdb = ((TDB *)GlobalLock16( (HANDLE16)thread ))->thdb;
1310 if ( thdb ) thdb->teb.queue = (HQUEUE16) hQueue;
1313 /***********************************************************************
1314 * GetFastQueue (KERNEL.625)
1316 HANDLE WINAPI GetFastQueue16( void )
1318 THDB *thdb = THREAD_Current();
1319 if (!thdb) return 0;
1321 if (!thdb->teb.queue)
1322 Callout.InitThreadInput16( 0, THREAD_IsWin16(thdb)? 4 : 5 );
1324 if (!thdb->teb.queue)
1325 FIXME( task, "(): should initialize thread-local queue, expect failure!\n" );
1327 return (HANDLE)thdb->teb.queue;
1330 /***********************************************************************
1331 * SwitchStackTo (KERNEL.108)
1333 void WINAPI SwitchStackTo16( WORD seg, WORD ptr, WORD top )
1335 TDB *pTask;
1336 STACK16FRAME *oldFrame, *newFrame;
1337 INSTANCEDATA *pData;
1338 UINT16 copySize;
1340 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
1341 if (!(pData = (INSTANCEDATA *)GlobalLock16( seg ))) return;
1342 TRACE(task, "old=%04x:%04x new=%04x:%04x\n",
1343 SELECTOROF( pTask->thdb->cur_stack ),
1344 OFFSETOF( pTask->thdb->cur_stack ), seg, ptr );
1346 /* Save the old stack */
1348 oldFrame = THREAD_STACK16( pTask->thdb );
1349 /* pop frame + args and push bp */
1350 pData->old_ss_sp = pTask->thdb->cur_stack + sizeof(STACK16FRAME)
1351 + 2 * sizeof(WORD);
1352 *(WORD *)PTR_SEG_TO_LIN(pData->old_ss_sp) = oldFrame->bp;
1353 pData->stacktop = top;
1354 pData->stackmin = ptr;
1355 pData->stackbottom = ptr;
1357 /* Switch to the new stack */
1359 /* Note: we need to take the 3 arguments into account; otherwise,
1360 * the stack will underflow upon return from this function.
1362 copySize = oldFrame->bp - OFFSETOF(pData->old_ss_sp);
1363 copySize += 3 * sizeof(WORD) + sizeof(STACK16FRAME);
1364 pTask->thdb->cur_stack = PTR_SEG_OFF_TO_SEGPTR( seg, ptr - copySize );
1365 newFrame = THREAD_STACK16( pTask->thdb );
1367 /* Copy the stack frame and the local variables to the new stack */
1369 memmove( newFrame, oldFrame, copySize );
1370 newFrame->bp = ptr;
1371 *(WORD *)PTR_SEG_OFF_TO_LIN( seg, ptr ) = 0; /* clear previous bp */
1375 /***********************************************************************
1376 * SwitchStackBack (KERNEL.109)
1378 void WINAPI SwitchStackBack16( CONTEXT *context )
1380 TDB *pTask;
1381 STACK16FRAME *oldFrame, *newFrame;
1382 INSTANCEDATA *pData;
1384 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
1385 if (!(pData = (INSTANCEDATA *)GlobalLock16(SELECTOROF(pTask->thdb->cur_stack))))
1386 return;
1387 if (!pData->old_ss_sp)
1389 WARN( task, "No previous SwitchStackTo\n" );
1390 return;
1392 TRACE(task, "restoring stack %04x:%04x\n",
1393 SELECTOROF(pData->old_ss_sp), OFFSETOF(pData->old_ss_sp) );
1395 oldFrame = THREAD_STACK16( pTask->thdb );
1397 /* Pop bp from the previous stack */
1399 BP_reg(context) = *(WORD *)PTR_SEG_TO_LIN(pData->old_ss_sp);
1400 pData->old_ss_sp += sizeof(WORD);
1402 /* Switch back to the old stack */
1404 pTask->thdb->cur_stack = pData->old_ss_sp - sizeof(STACK16FRAME);
1405 SS_reg(context) = SELECTOROF(pData->old_ss_sp);
1406 ESP_reg(context) = OFFSETOF(pData->old_ss_sp) - sizeof(DWORD); /*ret addr*/
1407 pData->old_ss_sp = 0;
1409 /* Build a stack frame for the return */
1411 newFrame = THREAD_STACK16( pTask->thdb );
1412 newFrame->frame32 = oldFrame->frame32;
1413 if (TRACE_ON(relay))
1415 newFrame->entry_ip = oldFrame->entry_ip;
1416 newFrame->entry_cs = oldFrame->entry_cs;
1421 /***********************************************************************
1422 * GetTaskQueueDS (KERNEL.118)
1424 void WINAPI GetTaskQueueDS16( CONTEXT *context )
1426 DS_reg(context) = GlobalHandleToSel16( GetTaskQueue16(0) );
1430 /***********************************************************************
1431 * GetTaskQueueES (KERNEL.119)
1433 void WINAPI GetTaskQueueES16( CONTEXT *context )
1435 ES_reg(context) = GlobalHandleToSel16( GetTaskQueue16(0) );
1439 /***********************************************************************
1440 * GetCurrentTask (KERNEL.36)
1442 HTASK16 WINAPI GetCurrentTask(void)
1444 return THREAD_InitDone? PROCESS_Current()->task : 0;
1447 DWORD WINAPI WIN16_GetCurrentTask(void)
1449 /* This is the version used by relay code; the first task is */
1450 /* returned in the high word of the result */
1451 return MAKELONG( GetCurrentTask(), hFirstTask );
1455 /***********************************************************************
1456 * GetCurrentPDB (KERNEL.37)
1458 * UNDOC: returns PSP of KERNEL in high word
1460 DWORD WINAPI GetCurrentPDB16(void)
1462 TDB *pTask;
1464 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1465 return MAKELONG(pTask->hPDB, 0); /* FIXME */
1469 /***********************************************************************
1470 * GetInstanceData (KERNEL.54)
1472 INT16 WINAPI GetInstanceData16( HINSTANCE16 instance, WORD buffer, INT16 len )
1474 char *ptr = (char *)GlobalLock16( instance );
1475 if (!ptr || !len) return 0;
1476 if ((int)buffer + len >= 0x10000) len = 0x10000 - buffer;
1477 memcpy( (char *)GlobalLock16(CURRENT_DS) + buffer, ptr + buffer, len );
1478 return len;
1482 /***********************************************************************
1483 * GetExeVersion (KERNEL.105)
1485 WORD WINAPI GetExeVersion16(void)
1487 TDB *pTask;
1489 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1490 return pTask->version;
1494 /***********************************************************************
1495 * SetErrorMode16 (KERNEL.107)
1497 UINT16 WINAPI SetErrorMode16( UINT16 mode )
1499 TDB *pTask;
1500 UINT16 oldMode;
1502 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1503 oldMode = pTask->error_mode;
1504 pTask->error_mode = mode;
1505 pTask->thdb->process->error_mode = mode;
1506 return oldMode;
1510 /***********************************************************************
1511 * SetErrorMode32 (KERNEL32.486)
1513 UINT WINAPI SetErrorMode( UINT mode )
1515 return SetErrorMode16( (UINT16)mode );
1519 /***********************************************************************
1520 * GetDOSEnvironment (KERNEL.131)
1522 SEGPTR WINAPI GetDOSEnvironment16(void)
1524 TDB *pTask;
1526 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1527 return PTR_SEG_OFF_TO_SEGPTR( pTask->pdb.environment, 0 );
1531 /***********************************************************************
1532 * GetNumTasks (KERNEL.152)
1534 UINT16 WINAPI GetNumTasks16(void)
1536 return nTaskCount;
1540 /***********************************************************************
1541 * GetTaskDS (KERNEL.155)
1543 * Note: this function apparently returns a DWORD with LOWORD == HIWORD.
1544 * I don't think we need to bother with this.
1546 HINSTANCE16 WINAPI GetTaskDS16(void)
1548 TDB *pTask;
1550 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1551 return pTask->hInstance;
1554 /***********************************************************************
1555 * GetDummyModuleHandleDS (KERNEL.602)
1557 VOID WINAPI GetDummyModuleHandleDS16( CONTEXT *context )
1559 TDB *pTask;
1560 WORD selector;
1562 AX_reg( context ) = 0;
1563 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
1564 if (!(pTask->flags & TDBF_WIN32)) return;
1566 selector = GlobalHandleToSel16( pTask->hModule );
1567 DS_reg( context ) = selector;
1568 AX_reg( context ) = selector;
1571 /***********************************************************************
1572 * IsTask (KERNEL.320)
1574 BOOL16 WINAPI IsTask16( HTASK16 hTask )
1576 TDB *pTask;
1578 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return FALSE;
1579 if (GlobalSize16( hTask ) < sizeof(TDB)) return FALSE;
1580 return (pTask->magic == TDB_MAGIC);
1584 /***********************************************************************
1585 * SetTaskSignalProc (KERNEL.38)
1587 * Real 16-bit interface is provided by the THUNK_SetTaskSignalProc.
1589 FARPROC16 WINAPI SetTaskSignalProc( HTASK16 hTask, FARPROC16 proc )
1591 TDB *pTask;
1592 FARPROC16 oldProc;
1594 if (!hTask) hTask = GetCurrentTask();
1595 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return NULL;
1596 oldProc = (FARPROC16)pTask->userhandler;
1597 pTask->userhandler = (USERSIGNALPROC)proc;
1598 return oldProc;
1602 /***********************************************************************
1603 * SetSigHandler (KERNEL.140)
1605 WORD WINAPI SetSigHandler16( FARPROC16 newhandler, FARPROC16* oldhandler,
1606 UINT16 *oldmode, UINT16 newmode, UINT16 flag )
1608 FIXME(task,"(%p,%p,%p,%d,%d), unimplemented.\n",
1609 newhandler,oldhandler,oldmode,newmode,flag );
1611 if (flag != 1) return 0;
1612 if (!newmode) newhandler = NULL; /* Default handler */
1613 if (newmode != 4)
1615 TDB *pTask;
1617 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1618 if (oldmode) *oldmode = pTask->signal_flags;
1619 pTask->signal_flags = newmode;
1620 if (oldhandler) *oldhandler = pTask->sighandler;
1621 pTask->sighandler = newhandler;
1623 return 0;
1627 /***********************************************************************
1628 * GlobalNotify (KERNEL.154)
1630 * Note that GlobalNotify does _not_ return the old NotifyProc
1631 * -- contrary to LocalNotify !!
1633 VOID WINAPI GlobalNotify16( FARPROC16 proc )
1635 TDB *pTask;
1637 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
1638 pTask->discardhandler = proc;
1642 /***********************************************************************
1643 * GetExePtr (KERNEL.133)
1645 static HMODULE16 GetExePtrHelper( HANDLE16 handle, HTASK16 *hTask )
1647 char *ptr;
1648 HANDLE16 owner;
1650 /* Check for module handle */
1652 if (!(ptr = GlobalLock16( handle ))) return 0;
1653 if (((NE_MODULE *)ptr)->magic == IMAGE_OS2_SIGNATURE) return handle;
1655 /* Search for this handle inside all tasks */
1657 *hTask = hFirstTask;
1658 while (*hTask)
1660 TDB *pTask = (TDB *)GlobalLock16( *hTask );
1661 if ((*hTask == handle) ||
1662 (pTask->hInstance == handle) ||
1663 (pTask->hQueue == handle) ||
1664 (pTask->hPDB == handle)) return pTask->hModule;
1665 *hTask = pTask->hNext;
1668 /* Check the owner for module handle */
1670 owner = FarGetOwner16( handle );
1671 if (!(ptr = GlobalLock16( owner ))) return 0;
1672 if (((NE_MODULE *)ptr)->magic == IMAGE_OS2_SIGNATURE) return owner;
1674 /* Search for the owner inside all tasks */
1676 *hTask = hFirstTask;
1677 while (*hTask)
1679 TDB *pTask = (TDB *)GlobalLock16( *hTask );
1680 if ((*hTask == owner) ||
1681 (pTask->hInstance == owner) ||
1682 (pTask->hQueue == owner) ||
1683 (pTask->hPDB == owner)) return pTask->hModule;
1684 *hTask = pTask->hNext;
1687 return 0;
1690 HMODULE16 WINAPI GetExePtr( HANDLE16 handle )
1692 HTASK16 dummy;
1693 return GetExePtrHelper( handle, &dummy );
1696 void WINAPI WIN16_GetExePtr( CONTEXT *context )
1698 WORD *stack = PTR_SEG_OFF_TO_LIN(SS_reg(context), SP_reg(context));
1699 HANDLE16 handle = (HANDLE16)stack[2];
1700 HTASK16 hTask = 0;
1701 HMODULE16 hModule;
1703 hModule = GetExePtrHelper( handle, &hTask );
1705 AX_reg(context) = CX_reg(context) = hModule;
1706 if (hTask) ES_reg(context) = hTask;
1709 /***********************************************************************
1710 * TaskFirst (TOOLHELP.63)
1712 BOOL16 WINAPI TaskFirst16( TASKENTRY *lpte )
1714 lpte->hNext = hFirstTask;
1715 return TaskNext16( lpte );
1719 /***********************************************************************
1720 * TaskNext (TOOLHELP.64)
1722 BOOL16 WINAPI TaskNext16( TASKENTRY *lpte )
1724 TDB *pTask;
1725 INSTANCEDATA *pInstData;
1727 TRACE(toolhelp, "(%p): task=%04x\n", lpte, lpte->hNext );
1728 if (!lpte->hNext) return FALSE;
1729 pTask = (TDB *)GlobalLock16( lpte->hNext );
1730 if (!pTask || pTask->magic != TDB_MAGIC) return FALSE;
1731 pInstData = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( pTask->hInstance, 0 );
1732 lpte->hTask = lpte->hNext;
1733 lpte->hTaskParent = pTask->hParent;
1734 lpte->hInst = pTask->hInstance;
1735 lpte->hModule = pTask->hModule;
1736 lpte->wSS = SELECTOROF( pTask->thdb->cur_stack );
1737 lpte->wSP = OFFSETOF( pTask->thdb->cur_stack );
1738 lpte->wStackTop = pInstData->stacktop;
1739 lpte->wStackMinimum = pInstData->stackmin;
1740 lpte->wStackBottom = pInstData->stackbottom;
1741 lpte->wcEvents = pTask->nEvents;
1742 lpte->hQueue = pTask->hQueue;
1743 strncpy( lpte->szModule, pTask->module_name, 8 );
1744 lpte->szModule[8] = '\0';
1745 lpte->wPSPOffset = 0x100; /*??*/
1746 lpte->hNext = pTask->hNext;
1747 return TRUE;
1751 /***********************************************************************
1752 * TaskFindHandle (TOOLHELP.65)
1754 BOOL16 WINAPI TaskFindHandle16( TASKENTRY *lpte, HTASK16 hTask )
1756 lpte->hNext = hTask;
1757 return TaskNext16( lpte );
1761 /***********************************************************************
1762 * GetAppCompatFlags16 (KERNEL.354)
1764 DWORD WINAPI GetAppCompatFlags16( HTASK16 hTask )
1766 return GetAppCompatFlags( hTask );
1770 /***********************************************************************
1771 * GetAppCompatFlags32 (USER32.206)
1773 DWORD WINAPI GetAppCompatFlags( HTASK hTask )
1775 TDB *pTask;
1777 if (!hTask) hTask = GetCurrentTask();
1778 if (!(pTask=(TDB *)GlobalLock16( (HTASK16)hTask ))) return 0;
1779 if (GlobalSize16(hTask) < sizeof(TDB)) return 0;
1780 return pTask->compat_flags;