Moved error codes to cderr.h.
[wine/multimedia.git] / loader / task.c
blob8adcb13eebf2ab3ee0fbd3bb831970de479c279f
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(TDB*);
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 /* FIXME: all this is an ugly hack */
245 OFSTRUCT *ofs = (OFSTRUCT *)((char*)(pModule) + (pModule)->fileinfo);
246 LPTHREAD_START_ROUTINE entry = (LPTHREAD_START_ROUTINE)
247 RVA_PTR(pModule->module32, OptionalHeader.AddressOfEntryPoint);
249 /* Create 32-bit MODREF */
250 if ( !PE_CreateModule( pModule->module32, ofs, 0, FALSE ) )
252 ERR( task, "Could not initialize process\n" );
253 ExitProcess( 1 );
256 /* Initialize Thread-Local Storage */
257 PE_InitTls( pTask->thdb );
259 if (PE_HEADER(pModule->module32)->OptionalHeader.Subsystem==IMAGE_SUBSYSTEM_WINDOWS_CUI)
260 AllocConsole();
262 MODULE_InitializeDLLs( 0, DLL_PROCESS_ATTACH, (LPVOID)-1 );
263 TRACE(relay, "(entryproc=%p)\n", entry );
265 #if 1
266 ExitProcess( entry(NULL) );
267 #else
269 DWORD size = PE_HEADER(pModule->module32)->OptionalHeader.SizeOfStackReserve;
270 DWORD id;
271 THDB *thdb;
273 CreateThread( NULL, size, entry, NULL, 0, &id );
274 thdb = THREAD_IdToTHDB( id );
276 while ( thdb->exit_code == 0x103 )
278 WaitEvent16( 0 );
279 QUEUE_Signal( pTask->hSelf );
282 ExitProcess( thdb->exit_code );
284 #endif
286 else if (pModule->dos_image)
288 DOSVM_Enter( NULL );
289 ExitProcess( 0 );
291 else
293 /* Registers at initialization must be:
294 * ax zero
295 * bx stack size in bytes
296 * cx heap size in bytes
297 * si previous app instance
298 * di current app instance
299 * bp zero
300 * es selector to the PSP
301 * ds dgroup of the application
302 * ss stack selector
303 * sp top of the stack
305 CONTEXT context;
307 memset( &context, 0, sizeof(context) );
308 CS_reg(&context) = GlobalHandleToSel16(pSegTable[pModule->cs - 1].hSeg);
309 DS_reg(&context) = GlobalHandleToSel16(pSegTable[pModule->dgroup - 1].hSeg);
310 ES_reg(&context) = pTask->hPDB;
311 EIP_reg(&context) = pModule->ip;
312 EBX_reg(&context) = pModule->stack_size;
313 ECX_reg(&context) = pModule->heap_size;
314 EDI_reg(&context) = context.SegDs;
316 TRACE(task, "Starting main program: cs:ip=%04lx:%04x ds=%04lx ss:sp=%04x:%04x\n",
317 CS_reg(&context), IP_reg(&context), DS_reg(&context),
318 SELECTOROF(pTask->thdb->cur_stack),
319 OFFSETOF(pTask->thdb->cur_stack) );
321 Callbacks->CallRegisterShortProc( &context, 0 );
322 /* This should never return */
323 ERR( task, "Main program returned! (should never happen)\n" );
324 ExitProcess( 1 );
329 /***********************************************************************
330 * TASK_Create
332 * NOTE: This routine might be called by a Win32 thread. We don't have
333 * any real problems with that, since we operated merely on a private
334 * TDB structure that is not yet linked into the task list.
336 BOOL TASK_Create( THDB *thdb, NE_MODULE *pModule, HINSTANCE16 hInstance,
337 HINSTANCE16 hPrevInstance, UINT16 cmdShow)
339 HTASK16 hTask;
340 TDB *pTask, *pInitialTask;
341 LPSTR cmd_line;
342 WORD sp;
343 char *stack32Top;
344 char name[10];
345 STACK16FRAME *frame16;
346 STACK32FRAME *frame32;
347 PDB *pdb32 = thdb->process;
348 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
350 /* Allocate the task structure */
352 hTask = GLOBAL_Alloc( GMEM_FIXED | GMEM_ZEROINIT, sizeof(TDB),
353 pModule->self, FALSE, FALSE, FALSE );
354 if (!hTask) return FALSE;
355 pTask = (TDB *)GlobalLock16( hTask );
357 /* Fill the task structure */
359 pTask->nEvents = 1; /* So the task can be started */
360 pTask->hSelf = hTask;
361 pTask->flags = 0;
363 if (pModule->flags & NE_FFLAGS_WIN32)
364 pTask->flags |= TDBF_WIN32;
365 if (pModule->lpDosTask)
366 pTask->flags |= TDBF_WINOLDAP;
368 pTask->version = pModule->expected_version;
369 pTask->hInstance = hInstance? hInstance : pModule->self;
370 pTask->hPrevInstance = hPrevInstance;
371 pTask->hModule = pModule->self;
372 pTask->hParent = GetCurrentTask();
373 pTask->magic = TDB_MAGIC;
374 pTask->nCmdShow = cmdShow;
375 pTask->thdb = thdb;
376 pTask->curdrive = DRIVE_GetCurrentDrive() | 0x80;
377 strcpy( pTask->curdir, "\\" );
378 lstrcpynA( pTask->curdir + 1, DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() ),
379 sizeof(pTask->curdir) - 1 );
381 /* Create the thunks block */
383 TASK_CreateThunks( hTask, (int)&pTask->thunks - (int)pTask, 7 );
385 /* Copy the module name */
387 GetModuleName16( pModule->self, name, sizeof(name) );
388 strncpy( pTask->module_name, name, sizeof(pTask->module_name) );
390 /* Allocate a selector for the PDB */
392 pTask->hPDB = GLOBAL_CreateBlock( GMEM_FIXED, &pTask->pdb, sizeof(PDB16),
393 pModule->self, FALSE, FALSE, FALSE, NULL );
395 /* Fill the PDB */
397 pTask->pdb.int20 = 0x20cd;
398 pTask->pdb.dispatcher[0] = 0x9a; /* ljmp */
399 PUT_DWORD(&pTask->pdb.dispatcher[1], (DWORD)NE_GetEntryPoint(
400 GetModuleHandle16("KERNEL"), 102 )); /* KERNEL.102 is DOS3Call() */
401 pTask->pdb.savedint22 = INT_GetPMHandler( 0x22 );
402 pTask->pdb.savedint23 = INT_GetPMHandler( 0x23 );
403 pTask->pdb.savedint24 = INT_GetPMHandler( 0x24 );
404 pTask->pdb.fileHandlesPtr =
405 PTR_SEG_OFF_TO_SEGPTR( GlobalHandleToSel16(pTask->hPDB),
406 (int)&((PDB16 *)0)->fileHandles );
407 pTask->pdb.hFileHandles = 0;
408 memset( pTask->pdb.fileHandles, 0xff, sizeof(pTask->pdb.fileHandles) );
409 pTask->pdb.environment = pdb32->env_db->env_sel;
410 pTask->pdb.nbFiles = 20;
412 /* Fill the command line */
414 cmd_line = pdb32->env_db->cmd_line;
415 while (*cmd_line && (*cmd_line != ' ') && (*cmd_line != '\t')) cmd_line++;
416 while ((*cmd_line == ' ') || (*cmd_line == '\t')) cmd_line++;
417 lstrcpynA( pTask->pdb.cmdLine+1, cmd_line, sizeof(pTask->pdb.cmdLine)-1);
418 pTask->pdb.cmdLine[0] = strlen( pTask->pdb.cmdLine + 1 );
420 /* Get the compatibility flags */
422 pTask->compat_flags = GetProfileIntA( "Compatibility", name, 0 );
424 /* Allocate a code segment alias for the TDB */
426 pTask->hCSAlias = GLOBAL_CreateBlock( GMEM_FIXED, (void *)pTask,
427 sizeof(TDB), pTask->hPDB, TRUE,
428 FALSE, FALSE, NULL );
430 /* Set the owner of the environment block */
432 FarSetOwner16( pTask->pdb.environment, pTask->hPDB );
434 /* Default DTA overwrites command-line */
436 pTask->dta = PTR_SEG_OFF_TO_SEGPTR( pTask->hPDB,
437 (int)&pTask->pdb.cmdLine - (int)&pTask->pdb );
439 /* Inherit default UserSignalHandler from initial process */
441 pInitialTask = (TDB *)GlobalLock16( PROCESS_Initial()->task );
442 if ( pInitialTask )
443 pTask->userhandler = pInitialTask->userhandler;
445 /* If we have a DGROUP/hInstance, use it for 16-bit stack */
447 if ( hInstance )
449 if (!(sp = pModule->sp))
450 sp = pSegTable[pModule->ss-1].minsize + pModule->stack_size;
451 sp &= ~1; sp -= sizeof(STACK16FRAME);
452 pTask->thdb->cur_stack = PTR_SEG_OFF_TO_SEGPTR( hInstance, sp );
455 /* Create the 16-bit stack frame */
457 pTask->thdb->cur_stack -= sizeof(STACK16FRAME);
458 frame16 = (STACK16FRAME *)PTR_SEG_TO_LIN( pTask->thdb->cur_stack );
459 frame16->ebp = OFFSETOF( pTask->thdb->cur_stack ) + (int)&((STACK16FRAME *)0)->bp;
460 frame16->bp = LOWORD(frame16->ebp);
461 frame16->ds = frame16->es = hInstance;
462 frame16->fs = 0;
463 frame16->entry_point = 0;
464 frame16->entry_cs = 0;
465 frame16->mutex_count = 1; /* TASK_Reschedule is called from 16-bit code */
466 /* The remaining fields will be initialized in TASK_Reschedule */
468 /* Create the 32-bit stack frame */
470 stack32Top = (char*)pTask->thdb->teb.stack_top;
471 frame16->frame32 = frame32 = (STACK32FRAME *)stack32Top - 1;
472 frame32->frame16 = pTask->thdb->cur_stack + sizeof(STACK16FRAME);
473 frame32->edi = 0;
474 frame32->esi = 0;
475 frame32->edx = 0;
476 frame32->ecx = 0;
477 frame32->ebx = 0;
478 frame32->retaddr = (DWORD)TASK_CallToStart;
479 /* The remaining fields will be initialized in TASK_Reschedule */
481 /* Enter task handle into thread and process */
483 pTask->thdb->teb.htask16 = pTask->thdb->process->task = hTask;
485 TRACE(task, "module='%s' cmdline='%s' task=%04x\n",
486 name, cmd_line, hTask );
488 return TRUE;
491 /***********************************************************************
492 * TASK_StartTask
494 * NOTE: This routine might be called by a Win32 thread. Thus, we need
495 * to be careful to protect global data structures. We do this
496 * by entering the Win16Lock while linking the task into the
497 * global task list.
499 void TASK_StartTask( HTASK16 hTask )
501 /* Add the task to the linked list */
503 SYSLEVEL_EnterWin16Lock();
504 TASK_LinkTask( hTask );
505 SYSLEVEL_LeaveWin16Lock();
507 TRACE(task, "linked task %04x\n", hTask );
509 /* If requested, add entry point breakpoint */
511 if ( TASK_AddTaskEntryBreakpoint )
512 TASK_AddTaskEntryBreakpoint( hTask );
514 /* Get the task up and running. If we ourselves are a 16-bit task,
515 we simply Yield(). If we are 32-bit however, we need to signal
516 the main process somehow (NOT YET IMPLEMENTED!) */
518 if ( THREAD_IsWin16( THREAD_Current() ) )
519 OldYield16();
520 else
521 /* wake-up the scheduler waiting in EVENT_WaitNetEvent */
522 EVENT_WakeUp();
526 /***********************************************************************
527 * TASK_DeleteTask
529 static void TASK_DeleteTask( HTASK16 hTask )
531 TDB *pTask;
532 HGLOBAL16 hPDB;
534 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
535 hPDB = pTask->hPDB;
537 pTask->magic = 0xdead; /* invalidate signature */
539 /* Delete the Win32 part of the task */
541 /* PROCESS_FreePDB( pTask->thdb->process ); FIXME */
542 /* K32OBJ_DecCount( &pTask->thdb->header ); FIXME */
544 /* Free the selector aliases */
546 GLOBAL_FreeBlock( pTask->hCSAlias );
547 GLOBAL_FreeBlock( pTask->hPDB );
549 /* Free the task module */
551 FreeModule16( pTask->hModule );
553 /* Free the task structure itself */
555 GlobalFree16( hTask );
557 /* Free all memory used by this task (including the 32-bit stack, */
558 /* the environment block and the thunk segments). */
560 GlobalFreeAll16( hPDB );
564 /***********************************************************************
565 * TASK_KillCurrentTask
567 * Kill the currently running task. As it's not possible to kill the
568 * current task like this, it is simply marked for destruction, and will
569 * be killed when either TASK_Reschedule or this function is called again
570 * in the context of another task.
572 void TASK_KillCurrentTask( INT16 exitCode )
574 TDB* pTask = (TDB*) GlobalLock16( GetCurrentTask() );
575 NE_MODULE* pModule = NE_GetPtr( pTask->hModule );
576 if (!pTask) USER_ExitWindows(); /* No current task yet */
578 if ( !THREAD_IsWin16( THREAD_Current() ) )
580 FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
581 return;
584 /* Enter the Win16Lock to protect global data structures
585 NOTE: We never explicitly leave it again. This shouldn't matter
586 though, as it will be released in TASK_Reschedule and this
587 task won't ever get scheduled again ... */
589 SYSLEVEL_EnterWin16Lock();
591 assert(hCurrentTask == GetCurrentTask());
593 TRACE(task, "Killing task %04x\n", hCurrentTask );
595 /* Delete active sockets */
597 if( pTask->pwsi )
598 WINSOCK_DeleteTaskWSI( pTask, pTask->pwsi );
600 #ifdef MZ_SUPPORTED
601 /* Kill DOS VM task */
602 if ( pModule->lpDosTask )
603 MZ_KillModule( pModule->lpDosTask );
604 #endif
606 /* Perform USER cleanup */
608 if (pTask->userhandler)
609 pTask->userhandler( hCurrentTask, USIG_TERMINATION, 0,
610 pTask->hInstance, pTask->hQueue );
612 if (hTaskToKill && (hTaskToKill != hCurrentTask))
614 /* If another task is already marked for destruction, */
615 /* we can kill it now, as we are in another context. */
616 TASK_DeleteTask( hTaskToKill );
619 if (nTaskCount <= 1)
621 TRACE(task, "this is the last task, exiting\n" );
622 USER_ExitWindows();
625 /* FIXME: Hack! Send a message to the initial task so that
626 * the GetMessage wakes up and the initial task can check whether
627 * it is the only remaining one and terminate itself ...
628 * The initial task should probably install hooks or something
629 * to get informed about task termination :-/
631 Callout.PostAppMessage16( PROCESS_Initial()->task, WM_NULL, 0, 0 );
633 /* Remove the task from the list to be sure we never switch back to it */
634 TASK_UnlinkTask( hCurrentTask );
635 if( nTaskCount )
637 TDB* p = (TDB *)GlobalLock16( hFirstTask );
638 while( p )
640 if( p->hYieldTo == hCurrentTask ) p->hYieldTo = 0;
641 p = (TDB *)GlobalLock16( p->hNext );
645 hTaskToKill = hCurrentTask;
646 hLockedTask = 0;
648 pTask->nEvents = 0;
649 TASK_YieldToSystem(pTask);
651 /* We should never return from this Yield() */
653 ERR(task,"Return of the living dead %04x!!!\n", hCurrentTask);
654 exit(1);
657 /***********************************************************************
658 * TASK_Reschedule
660 * This is where all the magic of task-switching happens!
662 * Note: This function should only be called via the TASK_YieldToSystem()
663 * wrapper, to make sure that all the context is saved correctly.
665 * It must not call functions that may yield control.
667 BOOL TASK_Reschedule(void)
669 TDB *pOldTask = NULL, *pNewTask;
670 HTASK16 hTask = 0;
671 STACK16FRAME *newframe16;
672 BOOL pending = FALSE;
674 /* Get the initial task up and running */
675 if (!hCurrentTask && GetCurrentTask())
677 /* We need to remove one pair of stackframes (exept for Winelib) */
678 STACK16FRAME *oldframe16 = CURRENT_STACK16;
679 STACK32FRAME *oldframe32 = oldframe16->frame32;
680 STACK16FRAME *newframe16 = PTR_SEG_TO_LIN( oldframe32->frame16 );
681 STACK32FRAME *newframe32 = newframe16->frame32;
682 if (newframe32)
684 newframe16->entry_ip = oldframe16->entry_ip;
685 newframe16->entry_cs = oldframe16->entry_cs;
686 newframe16->ip = oldframe16->ip;
687 newframe16->cs = oldframe16->cs;
688 newframe32->ebp = oldframe32->ebp;
689 newframe32->restore_addr = oldframe32->restore_addr;
690 newframe32->codeselector = oldframe32->codeselector;
692 THREAD_Current()->cur_stack = oldframe32->frame16;
695 hCurrentTask = GetCurrentTask();
696 pNewTask = (TDB *)GlobalLock16( hCurrentTask );
697 pNewTask->ss_sp = pNewTask->thdb->cur_stack;
698 return FALSE;
701 /* NOTE: As we are entered from 16-bit code, we hold the Win16Lock.
702 We hang onto it thoughout most of this routine, so that accesses
703 to global variables (most notably the task list) are protected. */
704 assert(hCurrentTask == GetCurrentTask());
706 TRACE(task, "entered with hTask %04x (pid %d)\n", hCurrentTask, getpid());
708 #ifdef CONFIG_IPC
709 /* FIXME: What about the Win16Lock ??? */
710 dde_reschedule();
711 #endif
712 /* First check if there's a task to kill */
714 if (hTaskToKill && (hTaskToKill != hCurrentTask))
716 TASK_DeleteTask( hTaskToKill );
717 hTaskToKill = 0;
720 /* Find a task to yield to */
722 pOldTask = (TDB *)GlobalLock16( hCurrentTask );
723 if (pOldTask && pOldTask->hYieldTo)
725 /* check for DirectedYield() */
727 hTask = pOldTask->hYieldTo;
728 pNewTask = (TDB *)GlobalLock16( hTask );
729 if( !pNewTask || !pNewTask->nEvents) hTask = 0;
730 pOldTask->hYieldTo = 0;
733 /* extract hardware events only! */
735 if (!hTask) pending = EVENT_WaitNetEvent( FALSE, TRUE );
737 while (!hTask)
739 /* Find a task that has an event pending */
741 hTask = hFirstTask;
742 while (hTask)
744 pNewTask = (TDB *)GlobalLock16( hTask );
746 TRACE(task, "\ttask = %04x, events = %i\n", hTask, pNewTask->nEvents);
748 if (pNewTask->nEvents) break;
749 hTask = pNewTask->hNext;
751 if (hLockedTask && (hTask != hLockedTask)) hTask = 0;
752 if (hTask) break;
754 /* If a non-hardware event is pending, return to TASK_YieldToSystem
755 temporarily to process it safely */
756 if (pending) return TRUE;
758 /* No task found, wait for some events to come in */
760 /* NOTE: We release the Win16Lock while waiting for events. This is to enable
761 Win32 threads to thunk down to 16-bit temporarily. Since Win16
762 tasks won't execute and Win32 threads are not allowed to enter
763 TASK_Reschedule anyway, there should be no re-entrancy problem ... */
765 SYSLEVEL_ReleaseWin16Lock();
766 pending = EVENT_WaitNetEvent( TRUE, TRUE );
767 SYSLEVEL_RestoreWin16Lock();
770 if (hTask == hCurrentTask)
772 TRACE(task, "returning to the current task(%04x)\n", hTask );
773 return FALSE; /* Nothing to do */
775 pNewTask = (TDB *)GlobalLock16( hTask );
776 TRACE(task, "Switching to task %04x (%.8s)\n",
777 hTask, pNewTask->module_name );
779 /* Make the task the last in the linked list (round-robin scheduling) */
781 pNewTask->priority++;
782 TASK_UnlinkTask( hTask );
783 TASK_LinkTask( hTask );
784 pNewTask->priority--;
786 /* Finish initializing the new task stack if necessary */
788 newframe16 = THREAD_STACK16( pNewTask->thdb );
789 if (!newframe16->entry_cs)
791 STACK16FRAME *oldframe16 = CURRENT_STACK16;
792 STACK32FRAME *oldframe32 = oldframe16->frame32;
793 STACK32FRAME *newframe32 = newframe16->frame32;
794 newframe16->entry_ip = oldframe16->entry_ip;
795 newframe16->entry_cs = oldframe16->entry_cs;
796 newframe16->ip = oldframe16->ip;
797 newframe16->cs = oldframe16->cs;
798 newframe32->ebp = oldframe32->ebp;
799 newframe32->restore_addr = oldframe32->restore_addr;
800 newframe32->codeselector = oldframe32->codeselector;
803 /* Switch to the new stack */
805 /* NOTE: We need to release/restore the Win16Lock, as the task
806 switched to might be at another recursion level than
807 the old task ... */
809 SYSLEVEL_ReleaseWin16Lock();
811 hCurrentTask = hTask;
812 SET_CUR_THREAD( pNewTask->thdb );
813 pNewTask->ss_sp = pNewTask->thdb->cur_stack;
815 SYSLEVEL_RestoreWin16Lock();
817 return FALSE;
821 /***********************************************************************
822 * TASK_YieldToSystem
824 * Scheduler interface, this way we ensure that all "unsafe" events are
825 * processed outside the scheduler.
827 void TASK_YieldToSystem(TDB* pTask)
829 if ( !THREAD_IsWin16( THREAD_Current() ) )
831 FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
832 return;
835 if ( Callbacks->CallTaskRescheduleProc() )
837 /* NOTE: We get here only when no task has an event. This means also
838 the current task, so we shouldn't actually return to the
839 caller here. But, we need to do so, as the EVENT_WaitNetEvent
840 call could lead to a complex series of inter-task SendMessage
841 calls which might leave this task in a state where it again
842 has no event, but where its queue's wakeMask is also reset
843 to zero. Reentering TASK_Reschedule in this state would be
844 suicide. Hence, we do return to the caller after processing
845 non-hardware events. Actually, this should not hurt anyone,
846 as the caller must be WaitEvent, and thus the QUEUE_WaitBits
847 loop in USER. Should there actually be no message pending
848 for this task after processing non-hardware events, that loop
849 will simply return to WaitEvent. */
851 EVENT_WaitNetEvent( FALSE, FALSE );
856 /***********************************************************************
857 * InitTask (KERNEL.91)
859 * Called by the application startup code.
861 void WINAPI InitTask16( CONTEXT *context )
863 TDB *pTask;
864 NE_MODULE *pModule;
865 SEGTABLEENTRY *pSegTable;
866 INSTANCEDATA *pinstance;
867 LONG stacklow, stackhi;
869 if (context) EAX_reg(context) = 0;
870 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
871 if (!(pModule = NE_GetPtr( pTask->hModule ))) return;
873 /* Initialize implicitly loaded DLLs */
874 NE_InitializeDLLs( pTask->hModule );
876 if (context)
878 /* Registers on return are:
879 * ax 1 if OK, 0 on error
880 * cx stack limit in bytes
881 * dx cmdShow parameter
882 * si instance handle of the previous instance
883 * di instance handle of the new task
884 * es:bx pointer to command-line inside PSP
886 * 0 (=%bp) is pushed on the stack
888 SEGPTR ptr = STACK16_PUSH( pTask->thdb, sizeof(WORD) );
889 *(WORD *)PTR_SEG_TO_LIN(ptr) = 0;
890 SP_reg(context) -= 2;
892 EAX_reg(context) = 1;
894 if (!pTask->pdb.cmdLine[0]) EBX_reg(context) = 0x80;
895 else
897 LPBYTE p = &pTask->pdb.cmdLine[1];
898 while ((*p == ' ') || (*p == '\t')) p++;
899 EBX_reg(context) = 0x80 + (p - pTask->pdb.cmdLine);
901 ECX_reg(context) = pModule->stack_size;
902 EDX_reg(context) = pTask->nCmdShow;
903 ESI_reg(context) = (DWORD)pTask->hPrevInstance;
904 EDI_reg(context) = (DWORD)pTask->hInstance;
905 ES_reg (context) = (WORD)pTask->hPDB;
908 /* Initialize the local heap */
909 if ( pModule->heap_size )
911 LocalInit16( pTask->hInstance, 0, pModule->heap_size );
914 /* Initialize the INSTANCEDATA structure */
915 pSegTable = NE_SEG_TABLE( pModule );
916 stacklow = pSegTable[pModule->ss - 1].minsize;
917 stackhi = stacklow + pModule->stack_size;
918 if (stackhi > 0xffff) stackhi = 0xffff;
919 pinstance = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN(CURRENT_DS, 0);
920 pinstance->stackbottom = stackhi; /* yup, that's right. Confused me too. */
921 pinstance->stacktop = stacklow;
922 pinstance->stackmin = OFFSETOF( pTask->thdb->cur_stack );
926 /***********************************************************************
927 * WaitEvent (KERNEL.30)
929 BOOL16 WINAPI WaitEvent16( HTASK16 hTask )
931 TDB *pTask;
933 if (!hTask) hTask = GetCurrentTask();
934 pTask = (TDB *)GlobalLock16( hTask );
936 if ( !THREAD_IsWin16( THREAD_Current() ) )
938 FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
939 return TRUE;
942 if (pTask->nEvents > 0)
944 pTask->nEvents--;
945 return FALSE;
947 TASK_YieldToSystem(pTask);
949 /* When we get back here, we have an event */
951 if (pTask->nEvents > 0) pTask->nEvents--;
952 return TRUE;
956 /***********************************************************************
957 * PostEvent (KERNEL.31)
959 void WINAPI PostEvent16( HTASK16 hTask )
961 TDB *pTask;
963 if (!hTask) hTask = GetCurrentTask();
964 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
966 pTask->nEvents++;
968 if ( !THREAD_IsWin16( THREAD_Current() ) )
970 /* wake-up the scheduler waiting in EVENT_WaitNetEvent */
971 EVENT_WakeUp();
976 /***********************************************************************
977 * SetPriority (KERNEL.32)
979 void WINAPI SetPriority16( HTASK16 hTask, INT16 delta )
981 TDB *pTask;
982 INT16 newpriority;
984 if (!hTask) hTask = GetCurrentTask();
985 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
986 newpriority = pTask->priority + delta;
987 if (newpriority < -32) newpriority = -32;
988 else if (newpriority > 15) newpriority = 15;
990 pTask->priority = newpriority + 1;
991 TASK_UnlinkTask( hTask );
992 TASK_LinkTask( hTask );
993 pTask->priority--;
997 /***********************************************************************
998 * LockCurrentTask (KERNEL.33)
1000 HTASK16 WINAPI LockCurrentTask16( BOOL16 bLock )
1002 if (bLock) hLockedTask = GetCurrentTask();
1003 else hLockedTask = 0;
1004 return hLockedTask;
1008 /***********************************************************************
1009 * IsTaskLocked (KERNEL.122)
1011 HTASK16 WINAPI IsTaskLocked16(void)
1013 return hLockedTask;
1017 /***********************************************************************
1018 * OldYield (KERNEL.117)
1020 void WINAPI OldYield16(void)
1022 TDB *pCurTask = (TDB *)GlobalLock16( GetCurrentTask() );
1024 if ( !THREAD_IsWin16( THREAD_Current() ) )
1026 FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
1027 return;
1030 if (pCurTask) pCurTask->nEvents++; /* Make sure we get back here */
1031 TASK_YieldToSystem(pCurTask);
1032 if (pCurTask) pCurTask->nEvents--;
1036 /***********************************************************************
1037 * DirectedYield (KERNEL.150)
1039 void WINAPI DirectedYield16( HTASK16 hTask )
1041 TDB *pCurTask = (TDB *)GlobalLock16( GetCurrentTask() );
1043 if ( !THREAD_IsWin16( THREAD_Current() ) )
1045 FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
1046 return;
1049 TRACE(task, "%04x: DirectedYield(%04x)\n", pCurTask->hSelf, hTask );
1051 pCurTask->hYieldTo = hTask;
1052 OldYield16();
1054 TRACE(task, "%04x: back from DirectedYield(%04x)\n", pCurTask->hSelf, hTask );
1057 /***********************************************************************
1058 * Yield16 (KERNEL.29)
1060 void WINAPI Yield16(void)
1062 TDB *pCurTask = (TDB *)GlobalLock16( GetCurrentTask() );
1064 if ( !THREAD_IsWin16( THREAD_Current() ) )
1066 FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
1067 return;
1070 if (pCurTask) pCurTask->hYieldTo = 0;
1071 if (pCurTask && pCurTask->hQueue) Callout.UserYield16();
1072 else OldYield16();
1075 /***********************************************************************
1076 * KERNEL_490 (KERNEL.490)
1078 HTASK16 WINAPI KERNEL_490( HTASK16 someTask )
1080 if ( !someTask ) return 0;
1082 FIXME( task, "(%04x): stub\n", someTask );
1083 return 0;
1086 /***********************************************************************
1087 * MakeProcInstance16 (KERNEL.51)
1089 FARPROC16 WINAPI MakeProcInstance16( FARPROC16 func, HANDLE16 hInstance )
1091 BYTE *thunk,*lfunc;
1092 SEGPTR thunkaddr;
1094 if (!func) {
1095 ERR(task, "Ouch ! MakeProcInstance called with func == NULL !\n");
1096 return (FARPROC16)0; /* Windows seems to do the same */
1098 if (!hInstance) hInstance = CURRENT_DS;
1099 thunkaddr = TASK_AllocThunk( GetCurrentTask() );
1100 if (!thunkaddr) return (FARPROC16)0;
1101 thunk = PTR_SEG_TO_LIN( thunkaddr );
1102 lfunc = PTR_SEG_TO_LIN( func );
1104 TRACE(task, "(%08lx,%04x): got thunk %08lx\n",
1105 (DWORD)func, hInstance, (DWORD)thunkaddr );
1106 if (((lfunc[0]==0x8c) && (lfunc[1]==0xd8)) ||
1107 ((lfunc[0]==0x1e) && (lfunc[1]==0x58))
1109 FIXME(task,"thunk would be useless for %p, overwriting with nop;nop;\n", func );
1110 lfunc[0]=0x90; /* nop */
1111 lfunc[1]=0x90; /* nop */
1114 *thunk++ = 0xb8; /* movw instance, %ax */
1115 *thunk++ = (BYTE)(hInstance & 0xff);
1116 *thunk++ = (BYTE)(hInstance >> 8);
1117 *thunk++ = 0xea; /* ljmp func */
1118 *(DWORD *)thunk = (DWORD)func;
1119 return (FARPROC16)thunkaddr;
1123 /***********************************************************************
1124 * FreeProcInstance16 (KERNEL.52)
1126 void WINAPI FreeProcInstance16( FARPROC16 func )
1128 TRACE(task, "(%08lx)\n", (DWORD)func );
1129 TASK_FreeThunk( GetCurrentTask(), (SEGPTR)func );
1133 /**********************************************************************
1134 * GetCodeHandle (KERNEL.93)
1136 HANDLE16 WINAPI GetCodeHandle16( FARPROC16 proc )
1138 HANDLE16 handle;
1139 BYTE *thunk = (BYTE *)PTR_SEG_TO_LIN( proc );
1141 /* Return the code segment containing 'proc'. */
1142 /* Not sure if this is really correct (shouldn't matter that much). */
1144 /* Check if it is really a thunk */
1145 if ((thunk[0] == 0xb8) && (thunk[3] == 0xea))
1146 handle = GlobalHandle16( thunk[6] + (thunk[7] << 8) );
1147 else
1148 handle = GlobalHandle16( HIWORD(proc) );
1150 return handle;
1153 /**********************************************************************
1154 * GetCodeInfo (KERNEL.104)
1156 VOID WINAPI GetCodeInfo16( FARPROC16 proc, SEGINFO *segInfo )
1158 BYTE *thunk = (BYTE *)PTR_SEG_TO_LIN( proc );
1159 NE_MODULE *pModule = NULL;
1160 SEGTABLEENTRY *pSeg = NULL;
1161 WORD segNr;
1163 /* proc is either a thunk, or else a pair of module handle
1164 and segment number. In the first case, we also need to
1165 extract module and segment number. */
1167 if ((thunk[0] == 0xb8) && (thunk[3] == 0xea))
1169 WORD selector = thunk[6] + (thunk[7] << 8);
1170 pModule = NE_GetPtr( GlobalHandle16( selector ) );
1171 pSeg = pModule? NE_SEG_TABLE( pModule ) : NULL;
1173 if ( pModule )
1174 for ( segNr = 0; segNr < pModule->seg_count; segNr++, pSeg++ )
1175 if ( GlobalHandleToSel16(pSeg->hSeg) == selector )
1176 break;
1178 if ( pModule && segNr >= pModule->seg_count )
1179 pSeg = NULL;
1181 else
1183 pModule = NE_GetPtr( HIWORD( proc ) );
1184 segNr = LOWORD( proc );
1186 if ( pModule && segNr < pModule->seg_count )
1187 pSeg = NE_SEG_TABLE( pModule ) + segNr;
1190 /* fill in segment information */
1192 segInfo->offSegment = pSeg? pSeg->filepos : 0;
1193 segInfo->cbSegment = pSeg? pSeg->size : 0;
1194 segInfo->flags = pSeg? pSeg->flags : 0;
1195 segInfo->cbAlloc = pSeg? pSeg->minsize : 0;
1196 segInfo->h = pSeg? pSeg->hSeg : 0;
1197 segInfo->alignShift = pModule? pModule->alignment : 0;
1201 /**********************************************************************
1202 * DefineHandleTable16 (KERNEL.94)
1204 BOOL16 WINAPI DefineHandleTable16( WORD wOffset )
1206 return TRUE; /* FIXME */
1210 /***********************************************************************
1211 * SetTaskQueue (KERNEL.34)
1213 HQUEUE16 WINAPI SetTaskQueue16( HTASK16 hTask, HQUEUE16 hQueue )
1215 HQUEUE16 hPrev;
1216 TDB *pTask;
1218 if (!hTask) hTask = GetCurrentTask();
1219 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
1221 hPrev = pTask->hQueue;
1222 pTask->hQueue = hQueue;
1224 TIMER_SwitchQueue( hPrev, hQueue );
1226 return hPrev;
1230 /***********************************************************************
1231 * GetTaskQueue (KERNEL.35)
1233 HQUEUE16 WINAPI GetTaskQueue16( HTASK16 hTask )
1235 TDB *pTask;
1237 if (!hTask) hTask = GetCurrentTask();
1238 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
1239 return pTask->hQueue;
1242 /***********************************************************************
1243 * SetThreadQueue (KERNEL.463)
1245 HQUEUE16 WINAPI SetThreadQueue16( DWORD thread, HQUEUE16 hQueue )
1247 THDB *thdb = thread? THREAD_IdToTHDB( thread ) : THREAD_Current();
1248 HQUEUE16 oldQueue = thdb? thdb->teb.queue : 0;
1250 if ( thdb )
1252 thdb->teb.queue = hQueue;
1254 if ( GetTaskQueue16( thdb->process->task ) == oldQueue )
1255 SetTaskQueue16( thdb->process->task, hQueue );
1258 return oldQueue;
1261 /***********************************************************************
1262 * GetThreadQueue (KERNEL.464)
1264 HQUEUE16 WINAPI GetThreadQueue16( DWORD thread )
1266 THDB *thdb = NULL;
1267 if ( !thread )
1268 thdb = THREAD_Current();
1269 else if ( HIWORD(thread) )
1270 thdb = THREAD_IdToTHDB( thread );
1271 else if ( IsTask16( (HTASK16)thread ) )
1272 thdb = ((TDB *)GlobalLock16( (HANDLE16)thread ))->thdb;
1274 return (HQUEUE16)(thdb? thdb->teb.queue : 0);
1277 /***********************************************************************
1278 * SetFastQueue (KERNEL.624)
1280 VOID WINAPI SetFastQueue16( DWORD thread, HANDLE hQueue )
1282 THDB *thdb = NULL;
1283 if ( !thread )
1284 thdb = THREAD_Current();
1285 else if ( HIWORD(thread) )
1286 thdb = THREAD_IdToTHDB( thread );
1287 else if ( IsTask16( (HTASK16)thread ) )
1288 thdb = ((TDB *)GlobalLock16( (HANDLE16)thread ))->thdb;
1290 if ( thdb ) thdb->teb.queue = (HQUEUE16) hQueue;
1293 /***********************************************************************
1294 * GetFastQueue (KERNEL.625)
1296 HANDLE WINAPI GetFastQueue16( void )
1298 THDB *thdb = THREAD_Current();
1299 if (!thdb) return 0;
1301 if (!thdb->teb.queue)
1302 Callout.InitThreadInput16( 0, THREAD_IsWin16(thdb)? 4 : 5 );
1304 if (!thdb->teb.queue)
1305 FIXME( task, "(): should initialize thread-local queue, expect failure!\n" );
1307 return (HANDLE)thdb->teb.queue;
1310 /***********************************************************************
1311 * SwitchStackTo (KERNEL.108)
1313 void WINAPI SwitchStackTo16( WORD seg, WORD ptr, WORD top )
1315 TDB *pTask;
1316 STACK16FRAME *oldFrame, *newFrame;
1317 INSTANCEDATA *pData;
1318 UINT16 copySize;
1320 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
1321 if (!(pData = (INSTANCEDATA *)GlobalLock16( seg ))) return;
1322 TRACE(task, "old=%04x:%04x new=%04x:%04x\n",
1323 SELECTOROF( pTask->thdb->cur_stack ),
1324 OFFSETOF( pTask->thdb->cur_stack ), seg, ptr );
1326 /* Save the old stack */
1328 oldFrame = THREAD_STACK16( pTask->thdb );
1329 /* pop frame + args and push bp */
1330 pData->old_ss_sp = pTask->thdb->cur_stack + sizeof(STACK16FRAME)
1331 + 2 * sizeof(WORD);
1332 *(WORD *)PTR_SEG_TO_LIN(pData->old_ss_sp) = oldFrame->bp;
1333 pData->stacktop = top;
1334 pData->stackmin = ptr;
1335 pData->stackbottom = ptr;
1337 /* Switch to the new stack */
1339 /* Note: we need to take the 3 arguments into account; otherwise,
1340 * the stack will underflow upon return from this function.
1342 copySize = oldFrame->bp - OFFSETOF(pData->old_ss_sp);
1343 copySize += 3 * sizeof(WORD) + sizeof(STACK16FRAME);
1344 pTask->thdb->cur_stack = PTR_SEG_OFF_TO_SEGPTR( seg, ptr - copySize );
1345 newFrame = THREAD_STACK16( pTask->thdb );
1347 /* Copy the stack frame and the local variables to the new stack */
1349 memmove( newFrame, oldFrame, copySize );
1350 newFrame->bp = ptr;
1351 *(WORD *)PTR_SEG_OFF_TO_LIN( seg, ptr ) = 0; /* clear previous bp */
1355 /***********************************************************************
1356 * SwitchStackBack (KERNEL.109)
1358 void WINAPI SwitchStackBack16( CONTEXT *context )
1360 TDB *pTask;
1361 STACK16FRAME *oldFrame, *newFrame;
1362 INSTANCEDATA *pData;
1364 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
1365 if (!(pData = (INSTANCEDATA *)GlobalLock16(SELECTOROF(pTask->thdb->cur_stack))))
1366 return;
1367 if (!pData->old_ss_sp)
1369 WARN( task, "No previous SwitchStackTo\n" );
1370 return;
1372 TRACE(task, "restoring stack %04x:%04x\n",
1373 SELECTOROF(pData->old_ss_sp), OFFSETOF(pData->old_ss_sp) );
1375 oldFrame = THREAD_STACK16( pTask->thdb );
1377 /* Pop bp from the previous stack */
1379 BP_reg(context) = *(WORD *)PTR_SEG_TO_LIN(pData->old_ss_sp);
1380 pData->old_ss_sp += sizeof(WORD);
1382 /* Switch back to the old stack */
1384 pTask->thdb->cur_stack = pData->old_ss_sp - sizeof(STACK16FRAME);
1385 SS_reg(context) = SELECTOROF(pData->old_ss_sp);
1386 ESP_reg(context) = OFFSETOF(pData->old_ss_sp) - sizeof(DWORD); /*ret addr*/
1387 pData->old_ss_sp = 0;
1389 /* Build a stack frame for the return */
1391 newFrame = THREAD_STACK16( pTask->thdb );
1392 newFrame->frame32 = oldFrame->frame32;
1393 if (TRACE_ON(relay))
1395 newFrame->entry_ip = oldFrame->entry_ip;
1396 newFrame->entry_cs = oldFrame->entry_cs;
1401 /***********************************************************************
1402 * GetTaskQueueDS (KERNEL.118)
1404 void WINAPI GetTaskQueueDS16( CONTEXT *context )
1406 DS_reg(context) = GlobalHandleToSel16( GetTaskQueue16(0) );
1410 /***********************************************************************
1411 * GetTaskQueueES (KERNEL.119)
1413 void WINAPI GetTaskQueueES16( CONTEXT *context )
1415 ES_reg(context) = GlobalHandleToSel16( GetTaskQueue16(0) );
1419 /***********************************************************************
1420 * GetCurrentTask (KERNEL.36)
1422 HTASK16 WINAPI GetCurrentTask(void)
1424 return THREAD_InitDone? PROCESS_Current()->task : 0;
1427 DWORD WINAPI WIN16_GetCurrentTask(void)
1429 /* This is the version used by relay code; the first task is */
1430 /* returned in the high word of the result */
1431 return MAKELONG( GetCurrentTask(), hFirstTask );
1435 /***********************************************************************
1436 * GetCurrentPDB (KERNEL.37)
1438 * UNDOC: returns PSP of KERNEL in high word
1440 DWORD WINAPI GetCurrentPDB16(void)
1442 TDB *pTask;
1444 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1445 return MAKELONG(pTask->hPDB, 0); /* FIXME */
1449 /***********************************************************************
1450 * GetInstanceData (KERNEL.54)
1452 INT16 WINAPI GetInstanceData16( HINSTANCE16 instance, WORD buffer, INT16 len )
1454 char *ptr = (char *)GlobalLock16( instance );
1455 if (!ptr || !len) return 0;
1456 if ((int)buffer + len >= 0x10000) len = 0x10000 - buffer;
1457 memcpy( (char *)GlobalLock16(CURRENT_DS) + buffer, ptr + buffer, len );
1458 return len;
1462 /***********************************************************************
1463 * GetExeVersion (KERNEL.105)
1465 WORD WINAPI GetExeVersion16(void)
1467 TDB *pTask;
1469 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1470 return pTask->version;
1474 /***********************************************************************
1475 * SetErrorMode16 (KERNEL.107)
1477 UINT16 WINAPI SetErrorMode16( UINT16 mode )
1479 TDB *pTask;
1480 UINT16 oldMode;
1482 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1483 oldMode = pTask->error_mode;
1484 pTask->error_mode = mode;
1485 pTask->thdb->process->error_mode = mode;
1486 return oldMode;
1490 /***********************************************************************
1491 * SetErrorMode32 (KERNEL32.486)
1493 UINT WINAPI SetErrorMode( UINT mode )
1495 return SetErrorMode16( (UINT16)mode );
1499 /***********************************************************************
1500 * GetDOSEnvironment (KERNEL.131)
1502 SEGPTR WINAPI GetDOSEnvironment16(void)
1504 TDB *pTask;
1506 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1507 return PTR_SEG_OFF_TO_SEGPTR( pTask->pdb.environment, 0 );
1511 /***********************************************************************
1512 * GetNumTasks (KERNEL.152)
1514 UINT16 WINAPI GetNumTasks16(void)
1516 return nTaskCount;
1520 /***********************************************************************
1521 * GetTaskDS (KERNEL.155)
1523 * Note: this function apparently returns a DWORD with LOWORD == HIWORD.
1524 * I don't think we need to bother with this.
1526 HINSTANCE16 WINAPI GetTaskDS16(void)
1528 TDB *pTask;
1530 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1531 return pTask->hInstance;
1534 /***********************************************************************
1535 * GetDummyModuleHandleDS (KERNEL.602)
1537 VOID WINAPI GetDummyModuleHandleDS16( CONTEXT *context )
1539 TDB *pTask;
1540 WORD selector;
1542 AX_reg( context ) = 0;
1543 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
1544 if (!(pTask->flags & TDBF_WIN32)) return;
1546 selector = GlobalHandleToSel16( pTask->hModule );
1547 DS_reg( context ) = selector;
1548 AX_reg( context ) = selector;
1551 /***********************************************************************
1552 * IsTask (KERNEL.320)
1554 BOOL16 WINAPI IsTask16( HTASK16 hTask )
1556 TDB *pTask;
1558 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return FALSE;
1559 if (GlobalSize16( hTask ) < sizeof(TDB)) return FALSE;
1560 return (pTask->magic == TDB_MAGIC);
1564 /***********************************************************************
1565 * SetTaskSignalProc (KERNEL.38)
1567 * Real 16-bit interface is provided by the THUNK_SetTaskSignalProc.
1569 FARPROC16 WINAPI SetTaskSignalProc( HTASK16 hTask, FARPROC16 proc )
1571 TDB *pTask;
1572 FARPROC16 oldProc;
1574 if (!hTask) hTask = GetCurrentTask();
1575 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return NULL;
1576 oldProc = (FARPROC16)pTask->userhandler;
1577 pTask->userhandler = (USERSIGNALPROC)proc;
1578 return oldProc;
1582 /***********************************************************************
1583 * SetSigHandler (KERNEL.140)
1585 WORD WINAPI SetSigHandler16( FARPROC16 newhandler, FARPROC16* oldhandler,
1586 UINT16 *oldmode, UINT16 newmode, UINT16 flag )
1588 FIXME(task,"(%p,%p,%p,%d,%d), unimplemented.\n",
1589 newhandler,oldhandler,oldmode,newmode,flag );
1591 if (flag != 1) return 0;
1592 if (!newmode) newhandler = NULL; /* Default handler */
1593 if (newmode != 4)
1595 TDB *pTask;
1597 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1598 if (oldmode) *oldmode = pTask->signal_flags;
1599 pTask->signal_flags = newmode;
1600 if (oldhandler) *oldhandler = pTask->sighandler;
1601 pTask->sighandler = newhandler;
1603 return 0;
1607 /***********************************************************************
1608 * GlobalNotify (KERNEL.154)
1610 * Note that GlobalNotify does _not_ return the old NotifyProc
1611 * -- contrary to LocalNotify !!
1613 VOID WINAPI GlobalNotify16( FARPROC16 proc )
1615 TDB *pTask;
1617 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
1618 pTask->discardhandler = proc;
1622 /***********************************************************************
1623 * GetExePtr (KERNEL.133)
1625 static HMODULE16 GetExePtrHelper( HANDLE16 handle, HTASK16 *hTask )
1627 char *ptr;
1628 HANDLE16 owner;
1630 /* Check for module handle */
1632 if (!(ptr = GlobalLock16( handle ))) return 0;
1633 if (((NE_MODULE *)ptr)->magic == IMAGE_OS2_SIGNATURE) return handle;
1635 /* Search for this handle inside all tasks */
1637 *hTask = hFirstTask;
1638 while (*hTask)
1640 TDB *pTask = (TDB *)GlobalLock16( *hTask );
1641 if ((*hTask == handle) ||
1642 (pTask->hInstance == handle) ||
1643 (pTask->hQueue == handle) ||
1644 (pTask->hPDB == handle)) return pTask->hModule;
1645 *hTask = pTask->hNext;
1648 /* Check the owner for module handle */
1650 owner = FarGetOwner16( handle );
1651 if (!(ptr = GlobalLock16( owner ))) return 0;
1652 if (((NE_MODULE *)ptr)->magic == IMAGE_OS2_SIGNATURE) return owner;
1654 /* Search for the owner inside all tasks */
1656 *hTask = hFirstTask;
1657 while (*hTask)
1659 TDB *pTask = (TDB *)GlobalLock16( *hTask );
1660 if ((*hTask == owner) ||
1661 (pTask->hInstance == owner) ||
1662 (pTask->hQueue == owner) ||
1663 (pTask->hPDB == owner)) return pTask->hModule;
1664 *hTask = pTask->hNext;
1667 return 0;
1670 HMODULE16 WINAPI GetExePtr( HANDLE16 handle )
1672 HTASK16 dummy;
1673 return GetExePtrHelper( handle, &dummy );
1676 void WINAPI WIN16_GetExePtr( CONTEXT *context )
1678 WORD *stack = PTR_SEG_OFF_TO_LIN(SS_reg(context), SP_reg(context));
1679 HANDLE16 handle = (HANDLE16)stack[2];
1680 HTASK16 hTask = 0;
1681 HMODULE16 hModule;
1683 hModule = GetExePtrHelper( handle, &hTask );
1685 AX_reg(context) = CX_reg(context) = hModule;
1686 if (hTask) ES_reg(context) = hTask;
1689 /***********************************************************************
1690 * TaskFirst (TOOLHELP.63)
1692 BOOL16 WINAPI TaskFirst16( TASKENTRY *lpte )
1694 lpte->hNext = hFirstTask;
1695 return TaskNext16( lpte );
1699 /***********************************************************************
1700 * TaskNext (TOOLHELP.64)
1702 BOOL16 WINAPI TaskNext16( TASKENTRY *lpte )
1704 TDB *pTask;
1705 INSTANCEDATA *pInstData;
1707 TRACE(toolhelp, "(%p): task=%04x\n", lpte, lpte->hNext );
1708 if (!lpte->hNext) return FALSE;
1709 pTask = (TDB *)GlobalLock16( lpte->hNext );
1710 if (!pTask || pTask->magic != TDB_MAGIC) return FALSE;
1711 pInstData = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( pTask->hInstance, 0 );
1712 lpte->hTask = lpte->hNext;
1713 lpte->hTaskParent = pTask->hParent;
1714 lpte->hInst = pTask->hInstance;
1715 lpte->hModule = pTask->hModule;
1716 lpte->wSS = SELECTOROF( pTask->thdb->cur_stack );
1717 lpte->wSP = OFFSETOF( pTask->thdb->cur_stack );
1718 lpte->wStackTop = pInstData->stacktop;
1719 lpte->wStackMinimum = pInstData->stackmin;
1720 lpte->wStackBottom = pInstData->stackbottom;
1721 lpte->wcEvents = pTask->nEvents;
1722 lpte->hQueue = pTask->hQueue;
1723 strncpy( lpte->szModule, pTask->module_name, 8 );
1724 lpte->szModule[8] = '\0';
1725 lpte->wPSPOffset = 0x100; /*??*/
1726 lpte->hNext = pTask->hNext;
1727 return TRUE;
1731 /***********************************************************************
1732 * TaskFindHandle (TOOLHELP.65)
1734 BOOL16 WINAPI TaskFindHandle16( TASKENTRY *lpte, HTASK16 hTask )
1736 lpte->hNext = hTask;
1737 return TaskNext16( lpte );
1741 /***********************************************************************
1742 * GetAppCompatFlags16 (KERNEL.354)
1744 DWORD WINAPI GetAppCompatFlags16( HTASK16 hTask )
1746 return GetAppCompatFlags( hTask );
1750 /***********************************************************************
1751 * GetAppCompatFlags32 (USER32.206)
1753 DWORD WINAPI GetAppCompatFlags( HTASK hTask )
1755 TDB *pTask;
1757 if (!hTask) hTask = GetCurrentTask();
1758 if (!(pTask=(TDB *)GlobalLock16( (HTASK16)hTask ))) return 0;
1759 if (GlobalSize16(hTask) < sizeof(TDB)) return 0;
1760 return pTask->compat_flags;