Add support for selection of console mode drivers to use using the
[wine/multimedia.git] / loader / task.c
blobb0aba02d5f66b3e0d289172026a405c37bb610a5
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 "windows.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 BOOL32 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 BOOL32 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 LPTHREAD_START_ROUTINE entry = (LPTHREAD_START_ROUTINE)
246 RVA_PTR(pModule->module32, OptionalHeader.AddressOfEntryPoint);
248 if (PE_HEADER(pModule->module32)->OptionalHeader.Subsystem==IMAGE_SUBSYSTEM_WINDOWS_CUI)
249 AllocConsole();
251 if (pModule->heap_size)
252 LocalInit( pTask->hInstance, 0, pModule->heap_size );
254 InitApp( pTask->hModule );
255 MODULE_InitializeDLLs( PROCESS_Current(), 0, DLL_PROCESS_ATTACH, (LPVOID)-1 );
256 TRACE(relay, "(entryproc=%p)\n", entry );
258 #if 1
259 ExitProcess( entry(NULL) );
260 #else
262 DWORD size = PE_HEADER(pModule->module32)->OptionalHeader.SizeOfStackReserve;
263 DWORD id;
264 THDB *thdb;
266 CreateThread( NULL, size, entry, NULL, 0, &id );
267 thdb = THREAD_ID_TO_THDB( id );
269 while ( thdb->exit_code == 0x103 )
271 WaitEvent( 0 );
272 QUEUE_Signal( pTask->hSelf );
275 ExitProcess( thdb->exit_code );
277 #endif
279 else if (pModule->dos_image)
281 DOSVM_Enter( NULL );
282 ExitProcess( 0 );
284 else
286 /* Registers at initialization must be:
287 * ax zero
288 * bx stack size in bytes
289 * cx heap size in bytes
290 * si previous app instance
291 * di current app instance
292 * bp zero
293 * es selector to the PSP
294 * ds dgroup of the application
295 * ss stack selector
296 * sp top of the stack
298 CONTEXT context;
300 memset( &context, 0, sizeof(context) );
301 CS_reg(&context) = GlobalHandleToSel(pSegTable[pModule->cs - 1].hSeg);
302 DS_reg(&context) = GlobalHandleToSel(pSegTable[pModule->dgroup - 1].hSeg);
303 ES_reg(&context) = pTask->hPDB;
304 EIP_reg(&context) = pModule->ip;
305 EBX_reg(&context) = pModule->stack_size;
306 ECX_reg(&context) = pModule->heap_size;
307 EDI_reg(&context) = context.SegDs;
309 TRACE(task, "Starting main program: cs:ip=%04lx:%04x ds=%04lx ss:sp=%04x:%04x\n",
310 CS_reg(&context), IP_reg(&context), DS_reg(&context),
311 SELECTOROF(pTask->thdb->cur_stack),
312 OFFSETOF(pTask->thdb->cur_stack) );
314 Callbacks->CallRegisterShortProc( &context, 0 );
315 /* This should never return */
316 ERR( task, "Main program returned! (should never happen)\n" );
317 ExitProcess( 1 );
322 /***********************************************************************
323 * TASK_Create
325 * NOTE: This routine might be called by a Win32 thread. We don't have
326 * any real problems with that, since we operated merely on a private
327 * TDB structure that is not yet linked into the task list.
329 HTASK16 TASK_Create( THDB *thdb, NE_MODULE *pModule, HINSTANCE16 hInstance,
330 HINSTANCE16 hPrevInstance, UINT16 cmdShow)
332 HTASK16 hTask;
333 TDB *pTask, *pInitialTask;
334 LPSTR cmd_line;
335 WORD sp;
336 char *stack32Top;
337 char name[10];
338 STACK16FRAME *frame16;
339 STACK32FRAME *frame32;
340 PDB32 *pdb32 = thdb->process;
341 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
343 /* Allocate the task structure */
345 hTask = GLOBAL_Alloc( GMEM_FIXED | GMEM_ZEROINIT, sizeof(TDB),
346 pModule->self, FALSE, FALSE, FALSE );
347 if (!hTask) return 0;
348 pTask = (TDB *)GlobalLock16( hTask );
350 /* Fill the task structure */
352 pTask->nEvents = 1; /* So the task can be started */
353 pTask->hSelf = hTask;
354 pTask->flags = 0;
356 if (pModule->flags & NE_FFLAGS_WIN32)
357 pTask->flags |= TDBF_WIN32;
358 if (pModule->lpDosTask)
359 pTask->flags |= TDBF_WINOLDAP;
361 pTask->version = pModule->expected_version;
362 pTask->hInstance = hInstance;
363 pTask->hPrevInstance = hPrevInstance;
364 pTask->hModule = pModule->self;
365 pTask->hParent = GetCurrentTask();
366 pTask->magic = TDB_MAGIC;
367 pTask->nCmdShow = cmdShow;
368 pTask->thdb = thdb;
369 pTask->curdrive = DRIVE_GetCurrentDrive() | 0x80;
370 strcpy( pTask->curdir, "\\" );
371 lstrcpyn32A( pTask->curdir + 1, DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() ),
372 sizeof(pTask->curdir) - 1 );
374 /* Create the thunks block */
376 TASK_CreateThunks( hTask, (int)&pTask->thunks - (int)pTask, 7 );
378 /* Copy the module name */
380 GetModuleName( pModule->self, name, sizeof(name) );
381 strncpy( pTask->module_name, name, sizeof(pTask->module_name) );
383 /* Allocate a selector for the PDB */
385 pTask->hPDB = GLOBAL_CreateBlock( GMEM_FIXED, &pTask->pdb, sizeof(PDB),
386 pModule->self, FALSE, FALSE, FALSE, NULL );
388 /* Fill the PDB */
390 pTask->pdb.int20 = 0x20cd;
391 pTask->pdb.dispatcher[0] = 0x9a; /* ljmp */
392 PUT_DWORD(&pTask->pdb.dispatcher[1], (DWORD)NE_GetEntryPoint(
393 GetModuleHandle16("KERNEL"), 102 )); /* KERNEL.102 is DOS3Call() */
394 pTask->pdb.savedint22 = INT_GetPMHandler( 0x22 );
395 pTask->pdb.savedint23 = INT_GetPMHandler( 0x23 );
396 pTask->pdb.savedint24 = INT_GetPMHandler( 0x24 );
397 pTask->pdb.fileHandlesPtr =
398 PTR_SEG_OFF_TO_SEGPTR( GlobalHandleToSel(pTask->hPDB),
399 (int)&((PDB *)0)->fileHandles );
400 pTask->pdb.hFileHandles = 0;
401 memset( pTask->pdb.fileHandles, 0xff, sizeof(pTask->pdb.fileHandles) );
402 pTask->pdb.environment = pdb32->env_db->env_sel;
403 pTask->pdb.nbFiles = 20;
405 /* Fill the command line */
407 cmd_line = pdb32->env_db->cmd_line;
408 while (*cmd_line && (*cmd_line != ' ') && (*cmd_line != '\t')) cmd_line++;
409 while ((*cmd_line == ' ') || (*cmd_line == '\t')) cmd_line++;
410 lstrcpyn32A( pTask->pdb.cmdLine+1, cmd_line, sizeof(pTask->pdb.cmdLine)-1);
411 pTask->pdb.cmdLine[0] = strlen( pTask->pdb.cmdLine + 1 );
413 /* Get the compatibility flags */
415 pTask->compat_flags = GetProfileInt32A( "Compatibility", name, 0 );
417 /* Allocate a code segment alias for the TDB */
419 pTask->hCSAlias = GLOBAL_CreateBlock( GMEM_FIXED, (void *)pTask,
420 sizeof(TDB), pTask->hPDB, TRUE,
421 FALSE, FALSE, NULL );
423 /* Set the owner of the environment block */
425 FarSetOwner( pTask->pdb.environment, pTask->hPDB );
427 /* Default DTA overwrites command-line */
429 pTask->dta = PTR_SEG_OFF_TO_SEGPTR( pTask->hPDB,
430 (int)&pTask->pdb.cmdLine - (int)&pTask->pdb );
432 /* Inherit default UserSignalHandler from initial process */
434 pInitialTask = (TDB *)GlobalLock16( PROCESS_Initial()->task );
435 if ( pInitialTask )
436 pTask->userhandler = pInitialTask->userhandler;
438 /* Create the 16-bit stack frame */
440 if (!(sp = pModule->sp))
441 sp = pSegTable[pModule->ss-1].minsize + pModule->stack_size;
442 sp &= ~1; sp -= 2*sizeof(STACK16FRAME);
443 pTask->thdb->cur_stack = PTR_SEG_OFF_TO_SEGPTR( pTask->hInstance, sp );
444 frame16 = (STACK16FRAME *)PTR_SEG_TO_LIN( pTask->thdb->cur_stack );
445 frame16->ebp = sp + (int)&((STACK16FRAME *)0)->bp;
446 frame16->bp = LOWORD(frame16->ebp);
447 frame16->ds = frame16->es = pTask->hInstance;
448 frame16->fs = 0;
449 frame16->entry_point = 0;
450 frame16->entry_cs = 0;
451 frame16->mutex_count = 1; /* TASK_Reschedule is called from 16-bit code */
452 /* The remaining fields will be initialized in TASK_Reschedule */
454 /* Create the 32-bit stack frame */
456 stack32Top = (char*)pTask->thdb->teb.stack_top;
457 frame16->frame32 = frame32 = (STACK32FRAME *)stack32Top - 1;
458 frame32->frame16 = pTask->thdb->cur_stack + sizeof(STACK16FRAME);
459 frame32->edi = 0;
460 frame32->esi = 0;
461 frame32->edx = 0;
462 frame32->ecx = 0;
463 frame32->ebx = 0;
464 frame32->retaddr = (DWORD)TASK_CallToStart;
465 /* The remaining fields will be initialized in TASK_Reschedule */
468 TRACE(task, "module='%s' cmdline='%s' task=%04x\n",
469 name, cmd_line, hTask );
471 return hTask;
474 /***********************************************************************
475 * TASK_StartTask
477 * NOTE: This routine might be called by a Win32 thread. Thus, we need
478 * to be careful to protect global data structures. We do this
479 * by entering the Win16Lock while linking the task into the
480 * global task list.
482 void TASK_StartTask( HTASK16 hTask )
484 /* Add the task to the linked list */
486 SYSLEVEL_EnterWin16Lock();
487 TASK_LinkTask( hTask );
488 SYSLEVEL_LeaveWin16Lock();
490 TRACE(task, "linked task %04x\n", hTask );
492 /* If requested, add entry point breakpoint */
494 if ( TASK_AddTaskEntryBreakpoint )
495 TASK_AddTaskEntryBreakpoint( hTask );
497 /* Get the task up and running. If we ourselves are a 16-bit task,
498 we simply Yield(). If we are 32-bit however, we need to signal
499 the main process somehow (NOT YET IMPLEMENTED!) */
501 if ( THREAD_IsWin16( THREAD_Current() ) )
502 OldYield();
503 else
504 FIXME(task, "Don't know how to start 16-bit task from 32-bit thread. Move the mouse!\n");
508 /***********************************************************************
509 * TASK_DeleteTask
511 static void TASK_DeleteTask( HTASK16 hTask )
513 TDB *pTask;
514 HGLOBAL16 hPDB;
516 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
517 hPDB = pTask->hPDB;
519 pTask->magic = 0xdead; /* invalidate signature */
521 /* Delete the Win32 part of the task */
523 K32OBJ_DecCount( &pTask->thdb->process->header );
524 K32OBJ_DecCount( &pTask->thdb->header );
526 /* Free the selector aliases */
528 GLOBAL_FreeBlock( pTask->hCSAlias );
529 GLOBAL_FreeBlock( pTask->hPDB );
531 /* Free the task module */
533 FreeModule16( pTask->hModule );
535 /* Free the task structure itself */
537 GlobalFree16( hTask );
539 /* Free all memory used by this task (including the 32-bit stack, */
540 /* the environment block and the thunk segments). */
542 GlobalFreeAll( hPDB );
546 /***********************************************************************
547 * TASK_KillCurrentTask
549 * Kill the currently running task. As it's not possible to kill the
550 * current task like this, it is simply marked for destruction, and will
551 * be killed when either TASK_Reschedule or this function is called again
552 * in the context of another task.
554 void TASK_KillCurrentTask( INT16 exitCode )
556 TDB* pTask = (TDB*) GlobalLock16( GetCurrentTask() );
557 NE_MODULE* pModule = NE_GetPtr( pTask->hModule );
558 if (!pTask) USER_ExitWindows(); /* No current task yet */
560 if ( !THREAD_IsWin16( THREAD_Current() ) )
562 FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
563 return;
566 /* Enter the Win16Lock to protect global data structures
567 NOTE: We never explicitly leave it again. This shouldn't matter
568 though, as it will be released in TASK_Reschedule and this
569 task won't ever get scheduled again ... */
571 SYSLEVEL_EnterWin16Lock();
573 assert(hCurrentTask == GetCurrentTask());
575 TRACE(task, "Killing task %04x\n", hCurrentTask );
577 /* Delete active sockets */
579 if( pTask->pwsi )
580 WINSOCK_DeleteTaskWSI( pTask, pTask->pwsi );
582 #ifdef MZ_SUPPORTED
583 /* Kill DOS VM task */
584 if ( pModule->lpDosTask )
585 MZ_KillModule( pModule->lpDosTask );
586 #endif
588 /* Perform USER cleanup */
590 if (pTask->userhandler)
591 pTask->userhandler( hCurrentTask, USIG_TERMINATION, 0,
592 pTask->hInstance, pTask->hQueue );
594 if (hTaskToKill && (hTaskToKill != hCurrentTask))
596 /* If another task is already marked for destruction, */
597 /* we can kill it now, as we are in another context. */
598 TASK_DeleteTask( hTaskToKill );
601 if (nTaskCount <= 1)
603 TRACE(task, "this is the last task, exiting\n" );
604 USER_ExitWindows();
607 /* FIXME: Hack! Send a message to the initial task so that
608 * the GetMessage wakes up and the initial task can check whether
609 * it is the only remaining one and terminate itself ...
610 * The initial task should probably install hooks or something
611 * to get informed about task termination :-/
613 Callout.PostAppMessage16( PROCESS_Initial()->task, WM_NULL, 0, 0 );
615 /* Remove the task from the list to be sure we never switch back to it */
616 TASK_UnlinkTask( hCurrentTask );
617 if( nTaskCount )
619 TDB* p = (TDB *)GlobalLock16( hFirstTask );
620 while( p )
622 if( p->hYieldTo == hCurrentTask ) p->hYieldTo = 0;
623 p = (TDB *)GlobalLock16( p->hNext );
627 hTaskToKill = hCurrentTask;
628 hLockedTask = 0;
630 pTask->nEvents = 0;
631 TASK_YieldToSystem(pTask);
633 /* We should never return from this Yield() */
635 ERR(task,"Return of the living dead %04x!!!\n", hCurrentTask);
636 exit(1);
639 /***********************************************************************
640 * TASK_Reschedule
642 * This is where all the magic of task-switching happens!
644 * Note: This function should only be called via the TASK_YieldToSystem()
645 * wrapper, to make sure that all the context is saved correctly.
647 * It must not call functions that may yield control.
649 BOOL32 TASK_Reschedule(void)
651 TDB *pOldTask = NULL, *pNewTask;
652 HTASK16 hTask = 0;
653 STACK16FRAME *newframe16;
654 BOOL32 pending = FALSE;
656 /* Get the initial task up and running */
657 if (!hCurrentTask && GetCurrentTask())
659 /* We need to remove one pair of stackframes (exept for Winelib) */
660 STACK16FRAME *oldframe16 = CURRENT_STACK16;
661 STACK32FRAME *oldframe32 = oldframe16->frame32;
662 STACK16FRAME *newframe16 = PTR_SEG_TO_LIN( oldframe32->frame16 );
663 STACK32FRAME *newframe32 = newframe16->frame32;
664 if (newframe32)
666 newframe16->entry_ip = oldframe16->entry_ip;
667 newframe16->entry_cs = oldframe16->entry_cs;
668 newframe16->ip = oldframe16->ip;
669 newframe16->cs = oldframe16->cs;
670 newframe32->ebp = oldframe32->ebp;
671 newframe32->restore_addr = oldframe32->restore_addr;
672 newframe32->codeselector = oldframe32->codeselector;
674 THREAD_Current()->cur_stack = oldframe32->frame16;
677 hCurrentTask = GetCurrentTask();
678 pNewTask = (TDB *)GlobalLock16( hCurrentTask );
679 pNewTask->ss_sp = pNewTask->thdb->cur_stack;
680 return FALSE;
683 /* NOTE: As we are entered from 16-bit code, we hold the Win16Lock.
684 We hang onto it thoughout most of this routine, so that accesses
685 to global variables (most notably the task list) are protected. */
686 assert(hCurrentTask == GetCurrentTask());
688 TRACE(task, "entered with hTask %04x (pid %d)\n", hCurrentTask, getpid());
690 #ifdef CONFIG_IPC
691 /* FIXME: What about the Win16Lock ??? */
692 dde_reschedule();
693 #endif
694 /* First check if there's a task to kill */
696 if (hTaskToKill && (hTaskToKill != hCurrentTask))
698 TASK_DeleteTask( hTaskToKill );
699 hTaskToKill = 0;
702 /* Find a task to yield to */
704 pOldTask = (TDB *)GlobalLock16( hCurrentTask );
705 if (pOldTask && pOldTask->hYieldTo)
707 /* check for DirectedYield() */
709 hTask = pOldTask->hYieldTo;
710 pNewTask = (TDB *)GlobalLock16( hTask );
711 if( !pNewTask || !pNewTask->nEvents) hTask = 0;
712 pOldTask->hYieldTo = 0;
715 /* extract hardware events only! */
717 if (!hTask) pending = EVENT_WaitNetEvent( FALSE, TRUE );
719 while (!hTask)
721 /* Find a task that has an event pending */
723 hTask = hFirstTask;
724 while (hTask)
726 pNewTask = (TDB *)GlobalLock16( hTask );
728 TRACE(task, "\ttask = %04x, events = %i\n", hTask, pNewTask->nEvents);
730 if (pNewTask->nEvents) break;
731 hTask = pNewTask->hNext;
733 if (hLockedTask && (hTask != hLockedTask)) hTask = 0;
734 if (hTask) break;
736 /* If a non-hardware event is pending, return to TASK_YieldToSystem
737 temporarily to process it safely */
738 if (pending) return TRUE;
740 /* No task found, wait for some events to come in */
742 /* NOTE: We release the Win16Lock while waiting for events. This is to enable
743 Win32 threads to thunk down to 16-bit temporarily. Since Win16
744 tasks won't execute and Win32 threads are not allowed to enter
745 TASK_Reschedule anyway, there should be no re-entrancy problem ... */
747 SYSLEVEL_ReleaseWin16Lock();
748 pending = EVENT_WaitNetEvent( TRUE, TRUE );
749 SYSLEVEL_RestoreWin16Lock();
752 if (hTask == hCurrentTask)
754 TRACE(task, "returning to the current task(%04x)\n", hTask );
755 return FALSE; /* Nothing to do */
757 pNewTask = (TDB *)GlobalLock16( hTask );
758 TRACE(task, "Switching to task %04x (%.8s)\n",
759 hTask, pNewTask->module_name );
761 /* Make the task the last in the linked list (round-robin scheduling) */
763 pNewTask->priority++;
764 TASK_UnlinkTask( hTask );
765 TASK_LinkTask( hTask );
766 pNewTask->priority--;
768 /* Finish initializing the new task stack if necessary */
770 newframe16 = THREAD_STACK16( pNewTask->thdb );
771 if (!newframe16->entry_cs)
773 STACK16FRAME *oldframe16 = CURRENT_STACK16;
774 STACK32FRAME *oldframe32 = oldframe16->frame32;
775 STACK32FRAME *newframe32 = newframe16->frame32;
776 newframe16->entry_ip = oldframe16->entry_ip;
777 newframe16->entry_cs = oldframe16->entry_cs;
778 newframe16->ip = oldframe16->ip;
779 newframe16->cs = oldframe16->cs;
780 newframe32->ebp = oldframe32->ebp;
781 newframe32->restore_addr = oldframe32->restore_addr;
782 newframe32->codeselector = oldframe32->codeselector;
785 /* Switch to the new stack */
787 /* NOTE: We need to release/restore the Win16Lock, as the task
788 switched to might be at another recursion level than
789 the old task ... */
791 SYSLEVEL_ReleaseWin16Lock();
793 hCurrentTask = hTask;
794 SET_CUR_THREAD( pNewTask->thdb );
795 pNewTask->ss_sp = pNewTask->thdb->cur_stack;
797 SYSLEVEL_RestoreWin16Lock();
799 return FALSE;
803 /***********************************************************************
804 * TASK_YieldToSystem
806 * Scheduler interface, this way we ensure that all "unsafe" events are
807 * processed outside the scheduler.
809 void TASK_YieldToSystem(TDB* pTask)
811 if ( !THREAD_IsWin16( THREAD_Current() ) )
813 FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
814 return;
817 if ( Callbacks->CallTaskRescheduleProc() )
819 /* NOTE: We get here only when no task has an event. This means also
820 the current task, so we shouldn't actually return to the
821 caller here. But, we need to do so, as the EVENT_WaitNetEvent
822 call could lead to a complex series of inter-task SendMessage
823 calls which might leave this task in a state where it again
824 has no event, but where its queue's wakeMask is also reset
825 to zero. Reentering TASK_Reschedule in this state would be
826 suicide. Hence, we do return to the caller after processing
827 non-hardware events. Actually, this should not hurt anyone,
828 as the caller must be WaitEvent, and thus the QUEUE_WaitBits
829 loop in USER. Should there actually be no message pending
830 for this task after processing non-hardware events, that loop
831 will simply return to WaitEvent. */
833 EVENT_WaitNetEvent( FALSE, FALSE );
838 /***********************************************************************
839 * InitTask (KERNEL.91)
841 * Called by the application startup code.
843 void WINAPI InitTask( CONTEXT *context )
845 TDB *pTask;
846 NE_MODULE *pModule;
847 SEGTABLEENTRY *pSegTable;
848 INSTANCEDATA *pinstance;
849 LONG stacklow, stackhi;
851 if (context) EAX_reg(context) = 0;
852 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
853 if (!(pModule = NE_GetPtr( pTask->hModule ))) return;
855 /* Initialize implicitly loaded DLLs */
856 NE_InitializeDLLs( pTask->hModule );
858 if (context)
860 /* Registers on return are:
861 * ax 1 if OK, 0 on error
862 * cx stack limit in bytes
863 * dx cmdShow parameter
864 * si instance handle of the previous instance
865 * di instance handle of the new task
866 * es:bx pointer to command-line inside PSP
868 * 0 (=%bp) is pushed on the stack
870 SEGPTR ptr = STACK16_PUSH( pTask->thdb, sizeof(WORD) );
871 *(WORD *)PTR_SEG_TO_LIN(ptr) = 0;
872 SP_reg(context) -= 2;
874 EAX_reg(context) = 1;
876 if (!pTask->pdb.cmdLine[0]) EBX_reg(context) = 0x80;
877 else
879 LPBYTE p = &pTask->pdb.cmdLine[1];
880 while ((*p == ' ') || (*p == '\t')) p++;
881 EBX_reg(context) = 0x80 + (p - pTask->pdb.cmdLine);
883 ECX_reg(context) = pModule->stack_size;
884 EDX_reg(context) = pTask->nCmdShow;
885 ESI_reg(context) = (DWORD)pTask->hPrevInstance;
886 EDI_reg(context) = (DWORD)pTask->hInstance;
887 ES_reg (context) = (WORD)pTask->hPDB;
890 /* Initialize the local heap */
891 if ( pModule->heap_size )
893 LocalInit( pTask->hInstance, 0, pModule->heap_size );
896 /* Initialize the INSTANCEDATA structure */
897 pSegTable = NE_SEG_TABLE( pModule );
898 stacklow = pSegTable[pModule->ss - 1].minsize;
899 stackhi = stacklow + pModule->stack_size;
900 if (stackhi > 0xffff) stackhi = 0xffff;
901 pinstance = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN(CURRENT_DS, 0);
902 pinstance->stackbottom = stackhi; /* yup, that's right. Confused me too. */
903 pinstance->stacktop = stacklow;
904 pinstance->stackmin = OFFSETOF( pTask->thdb->cur_stack );
908 /***********************************************************************
909 * WaitEvent (KERNEL.30)
911 BOOL16 WINAPI WaitEvent( HTASK16 hTask )
913 TDB *pTask;
915 if (!hTask) hTask = GetCurrentTask();
916 pTask = (TDB *)GlobalLock16( hTask );
918 if ( !THREAD_IsWin16( THREAD_Current() ) )
920 FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
921 return TRUE;
924 if (pTask->nEvents > 0)
926 pTask->nEvents--;
927 return FALSE;
929 TASK_YieldToSystem(pTask);
931 /* When we get back here, we have an event */
933 if (pTask->nEvents > 0) pTask->nEvents--;
934 return TRUE;
938 /***********************************************************************
939 * PostEvent (KERNEL.31)
941 void WINAPI PostEvent( HTASK16 hTask )
943 TDB *pTask;
945 if (!hTask) hTask = GetCurrentTask();
946 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
948 if ( !THREAD_IsWin16( THREAD_Current() ) )
950 WARN(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
951 /* return; */
954 pTask->nEvents++;
958 /***********************************************************************
959 * SetPriority (KERNEL.32)
961 void WINAPI SetPriority( HTASK16 hTask, INT16 delta )
963 TDB *pTask;
964 INT16 newpriority;
966 if (!hTask) hTask = GetCurrentTask();
967 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
968 newpriority = pTask->priority + delta;
969 if (newpriority < -32) newpriority = -32;
970 else if (newpriority > 15) newpriority = 15;
972 pTask->priority = newpriority + 1;
973 TASK_UnlinkTask( hTask );
974 TASK_LinkTask( hTask );
975 pTask->priority--;
979 /***********************************************************************
980 * LockCurrentTask (KERNEL.33)
982 HTASK16 WINAPI LockCurrentTask( BOOL16 bLock )
984 if (bLock) hLockedTask = GetCurrentTask();
985 else hLockedTask = 0;
986 return hLockedTask;
990 /***********************************************************************
991 * IsTaskLocked (KERNEL.122)
993 HTASK16 WINAPI IsTaskLocked(void)
995 return hLockedTask;
999 /***********************************************************************
1000 * OldYield (KERNEL.117)
1002 void WINAPI OldYield(void)
1004 TDB *pCurTask = (TDB *)GlobalLock16( GetCurrentTask() );
1006 if ( !THREAD_IsWin16( THREAD_Current() ) )
1008 FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
1009 return;
1012 if (pCurTask) pCurTask->nEvents++; /* Make sure we get back here */
1013 TASK_YieldToSystem(pCurTask);
1014 if (pCurTask) pCurTask->nEvents--;
1018 /***********************************************************************
1019 * DirectedYield (KERNEL.150)
1021 void WINAPI DirectedYield( HTASK16 hTask )
1023 TDB *pCurTask = (TDB *)GlobalLock16( GetCurrentTask() );
1025 if ( !THREAD_IsWin16( THREAD_Current() ) )
1027 FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
1028 return;
1031 TRACE(task, "%04x: DirectedYield(%04x)\n", pCurTask->hSelf, hTask );
1033 pCurTask->hYieldTo = hTask;
1034 OldYield();
1036 TRACE(task, "%04x: back from DirectedYield(%04x)\n", pCurTask->hSelf, hTask );
1039 /***********************************************************************
1040 * Yield16 (KERNEL.29)
1042 void WINAPI Yield16(void)
1044 TDB *pCurTask = (TDB *)GlobalLock16( GetCurrentTask() );
1046 if ( !THREAD_IsWin16( THREAD_Current() ) )
1048 FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
1049 return;
1052 if (pCurTask) pCurTask->hYieldTo = 0;
1053 if (pCurTask && pCurTask->hQueue) Callout.UserYield();
1054 else OldYield();
1057 /***********************************************************************
1058 * KERNEL_490 (KERNEL.490)
1060 HTASK16 WINAPI KERNEL_490( HTASK16 someTask )
1062 if ( !someTask ) return 0;
1064 FIXME( task, "(%04x): stub\n", someTask );
1065 return 0;
1068 /***********************************************************************
1069 * MakeProcInstance16 (KERNEL.51)
1071 FARPROC16 WINAPI MakeProcInstance16( FARPROC16 func, HANDLE16 hInstance )
1073 BYTE *thunk,*lfunc;
1074 SEGPTR thunkaddr;
1076 if (!func) {
1077 ERR(task, "Ouch ! MakeProcInstance called with func == NULL !\n");
1078 return (FARPROC16)0; /* Windows seems to do the same */
1080 if (!hInstance) hInstance = CURRENT_DS;
1081 thunkaddr = TASK_AllocThunk( GetCurrentTask() );
1082 if (!thunkaddr) return (FARPROC16)0;
1083 thunk = PTR_SEG_TO_LIN( thunkaddr );
1084 lfunc = PTR_SEG_TO_LIN( func );
1086 TRACE(task, "(%08lx,%04x): got thunk %08lx\n",
1087 (DWORD)func, hInstance, (DWORD)thunkaddr );
1088 if (((lfunc[0]==0x8c) && (lfunc[1]==0xd8)) ||
1089 ((lfunc[0]==0x1e) && (lfunc[1]==0x58))
1091 FIXME(task,"thunk would be useless for %p, overwriting with nop;nop;\n", func );
1092 lfunc[0]=0x90; /* nop */
1093 lfunc[1]=0x90; /* nop */
1096 *thunk++ = 0xb8; /* movw instance, %ax */
1097 *thunk++ = (BYTE)(hInstance & 0xff);
1098 *thunk++ = (BYTE)(hInstance >> 8);
1099 *thunk++ = 0xea; /* ljmp func */
1100 *(DWORD *)thunk = (DWORD)func;
1101 return (FARPROC16)thunkaddr;
1105 /***********************************************************************
1106 * FreeProcInstance16 (KERNEL.52)
1108 void WINAPI FreeProcInstance16( FARPROC16 func )
1110 TRACE(task, "(%08lx)\n", (DWORD)func );
1111 TASK_FreeThunk( GetCurrentTask(), (SEGPTR)func );
1115 /**********************************************************************
1116 * GetCodeHandle (KERNEL.93)
1118 HANDLE16 WINAPI GetCodeHandle( FARPROC16 proc )
1120 HANDLE16 handle;
1121 BYTE *thunk = (BYTE *)PTR_SEG_TO_LIN( proc );
1123 /* Return the code segment containing 'proc'. */
1124 /* Not sure if this is really correct (shouldn't matter that much). */
1126 /* Check if it is really a thunk */
1127 if ((thunk[0] == 0xb8) && (thunk[3] == 0xea))
1128 handle = GlobalHandle16( thunk[6] + (thunk[7] << 8) );
1129 else
1130 handle = GlobalHandle16( HIWORD(proc) );
1132 return handle;
1135 /**********************************************************************
1136 * GetCodeInfo (KERNEL.104)
1138 VOID WINAPI GetCodeInfo( FARPROC16 proc, SEGINFO *segInfo )
1140 BYTE *thunk = (BYTE *)PTR_SEG_TO_LIN( proc );
1141 NE_MODULE *pModule = NULL;
1142 SEGTABLEENTRY *pSeg = NULL;
1143 WORD segNr;
1145 /* proc is either a thunk, or else a pair of module handle
1146 and segment number. In the first case, we also need to
1147 extract module and segment number. */
1149 if ((thunk[0] == 0xb8) && (thunk[3] == 0xea))
1151 WORD selector = thunk[6] + (thunk[7] << 8);
1152 pModule = NE_GetPtr( GlobalHandle16( selector ) );
1153 pSeg = pModule? NE_SEG_TABLE( pModule ) : NULL;
1155 if ( pModule )
1156 for ( segNr = 0; segNr < pModule->seg_count; segNr++, pSeg++ )
1157 if ( GlobalHandleToSel(pSeg->hSeg) == selector )
1158 break;
1160 if ( pModule && segNr >= pModule->seg_count )
1161 pSeg = NULL;
1163 else
1165 pModule = NE_GetPtr( HIWORD( proc ) );
1166 segNr = LOWORD( proc );
1168 if ( pModule && segNr < pModule->seg_count )
1169 pSeg = NE_SEG_TABLE( pModule ) + segNr;
1172 /* fill in segment information */
1174 segInfo->offSegment = pSeg? pSeg->filepos : 0;
1175 segInfo->cbSegment = pSeg? pSeg->size : 0;
1176 segInfo->flags = pSeg? pSeg->flags : 0;
1177 segInfo->cbAlloc = pSeg? pSeg->minsize : 0;
1178 segInfo->h = pSeg? pSeg->hSeg : 0;
1179 segInfo->alignShift = pModule? pModule->alignment : 0;
1183 /**********************************************************************
1184 * DefineHandleTable16 (KERNEL.94)
1186 BOOL16 WINAPI DefineHandleTable16( WORD wOffset )
1188 return TRUE; /* FIXME */
1192 /***********************************************************************
1193 * SetTaskQueue (KERNEL.34)
1195 HQUEUE16 WINAPI SetTaskQueue( HTASK16 hTask, HQUEUE16 hQueue )
1197 HQUEUE16 hPrev;
1198 TDB *pTask;
1200 if (!hTask) hTask = GetCurrentTask();
1201 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
1203 hPrev = pTask->hQueue;
1204 pTask->hQueue = hQueue;
1206 TIMER_SwitchQueue( hPrev, hQueue );
1208 return hPrev;
1212 /***********************************************************************
1213 * GetTaskQueue (KERNEL.35)
1215 HQUEUE16 WINAPI GetTaskQueue( HTASK16 hTask )
1217 TDB *pTask;
1219 if (!hTask) hTask = GetCurrentTask();
1220 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
1221 return pTask->hQueue;
1224 /***********************************************************************
1225 * SetThreadQueue (KERNEL.463)
1227 HQUEUE16 WINAPI SetThreadQueue( DWORD thread, HQUEUE16 hQueue )
1229 THDB *thdb = thread? THREAD_IdToTHDB( thread ) : THREAD_Current();
1230 HQUEUE16 oldQueue = thdb? thdb->teb.queue : 0;
1232 if ( thdb )
1234 thdb->teb.queue = hQueue;
1236 if ( GetTaskQueue( thdb->process->task ) == oldQueue )
1237 SetTaskQueue( thdb->process->task, hQueue );
1240 return oldQueue;
1243 /***********************************************************************
1244 * GetThreadQueue (KERNEL.464)
1246 HQUEUE16 WINAPI GetThreadQueue( DWORD thread )
1248 THDB *thdb = thread? THREAD_IdToTHDB( thread ) : THREAD_Current();
1249 return (HQUEUE16)(thdb? thdb->teb.queue : 0);
1252 /***********************************************************************
1253 * SetFastQueue (KERNEL.624)
1255 VOID WINAPI SetFastQueue( DWORD thread, HANDLE32 hQueue )
1257 THDB *thdb = thread? THREAD_IdToTHDB( thread ) : THREAD_Current();
1258 if ( thdb ) thdb->teb.queue = (HQUEUE16) hQueue;
1261 /***********************************************************************
1262 * GetFastQueue (KERNEL.625)
1264 HANDLE32 WINAPI GetFastQueue( void )
1266 THDB *thdb = THREAD_Current();
1267 if (!thdb) return 0;
1269 if (!thdb->teb.queue)
1270 Callout.InitThreadInput( 0, THREAD_IsWin16(thdb)? 4 : 5 );
1272 if (!thdb->teb.queue)
1273 FIXME( task, "(): should initialize thread-local queue, expect failure!\n" );
1275 return (HANDLE32)thdb->teb.queue;
1278 /***********************************************************************
1279 * SwitchStackTo (KERNEL.108)
1281 void WINAPI SwitchStackTo( WORD seg, WORD ptr, WORD top )
1283 TDB *pTask;
1284 STACK16FRAME *oldFrame, *newFrame;
1285 INSTANCEDATA *pData;
1286 UINT16 copySize;
1288 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
1289 if (!(pData = (INSTANCEDATA *)GlobalLock16( seg ))) return;
1290 TRACE(task, "old=%04x:%04x new=%04x:%04x\n",
1291 SELECTOROF( pTask->thdb->cur_stack ),
1292 OFFSETOF( pTask->thdb->cur_stack ), seg, ptr );
1294 /* Save the old stack */
1296 oldFrame = THREAD_STACK16( pTask->thdb );
1297 /* pop frame + args and push bp */
1298 pData->old_ss_sp = pTask->thdb->cur_stack + sizeof(STACK16FRAME)
1299 + 2 * sizeof(WORD);
1300 *(WORD *)PTR_SEG_TO_LIN(pData->old_ss_sp) = oldFrame->bp;
1301 pData->stacktop = top;
1302 pData->stackmin = ptr;
1303 pData->stackbottom = ptr;
1305 /* Switch to the new stack */
1307 /* Note: we need to take the 3 arguments into account; otherwise,
1308 * the stack will underflow upon return from this function.
1310 copySize = oldFrame->bp - OFFSETOF(pData->old_ss_sp);
1311 copySize += 3 * sizeof(WORD) + sizeof(STACK16FRAME);
1312 pTask->thdb->cur_stack = PTR_SEG_OFF_TO_SEGPTR( seg, ptr - copySize );
1313 newFrame = THREAD_STACK16( pTask->thdb );
1315 /* Copy the stack frame and the local variables to the new stack */
1317 memmove( newFrame, oldFrame, copySize );
1318 newFrame->bp = ptr;
1319 *(WORD *)PTR_SEG_OFF_TO_LIN( seg, ptr ) = 0; /* clear previous bp */
1323 /***********************************************************************
1324 * SwitchStackBack (KERNEL.109)
1326 void WINAPI SwitchStackBack( CONTEXT *context )
1328 TDB *pTask;
1329 STACK16FRAME *oldFrame, *newFrame;
1330 INSTANCEDATA *pData;
1332 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
1333 if (!(pData = (INSTANCEDATA *)GlobalLock16(SELECTOROF(pTask->thdb->cur_stack))))
1334 return;
1335 if (!pData->old_ss_sp)
1337 WARN( task, "No previous SwitchStackTo\n" );
1338 return;
1340 TRACE(task, "restoring stack %04x:%04x\n",
1341 SELECTOROF(pData->old_ss_sp), OFFSETOF(pData->old_ss_sp) );
1343 oldFrame = THREAD_STACK16( pTask->thdb );
1345 /* Pop bp from the previous stack */
1347 BP_reg(context) = *(WORD *)PTR_SEG_TO_LIN(pData->old_ss_sp);
1348 pData->old_ss_sp += sizeof(WORD);
1350 /* Switch back to the old stack */
1352 pTask->thdb->cur_stack = pData->old_ss_sp - sizeof(STACK16FRAME);
1353 SS_reg(context) = SELECTOROF(pData->old_ss_sp);
1354 ESP_reg(context) = OFFSETOF(pData->old_ss_sp) - sizeof(DWORD); /*ret addr*/
1355 pData->old_ss_sp = 0;
1357 /* Build a stack frame for the return */
1359 newFrame = THREAD_STACK16( pTask->thdb );
1360 newFrame->frame32 = oldFrame->frame32;
1361 if (TRACE_ON(relay))
1363 newFrame->entry_ip = oldFrame->entry_ip;
1364 newFrame->entry_cs = oldFrame->entry_cs;
1369 /***********************************************************************
1370 * GetTaskQueueDS (KERNEL.118)
1372 void WINAPI GetTaskQueueDS( CONTEXT *context )
1374 DS_reg(context) = GlobalHandleToSel( GetTaskQueue(0) );
1378 /***********************************************************************
1379 * GetTaskQueueES (KERNEL.119)
1381 void WINAPI GetTaskQueueES( CONTEXT *context )
1383 ES_reg(context) = GlobalHandleToSel( GetTaskQueue(0) );
1387 /***********************************************************************
1388 * GetCurrentTask (KERNEL.36)
1390 HTASK16 WINAPI GetCurrentTask(void)
1392 return THREAD_InitDone? PROCESS_Current()->task : 0;
1395 DWORD WINAPI WIN16_GetCurrentTask(void)
1397 /* This is the version used by relay code; the first task is */
1398 /* returned in the high word of the result */
1399 return MAKELONG( GetCurrentTask(), hFirstTask );
1403 /***********************************************************************
1404 * GetCurrentPDB (KERNEL.37)
1406 * UNDOC: returns PSP of KERNEL in high word
1408 DWORD WINAPI GetCurrentPDB(void)
1410 TDB *pTask;
1412 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1413 return MAKELONG(pTask->hPDB, 0); /* FIXME */
1417 /***********************************************************************
1418 * GetInstanceData (KERNEL.54)
1420 INT16 WINAPI GetInstanceData( HINSTANCE16 instance, WORD buffer, INT16 len )
1422 char *ptr = (char *)GlobalLock16( instance );
1423 if (!ptr || !len) return 0;
1424 if ((int)buffer + len >= 0x10000) len = 0x10000 - buffer;
1425 memcpy( (char *)GlobalLock16(CURRENT_DS) + buffer, ptr + buffer, len );
1426 return len;
1430 /***********************************************************************
1431 * GetExeVersion (KERNEL.105)
1433 WORD WINAPI GetExeVersion(void)
1435 TDB *pTask;
1437 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1438 return pTask->version;
1442 /***********************************************************************
1443 * SetErrorMode16 (KERNEL.107)
1445 UINT16 WINAPI SetErrorMode16( UINT16 mode )
1447 TDB *pTask;
1448 UINT16 oldMode;
1450 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1451 oldMode = pTask->error_mode;
1452 pTask->error_mode = mode;
1453 pTask->thdb->process->error_mode = mode;
1454 return oldMode;
1458 /***********************************************************************
1459 * SetErrorMode32 (KERNEL32.486)
1461 UINT32 WINAPI SetErrorMode32( UINT32 mode )
1463 return SetErrorMode16( (UINT16)mode );
1467 /***********************************************************************
1468 * GetDOSEnvironment (KERNEL.131)
1470 SEGPTR WINAPI GetDOSEnvironment(void)
1472 TDB *pTask;
1474 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1475 return PTR_SEG_OFF_TO_SEGPTR( pTask->pdb.environment, 0 );
1479 /***********************************************************************
1480 * GetNumTasks (KERNEL.152)
1482 UINT16 WINAPI GetNumTasks(void)
1484 return nTaskCount;
1488 /***********************************************************************
1489 * GetTaskDS (KERNEL.155)
1491 * Note: this function apparently returns a DWORD with LOWORD == HIWORD.
1492 * I don't think we need to bother with this.
1494 HINSTANCE16 WINAPI GetTaskDS(void)
1496 TDB *pTask;
1498 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1499 return pTask->hInstance;
1502 /***********************************************************************
1503 * GetDummyModuleHandleDS (KERNEL.602)
1505 VOID WINAPI GetDummyModuleHandleDS( CONTEXT *context )
1507 TDB *pTask;
1508 WORD selector;
1510 AX_reg( context ) = 0;
1511 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
1512 if (!(pTask->flags & TDBF_WIN32)) return;
1514 selector = GlobalHandleToSel( pTask->hModule );
1515 DS_reg( context ) = selector;
1516 AX_reg( context ) = selector;
1519 /***********************************************************************
1520 * IsTask (KERNEL.320)
1522 BOOL16 WINAPI IsTask( HTASK16 hTask )
1524 TDB *pTask;
1526 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return FALSE;
1527 if (GlobalSize16( hTask ) < sizeof(TDB)) return FALSE;
1528 return (pTask->magic == TDB_MAGIC);
1532 /***********************************************************************
1533 * SetTaskSignalProc (KERNEL.38)
1535 * Real 16-bit interface is provided by the THUNK_SetTaskSignalProc.
1537 FARPROC16 WINAPI SetTaskSignalProc( HTASK16 hTask, FARPROC16 proc )
1539 TDB *pTask;
1540 FARPROC16 oldProc;
1542 if (!hTask) hTask = GetCurrentTask();
1543 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return NULL;
1544 oldProc = (FARPROC16)pTask->userhandler;
1545 pTask->userhandler = (USERSIGNALPROC)proc;
1546 return oldProc;
1550 /***********************************************************************
1551 * SetSigHandler (KERNEL.140)
1553 WORD WINAPI SetSigHandler( FARPROC16 newhandler, FARPROC16* oldhandler,
1554 UINT16 *oldmode, UINT16 newmode, UINT16 flag )
1556 FIXME(task,"(%p,%p,%p,%d,%d), unimplemented.\n",
1557 newhandler,oldhandler,oldmode,newmode,flag );
1559 if (flag != 1) return 0;
1560 if (!newmode) newhandler = NULL; /* Default handler */
1561 if (newmode != 4)
1563 TDB *pTask;
1565 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1566 if (oldmode) *oldmode = pTask->signal_flags;
1567 pTask->signal_flags = newmode;
1568 if (oldhandler) *oldhandler = pTask->sighandler;
1569 pTask->sighandler = newhandler;
1571 return 0;
1575 /***********************************************************************
1576 * GlobalNotify (KERNEL.154)
1578 * Note that GlobalNotify does _not_ return the old NotifyProc
1579 * -- contrary to LocalNotify !!
1581 VOID WINAPI GlobalNotify( FARPROC16 proc )
1583 TDB *pTask;
1585 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
1586 pTask->discardhandler = proc;
1590 /***********************************************************************
1591 * GetExePtr (KERNEL.133)
1593 static HMODULE16 GetExePtrHelper( HANDLE16 handle, HTASK16 *hTask )
1595 char *ptr;
1596 HANDLE16 owner;
1598 /* Check for module handle */
1600 if (!(ptr = GlobalLock16( handle ))) return 0;
1601 if (((NE_MODULE *)ptr)->magic == IMAGE_OS2_SIGNATURE) return handle;
1603 /* Search for this handle inside all tasks */
1605 *hTask = hFirstTask;
1606 while (*hTask)
1608 TDB *pTask = (TDB *)GlobalLock16( *hTask );
1609 if ((*hTask == handle) ||
1610 (pTask->hInstance == handle) ||
1611 (pTask->hQueue == handle) ||
1612 (pTask->hPDB == handle)) return pTask->hModule;
1613 *hTask = pTask->hNext;
1616 /* Check the owner for module handle */
1618 owner = FarGetOwner( handle );
1619 if (!(ptr = GlobalLock16( owner ))) return 0;
1620 if (((NE_MODULE *)ptr)->magic == IMAGE_OS2_SIGNATURE) return owner;
1622 /* Search for the owner inside all tasks */
1624 *hTask = hFirstTask;
1625 while (*hTask)
1627 TDB *pTask = (TDB *)GlobalLock16( *hTask );
1628 if ((*hTask == owner) ||
1629 (pTask->hInstance == owner) ||
1630 (pTask->hQueue == owner) ||
1631 (pTask->hPDB == owner)) return pTask->hModule;
1632 *hTask = pTask->hNext;
1635 return 0;
1638 HMODULE16 WINAPI GetExePtr( HANDLE16 handle )
1640 HTASK16 dummy;
1641 return GetExePtrHelper( handle, &dummy );
1644 void WINAPI WIN16_GetExePtr( CONTEXT *context )
1646 WORD *stack = PTR_SEG_OFF_TO_LIN(SS_reg(context), SP_reg(context));
1647 HANDLE16 handle = (HANDLE16)stack[2];
1648 HTASK16 hTask = 0;
1649 HMODULE16 hModule;
1651 hModule = GetExePtrHelper( handle, &hTask );
1653 AX_reg(context) = CX_reg(context) = hModule;
1654 if (hTask) ES_reg(context) = hTask;
1657 /***********************************************************************
1658 * TaskFirst (TOOLHELP.63)
1660 BOOL16 WINAPI TaskFirst( TASKENTRY *lpte )
1662 lpte->hNext = hFirstTask;
1663 return TaskNext( lpte );
1667 /***********************************************************************
1668 * TaskNext (TOOLHELP.64)
1670 BOOL16 WINAPI TaskNext( TASKENTRY *lpte )
1672 TDB *pTask;
1673 INSTANCEDATA *pInstData;
1675 TRACE(toolhelp, "(%p): task=%04x\n", lpte, lpte->hNext );
1676 if (!lpte->hNext) return FALSE;
1677 pTask = (TDB *)GlobalLock16( lpte->hNext );
1678 if (!pTask || pTask->magic != TDB_MAGIC) return FALSE;
1679 pInstData = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( pTask->hInstance, 0 );
1680 lpte->hTask = lpte->hNext;
1681 lpte->hTaskParent = pTask->hParent;
1682 lpte->hInst = pTask->hInstance;
1683 lpte->hModule = pTask->hModule;
1684 lpte->wSS = SELECTOROF( pTask->thdb->cur_stack );
1685 lpte->wSP = OFFSETOF( pTask->thdb->cur_stack );
1686 lpte->wStackTop = pInstData->stacktop;
1687 lpte->wStackMinimum = pInstData->stackmin;
1688 lpte->wStackBottom = pInstData->stackbottom;
1689 lpte->wcEvents = pTask->nEvents;
1690 lpte->hQueue = pTask->hQueue;
1691 strncpy( lpte->szModule, pTask->module_name, 8 );
1692 lpte->szModule[8] = '\0';
1693 lpte->wPSPOffset = 0x100; /*??*/
1694 lpte->hNext = pTask->hNext;
1695 return TRUE;
1699 /***********************************************************************
1700 * TaskFindHandle (TOOLHELP.65)
1702 BOOL16 WINAPI TaskFindHandle( TASKENTRY *lpte, HTASK16 hTask )
1704 lpte->hNext = hTask;
1705 return TaskNext( lpte );
1709 /***********************************************************************
1710 * GetAppCompatFlags16 (KERNEL.354)
1712 DWORD WINAPI GetAppCompatFlags16( HTASK16 hTask )
1714 return GetAppCompatFlags32( hTask );
1718 /***********************************************************************
1719 * GetAppCompatFlags32 (USER32.206)
1721 DWORD WINAPI GetAppCompatFlags32( HTASK32 hTask )
1723 TDB *pTask;
1725 if (!hTask) hTask = GetCurrentTask();
1726 if (!(pTask=(TDB *)GlobalLock16( (HTASK16)hTask ))) return 0;
1727 if (GlobalSize16(hTask) < sizeof(TDB)) return 0;
1728 return pTask->compat_flags;