Small patch.
[wine/multimedia.git] / loader / task.c
blob0f0e0d476dff90bd9128bd1b7226fbfd2493a073
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 MODULE_InitializeDLLs( PROCESS_Current(), 0, DLL_PROCESS_ATTACH, (LPVOID)-1 );
255 TRACE(relay, "(entryproc=%p)\n", entry );
257 #if 1
258 ExitProcess( entry(NULL) );
259 #else
261 DWORD size = PE_HEADER(pModule->module32)->OptionalHeader.SizeOfStackReserve;
262 DWORD id;
263 THDB *thdb;
265 CreateThread( NULL, size, entry, NULL, 0, &id );
266 thdb = THREAD_ID_TO_THDB( id );
268 while ( thdb->exit_code == 0x103 )
270 WaitEvent( 0 );
271 QUEUE_Signal( pTask->hSelf );
274 ExitProcess( thdb->exit_code );
276 #endif
278 else if (pModule->dos_image)
280 DOSVM_Enter( NULL );
281 ExitProcess( 0 );
283 else
285 /* Registers at initialization must be:
286 * ax zero
287 * bx stack size in bytes
288 * cx heap size in bytes
289 * si previous app instance
290 * di current app instance
291 * bp zero
292 * es selector to the PSP
293 * ds dgroup of the application
294 * ss stack selector
295 * sp top of the stack
297 CONTEXT context;
299 memset( &context, 0, sizeof(context) );
300 CS_reg(&context) = GlobalHandleToSel(pSegTable[pModule->cs - 1].hSeg);
301 DS_reg(&context) = GlobalHandleToSel(pSegTable[pModule->dgroup - 1].hSeg);
302 ES_reg(&context) = pTask->hPDB;
303 EIP_reg(&context) = pModule->ip;
304 EBX_reg(&context) = pModule->stack_size;
305 ECX_reg(&context) = pModule->heap_size;
306 EDI_reg(&context) = context.SegDs;
308 TRACE(task, "Starting main program: cs:ip=%04lx:%04x ds=%04lx ss:sp=%04x:%04x\n",
309 CS_reg(&context), IP_reg(&context), DS_reg(&context),
310 SELECTOROF(pTask->thdb->cur_stack),
311 OFFSETOF(pTask->thdb->cur_stack) );
313 Callbacks->CallRegisterShortProc( &context, 0 );
314 /* This should never return */
315 ERR( task, "Main program returned! (should never happen)\n" );
316 ExitProcess( 1 );
321 /***********************************************************************
322 * TASK_Create
324 * NOTE: This routine might be called by a Win32 thread. We don't have
325 * any real problems with that, since we operated merely on a private
326 * TDB structure that is not yet linked into the task list.
328 HTASK16 TASK_Create( THDB *thdb, NE_MODULE *pModule, HINSTANCE16 hInstance,
329 HINSTANCE16 hPrevInstance, UINT16 cmdShow)
331 HTASK16 hTask;
332 TDB *pTask, *pInitialTask;
333 LPSTR cmd_line;
334 WORD sp;
335 char *stack32Top;
336 char name[10];
337 STACK16FRAME *frame16;
338 STACK32FRAME *frame32;
339 PDB32 *pdb32 = thdb->process;
340 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
342 /* Allocate the task structure */
344 hTask = GLOBAL_Alloc( GMEM_FIXED | GMEM_ZEROINIT, sizeof(TDB),
345 pModule->self, FALSE, FALSE, FALSE );
346 if (!hTask) return 0;
347 pTask = (TDB *)GlobalLock16( hTask );
349 /* Fill the task structure */
351 pTask->nEvents = 1; /* So the task can be started */
352 pTask->hSelf = hTask;
353 pTask->flags = 0;
355 if (pModule->flags & NE_FFLAGS_WIN32)
356 pTask->flags |= TDBF_WIN32;
357 if (pModule->lpDosTask)
358 pTask->flags |= TDBF_WINOLDAP;
360 pTask->version = pModule->expected_version;
361 pTask->hInstance = hInstance;
362 pTask->hPrevInstance = hPrevInstance;
363 pTask->hModule = pModule->self;
364 pTask->hParent = GetCurrentTask();
365 pTask->magic = TDB_MAGIC;
366 pTask->nCmdShow = cmdShow;
367 pTask->thdb = thdb;
368 pTask->curdrive = DRIVE_GetCurrentDrive() | 0x80;
369 strcpy( pTask->curdir, "\\" );
370 lstrcpyn32A( pTask->curdir + 1, DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() ),
371 sizeof(pTask->curdir) - 1 );
373 /* Create the thunks block */
375 TASK_CreateThunks( hTask, (int)&pTask->thunks - (int)pTask, 7 );
377 /* Copy the module name */
379 GetModuleName( pModule->self, name, sizeof(name) );
380 strncpy( pTask->module_name, name, sizeof(pTask->module_name) );
382 /* Allocate a selector for the PDB */
384 pTask->hPDB = GLOBAL_CreateBlock( GMEM_FIXED, &pTask->pdb, sizeof(PDB),
385 pModule->self, FALSE, FALSE, FALSE, NULL );
387 /* Fill the PDB */
389 pTask->pdb.int20 = 0x20cd;
390 pTask->pdb.dispatcher[0] = 0x9a; /* ljmp */
391 PUT_DWORD(&pTask->pdb.dispatcher[1], (DWORD)NE_GetEntryPoint(
392 GetModuleHandle16("KERNEL"), 102 )); /* KERNEL.102 is DOS3Call() */
393 pTask->pdb.savedint22 = INT_GetPMHandler( 0x22 );
394 pTask->pdb.savedint23 = INT_GetPMHandler( 0x23 );
395 pTask->pdb.savedint24 = INT_GetPMHandler( 0x24 );
396 pTask->pdb.fileHandlesPtr =
397 PTR_SEG_OFF_TO_SEGPTR( GlobalHandleToSel(pTask->hPDB),
398 (int)&((PDB *)0)->fileHandles );
399 pTask->pdb.hFileHandles = 0;
400 memset( pTask->pdb.fileHandles, 0xff, sizeof(pTask->pdb.fileHandles) );
401 pTask->pdb.environment = pdb32->env_db->env_sel;
402 pTask->pdb.nbFiles = 20;
404 /* Fill the command line */
406 cmd_line = pdb32->env_db->cmd_line;
407 while (*cmd_line && (*cmd_line != ' ') && (*cmd_line != '\t')) cmd_line++;
408 while ((*cmd_line == ' ') || (*cmd_line == '\t')) cmd_line++;
409 lstrcpyn32A( pTask->pdb.cmdLine+1, cmd_line, sizeof(pTask->pdb.cmdLine)-1);
410 pTask->pdb.cmdLine[0] = strlen( pTask->pdb.cmdLine + 1 );
412 /* Get the compatibility flags */
414 pTask->compat_flags = GetProfileInt32A( "Compatibility", name, 0 );
416 /* Allocate a code segment alias for the TDB */
418 pTask->hCSAlias = GLOBAL_CreateBlock( GMEM_FIXED, (void *)pTask,
419 sizeof(TDB), pTask->hPDB, TRUE,
420 FALSE, FALSE, NULL );
422 /* Set the owner of the environment block */
424 FarSetOwner( pTask->pdb.environment, pTask->hPDB );
426 /* Default DTA overwrites command-line */
428 pTask->dta = PTR_SEG_OFF_TO_SEGPTR( pTask->hPDB,
429 (int)&pTask->pdb.cmdLine - (int)&pTask->pdb );
431 /* Inherit default UserSignalHandler from initial process */
433 pInitialTask = (TDB *)GlobalLock16( PROCESS_Initial()->task );
434 if ( pInitialTask )
435 pTask->userhandler = pInitialTask->userhandler;
437 /* Create the 16-bit stack frame */
439 if (!(sp = pModule->sp))
440 sp = pSegTable[pModule->ss-1].minsize + pModule->stack_size;
441 sp &= ~1; sp -= 2*sizeof(STACK16FRAME);
442 pTask->thdb->cur_stack = PTR_SEG_OFF_TO_SEGPTR( pTask->hInstance, sp );
443 frame16 = (STACK16FRAME *)PTR_SEG_TO_LIN( pTask->thdb->cur_stack );
444 frame16->ebp = sp + (int)&((STACK16FRAME *)0)->bp;
445 frame16->bp = LOWORD(frame16->ebp);
446 frame16->ds = frame16->es = pTask->hInstance;
447 frame16->fs = 0;
448 frame16->entry_point = 0;
449 frame16->entry_cs = 0;
450 frame16->mutex_count = 1; /* TASK_Reschedule is called from 16-bit code */
451 /* The remaining fields will be initialized in TASK_Reschedule */
453 /* Create the 32-bit stack frame */
455 stack32Top = (char*)pTask->thdb->teb.stack_top;
456 frame16->frame32 = frame32 = (STACK32FRAME *)stack32Top - 1;
457 frame32->frame16 = pTask->thdb->cur_stack + sizeof(STACK16FRAME);
458 frame32->edi = 0;
459 frame32->esi = 0;
460 frame32->edx = 0;
461 frame32->ecx = 0;
462 frame32->ebx = 0;
463 frame32->retaddr = (DWORD)TASK_CallToStart;
464 /* The remaining fields will be initialized in TASK_Reschedule */
467 TRACE(task, "module='%s' cmdline='%s' task=%04x\n",
468 name, cmd_line, hTask );
470 return hTask;
473 /***********************************************************************
474 * TASK_StartTask
476 * NOTE: This routine might be called by a Win32 thread. Thus, we need
477 * to be careful to protect global data structures. We do this
478 * by entering the Win16Lock while linking the task into the
479 * global task list.
481 void TASK_StartTask( HTASK16 hTask )
483 /* Add the task to the linked list */
485 SYSLEVEL_EnterWin16Lock();
486 TASK_LinkTask( hTask );
487 SYSLEVEL_LeaveWin16Lock();
489 TRACE(task, "linked task %04x\n", hTask );
491 /* If requested, add entry point breakpoint */
493 if ( TASK_AddTaskEntryBreakpoint )
494 TASK_AddTaskEntryBreakpoint( hTask );
496 /* Get the task up and running. If we ourselves are a 16-bit task,
497 we simply Yield(). If we are 32-bit however, we need to signal
498 the main process somehow (NOT YET IMPLEMENTED!) */
500 if ( THREAD_IsWin16( THREAD_Current() ) )
501 OldYield();
502 else
503 FIXME(task, "Don't know how to start 16-bit task from 32-bit thread. Move the mouse!\n");
507 /***********************************************************************
508 * TASK_DeleteTask
510 static void TASK_DeleteTask( HTASK16 hTask )
512 TDB *pTask;
513 HGLOBAL16 hPDB;
515 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
516 hPDB = pTask->hPDB;
518 pTask->magic = 0xdead; /* invalidate signature */
520 /* Delete the Win32 part of the task */
522 K32OBJ_DecCount( &pTask->thdb->process->header );
523 K32OBJ_DecCount( &pTask->thdb->header );
525 /* Free the selector aliases */
527 GLOBAL_FreeBlock( pTask->hCSAlias );
528 GLOBAL_FreeBlock( pTask->hPDB );
530 /* Free the task module */
532 FreeModule16( pTask->hModule );
534 /* Free the task structure itself */
536 GlobalFree16( hTask );
538 /* Free all memory used by this task (including the 32-bit stack, */
539 /* the environment block and the thunk segments). */
541 GlobalFreeAll( hPDB );
545 /***********************************************************************
546 * TASK_KillCurrentTask
548 * Kill the currently running task. As it's not possible to kill the
549 * current task like this, it is simply marked for destruction, and will
550 * be killed when either TASK_Reschedule or this function is called again
551 * in the context of another task.
553 void TASK_KillCurrentTask( INT16 exitCode )
555 TDB* pTask = (TDB*) GlobalLock16( GetCurrentTask() );
556 NE_MODULE* pModule = NE_GetPtr( pTask->hModule );
557 if (!pTask) USER_ExitWindows(); /* No current task yet */
559 if ( !THREAD_IsWin16( THREAD_Current() ) )
561 FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
562 return;
565 /* Enter the Win16Lock to protect global data structures
566 NOTE: We never explicitly leave it again. This shouldn't matter
567 though, as it will be released in TASK_Reschedule and this
568 task won't ever get scheduled again ... */
570 SYSLEVEL_EnterWin16Lock();
572 assert(hCurrentTask == GetCurrentTask());
574 TRACE(task, "Killing task %04x\n", hCurrentTask );
576 /* Delete active sockets */
578 if( pTask->pwsi )
579 WINSOCK_DeleteTaskWSI( pTask, pTask->pwsi );
581 #ifdef MZ_SUPPORTED
582 /* Kill DOS VM task */
583 if ( pModule->lpDosTask )
584 MZ_KillModule( pModule->lpDosTask );
585 #endif
587 /* Perform USER cleanup */
589 if (pTask->userhandler)
590 pTask->userhandler( hCurrentTask, USIG_TERMINATION, 0,
591 pTask->hInstance, pTask->hQueue );
593 if (hTaskToKill && (hTaskToKill != hCurrentTask))
595 /* If another task is already marked for destruction, */
596 /* we can kill it now, as we are in another context. */
597 TASK_DeleteTask( hTaskToKill );
600 if (nTaskCount <= 1)
602 TRACE(task, "this is the last task, exiting\n" );
603 USER_ExitWindows();
606 /* FIXME: Hack! Send a message to the initial task so that
607 * the GetMessage wakes up and the initial task can check whether
608 * it is the only remaining one and terminate itself ...
609 * The initial task should probably install hooks or something
610 * to get informed about task termination :-/
612 Callout.PostAppMessage16( PROCESS_Initial()->task, WM_NULL, 0, 0 );
614 /* Remove the task from the list to be sure we never switch back to it */
615 TASK_UnlinkTask( hCurrentTask );
616 if( nTaskCount )
618 TDB* p = (TDB *)GlobalLock16( hFirstTask );
619 while( p )
621 if( p->hYieldTo == hCurrentTask ) p->hYieldTo = 0;
622 p = (TDB *)GlobalLock16( p->hNext );
626 hTaskToKill = hCurrentTask;
627 hLockedTask = 0;
629 pTask->nEvents = 0;
630 TASK_YieldToSystem(pTask);
632 /* We should never return from this Yield() */
634 ERR(task,"Return of the living dead %04x!!!\n", hCurrentTask);
635 exit(1);
638 /***********************************************************************
639 * TASK_Reschedule
641 * This is where all the magic of task-switching happens!
643 * Note: This function should only be called via the TASK_YieldToSystem()
644 * wrapper, to make sure that all the context is saved correctly.
646 * It must not call functions that may yield control.
648 BOOL32 TASK_Reschedule(void)
650 TDB *pOldTask = NULL, *pNewTask;
651 HTASK16 hTask = 0;
652 STACK16FRAME *newframe16;
653 BOOL32 pending = FALSE;
655 /* Get the initial task up and running */
656 if (!hCurrentTask && GetCurrentTask())
658 /* We need to remove one pair of stackframes (exept for Winelib) */
659 STACK16FRAME *oldframe16 = CURRENT_STACK16;
660 STACK32FRAME *oldframe32 = oldframe16->frame32;
661 STACK16FRAME *newframe16 = PTR_SEG_TO_LIN( oldframe32->frame16 );
662 STACK32FRAME *newframe32 = newframe16->frame32;
663 if (newframe32)
665 newframe16->entry_ip = oldframe16->entry_ip;
666 newframe16->entry_cs = oldframe16->entry_cs;
667 newframe16->ip = oldframe16->ip;
668 newframe16->cs = oldframe16->cs;
669 newframe32->ebp = oldframe32->ebp;
670 newframe32->restore_addr = oldframe32->restore_addr;
671 newframe32->codeselector = oldframe32->codeselector;
673 THREAD_Current()->cur_stack = oldframe32->frame16;
676 hCurrentTask = GetCurrentTask();
677 pNewTask = (TDB *)GlobalLock16( hCurrentTask );
678 pNewTask->ss_sp = pNewTask->thdb->cur_stack;
679 return FALSE;
682 /* NOTE: As we are entered from 16-bit code, we hold the Win16Lock.
683 We hang onto it thoughout most of this routine, so that accesses
684 to global variables (most notably the task list) are protected. */
685 assert(hCurrentTask == GetCurrentTask());
687 TRACE(task, "entered with hTask %04x (pid %d)\n", hCurrentTask, getpid());
689 #ifdef CONFIG_IPC
690 /* FIXME: What about the Win16Lock ??? */
691 dde_reschedule();
692 #endif
693 /* First check if there's a task to kill */
695 if (hTaskToKill && (hTaskToKill != hCurrentTask))
697 TASK_DeleteTask( hTaskToKill );
698 hTaskToKill = 0;
701 /* Find a task to yield to */
703 pOldTask = (TDB *)GlobalLock16( hCurrentTask );
704 if (pOldTask && pOldTask->hYieldTo)
706 /* check for DirectedYield() */
708 hTask = pOldTask->hYieldTo;
709 pNewTask = (TDB *)GlobalLock16( hTask );
710 if( !pNewTask || !pNewTask->nEvents) hTask = 0;
711 pOldTask->hYieldTo = 0;
714 /* extract hardware events only! */
716 if (!hTask) pending = EVENT_WaitNetEvent( FALSE, TRUE );
718 while (!hTask)
720 /* Find a task that has an event pending */
722 hTask = hFirstTask;
723 while (hTask)
725 pNewTask = (TDB *)GlobalLock16( hTask );
727 TRACE(task, "\ttask = %04x, events = %i\n", hTask, pNewTask->nEvents);
729 if (pNewTask->nEvents) break;
730 hTask = pNewTask->hNext;
732 if (hLockedTask && (hTask != hLockedTask)) hTask = 0;
733 if (hTask) break;
735 /* If a non-hardware event is pending, return to TASK_YieldToSystem
736 temporarily to process it safely */
737 if (pending) return TRUE;
739 /* No task found, wait for some events to come in */
741 /* NOTE: We release the Win16Lock while waiting for events. This is to enable
742 Win32 threads to thunk down to 16-bit temporarily. Since Win16
743 tasks won't execute and Win32 threads are not allowed to enter
744 TASK_Reschedule anyway, there should be no re-entrancy problem ... */
746 SYSLEVEL_ReleaseWin16Lock();
747 pending = EVENT_WaitNetEvent( TRUE, TRUE );
748 SYSLEVEL_RestoreWin16Lock();
751 if (hTask == hCurrentTask)
753 TRACE(task, "returning to the current task(%04x)\n", hTask );
754 return FALSE; /* Nothing to do */
756 pNewTask = (TDB *)GlobalLock16( hTask );
757 TRACE(task, "Switching to task %04x (%.8s)\n",
758 hTask, pNewTask->module_name );
760 /* Make the task the last in the linked list (round-robin scheduling) */
762 pNewTask->priority++;
763 TASK_UnlinkTask( hTask );
764 TASK_LinkTask( hTask );
765 pNewTask->priority--;
767 /* Finish initializing the new task stack if necessary */
769 newframe16 = THREAD_STACK16( pNewTask->thdb );
770 if (!newframe16->entry_cs)
772 STACK16FRAME *oldframe16 = CURRENT_STACK16;
773 STACK32FRAME *oldframe32 = oldframe16->frame32;
774 STACK32FRAME *newframe32 = newframe16->frame32;
775 newframe16->entry_ip = oldframe16->entry_ip;
776 newframe16->entry_cs = oldframe16->entry_cs;
777 newframe16->ip = oldframe16->ip;
778 newframe16->cs = oldframe16->cs;
779 newframe32->ebp = oldframe32->ebp;
780 newframe32->restore_addr = oldframe32->restore_addr;
781 newframe32->codeselector = oldframe32->codeselector;
784 /* Switch to the new stack */
786 /* NOTE: We need to release/restore the Win16Lock, as the task
787 switched to might be at another recursion level than
788 the old task ... */
790 SYSLEVEL_ReleaseWin16Lock();
792 hCurrentTask = hTask;
793 SET_CUR_THREAD( pNewTask->thdb );
794 pNewTask->ss_sp = pNewTask->thdb->cur_stack;
796 SYSLEVEL_RestoreWin16Lock();
798 return FALSE;
802 /***********************************************************************
803 * TASK_YieldToSystem
805 * Scheduler interface, this way we ensure that all "unsafe" events are
806 * processed outside the scheduler.
808 void TASK_YieldToSystem(TDB* pTask)
810 if ( !THREAD_IsWin16( THREAD_Current() ) )
812 FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
813 return;
816 if ( Callbacks->CallTaskRescheduleProc() )
818 /* NOTE: We get here only when no task has an event. This means also
819 the current task, so we shouldn't actually return to the
820 caller here. But, we need to do so, as the EVENT_WaitNetEvent
821 call could lead to a complex series of inter-task SendMessage
822 calls which might leave this task in a state where it again
823 has no event, but where its queue's wakeMask is also reset
824 to zero. Reentering TASK_Reschedule in this state would be
825 suicide. Hence, we do return to the caller after processing
826 non-hardware events. Actually, this should not hurt anyone,
827 as the caller must be WaitEvent, and thus the QUEUE_WaitBits
828 loop in USER. Should there actually be no message pending
829 for this task after processing non-hardware events, that loop
830 will simply return to WaitEvent. */
832 EVENT_WaitNetEvent( FALSE, FALSE );
837 /***********************************************************************
838 * InitTask (KERNEL.91)
840 * Called by the application startup code.
842 void WINAPI InitTask( CONTEXT *context )
844 TDB *pTask;
845 NE_MODULE *pModule;
846 SEGTABLEENTRY *pSegTable;
847 INSTANCEDATA *pinstance;
848 LONG stacklow, stackhi;
850 if (context) EAX_reg(context) = 0;
851 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
852 if (!(pModule = NE_GetPtr( pTask->hModule ))) return;
854 /* Initialize implicitly loaded DLLs */
855 NE_InitializeDLLs( pTask->hModule );
857 if (context)
859 /* Registers on return are:
860 * ax 1 if OK, 0 on error
861 * cx stack limit in bytes
862 * dx cmdShow parameter
863 * si instance handle of the previous instance
864 * di instance handle of the new task
865 * es:bx pointer to command-line inside PSP
867 * 0 (=%bp) is pushed on the stack
869 SEGPTR ptr = STACK16_PUSH( pTask->thdb, sizeof(WORD) );
870 *(WORD *)PTR_SEG_TO_LIN(ptr) = 0;
871 SP_reg(context) -= 2;
873 EAX_reg(context) = 1;
875 if (!pTask->pdb.cmdLine[0]) EBX_reg(context) = 0x80;
876 else
878 LPBYTE p = &pTask->pdb.cmdLine[1];
879 while ((*p == ' ') || (*p == '\t')) p++;
880 EBX_reg(context) = 0x80 + (p - pTask->pdb.cmdLine);
882 ECX_reg(context) = pModule->stack_size;
883 EDX_reg(context) = pTask->nCmdShow;
884 ESI_reg(context) = (DWORD)pTask->hPrevInstance;
885 EDI_reg(context) = (DWORD)pTask->hInstance;
886 ES_reg (context) = (WORD)pTask->hPDB;
889 /* Initialize the local heap */
890 if ( pModule->heap_size )
892 LocalInit( pTask->hInstance, 0, pModule->heap_size );
895 /* Initialize the INSTANCEDATA structure */
896 pSegTable = NE_SEG_TABLE( pModule );
897 stacklow = pSegTable[pModule->ss - 1].minsize;
898 stackhi = stacklow + pModule->stack_size;
899 if (stackhi > 0xffff) stackhi = 0xffff;
900 pinstance = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN(CURRENT_DS, 0);
901 pinstance->stackbottom = stackhi; /* yup, that's right. Confused me too. */
902 pinstance->stacktop = stacklow;
903 pinstance->stackmin = OFFSETOF( pTask->thdb->cur_stack );
907 /***********************************************************************
908 * WaitEvent (KERNEL.30)
910 BOOL16 WINAPI WaitEvent( HTASK16 hTask )
912 TDB *pTask;
914 if (!hTask) hTask = GetCurrentTask();
915 pTask = (TDB *)GlobalLock16( hTask );
917 if ( !THREAD_IsWin16( THREAD_Current() ) )
919 FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
920 return TRUE;
923 if (pTask->nEvents > 0)
925 pTask->nEvents--;
926 return FALSE;
928 TASK_YieldToSystem(pTask);
930 /* When we get back here, we have an event */
932 if (pTask->nEvents > 0) pTask->nEvents--;
933 return TRUE;
937 /***********************************************************************
938 * PostEvent (KERNEL.31)
940 void WINAPI PostEvent( HTASK16 hTask )
942 TDB *pTask;
944 if (!hTask) hTask = GetCurrentTask();
945 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
947 if ( !THREAD_IsWin16( THREAD_Current() ) )
949 WARN(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
950 /* return; */
953 pTask->nEvents++;
957 /***********************************************************************
958 * SetPriority (KERNEL.32)
960 void WINAPI SetPriority( HTASK16 hTask, INT16 delta )
962 TDB *pTask;
963 INT16 newpriority;
965 if (!hTask) hTask = GetCurrentTask();
966 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
967 newpriority = pTask->priority + delta;
968 if (newpriority < -32) newpriority = -32;
969 else if (newpriority > 15) newpriority = 15;
971 pTask->priority = newpriority + 1;
972 TASK_UnlinkTask( hTask );
973 TASK_LinkTask( hTask );
974 pTask->priority--;
978 /***********************************************************************
979 * LockCurrentTask (KERNEL.33)
981 HTASK16 WINAPI LockCurrentTask( BOOL16 bLock )
983 if (bLock) hLockedTask = GetCurrentTask();
984 else hLockedTask = 0;
985 return hLockedTask;
989 /***********************************************************************
990 * IsTaskLocked (KERNEL.122)
992 HTASK16 WINAPI IsTaskLocked(void)
994 return hLockedTask;
998 /***********************************************************************
999 * OldYield (KERNEL.117)
1001 void WINAPI OldYield(void)
1003 TDB *pCurTask = (TDB *)GlobalLock16( GetCurrentTask() );
1005 if ( !THREAD_IsWin16( THREAD_Current() ) )
1007 FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
1008 return;
1011 if (pCurTask) pCurTask->nEvents++; /* Make sure we get back here */
1012 TASK_YieldToSystem(pCurTask);
1013 if (pCurTask) pCurTask->nEvents--;
1017 /***********************************************************************
1018 * DirectedYield (KERNEL.150)
1020 void WINAPI DirectedYield( HTASK16 hTask )
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 TRACE(task, "%04x: DirectedYield(%04x)\n", pCurTask->hSelf, hTask );
1032 pCurTask->hYieldTo = hTask;
1033 OldYield();
1035 TRACE(task, "%04x: back from DirectedYield(%04x)\n", pCurTask->hSelf, hTask );
1038 /***********************************************************************
1039 * Yield16 (KERNEL.29)
1041 void WINAPI Yield16(void)
1043 TDB *pCurTask = (TDB *)GlobalLock16( GetCurrentTask() );
1045 if ( !THREAD_IsWin16( THREAD_Current() ) )
1047 FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
1048 return;
1051 if (pCurTask) pCurTask->hYieldTo = 0;
1052 if (pCurTask && pCurTask->hQueue) Callout.UserYield();
1053 else OldYield();
1056 /***********************************************************************
1057 * KERNEL_490 (KERNEL.490)
1059 HTASK16 WINAPI KERNEL_490( HTASK16 someTask )
1061 if ( !someTask ) return 0;
1063 FIXME( task, "(%04x): stub\n", someTask );
1064 return 0;
1067 /***********************************************************************
1068 * MakeProcInstance16 (KERNEL.51)
1070 FARPROC16 WINAPI MakeProcInstance16( FARPROC16 func, HANDLE16 hInstance )
1072 BYTE *thunk,*lfunc;
1073 SEGPTR thunkaddr;
1075 if (!func) {
1076 ERR(task, "Ouch ! MakeProcInstance called with func == NULL !\n");
1077 return (FARPROC16)0; /* Windows seems to do the same */
1079 if (!hInstance) hInstance = CURRENT_DS;
1080 thunkaddr = TASK_AllocThunk( GetCurrentTask() );
1081 if (!thunkaddr) return (FARPROC16)0;
1082 thunk = PTR_SEG_TO_LIN( thunkaddr );
1083 lfunc = PTR_SEG_TO_LIN( func );
1085 TRACE(task, "(%08lx,%04x): got thunk %08lx\n",
1086 (DWORD)func, hInstance, (DWORD)thunkaddr );
1087 if (((lfunc[0]==0x8c) && (lfunc[1]==0xd8)) ||
1088 ((lfunc[0]==0x1e) && (lfunc[1]==0x58))
1090 FIXME(task,"thunk would be useless for %p, overwriting with nop;nop;\n", func );
1091 lfunc[0]=0x90; /* nop */
1092 lfunc[1]=0x90; /* nop */
1095 *thunk++ = 0xb8; /* movw instance, %ax */
1096 *thunk++ = (BYTE)(hInstance & 0xff);
1097 *thunk++ = (BYTE)(hInstance >> 8);
1098 *thunk++ = 0xea; /* ljmp func */
1099 *(DWORD *)thunk = (DWORD)func;
1100 return (FARPROC16)thunkaddr;
1104 /***********************************************************************
1105 * FreeProcInstance16 (KERNEL.52)
1107 void WINAPI FreeProcInstance16( FARPROC16 func )
1109 TRACE(task, "(%08lx)\n", (DWORD)func );
1110 TASK_FreeThunk( GetCurrentTask(), (SEGPTR)func );
1114 /**********************************************************************
1115 * GetCodeHandle (KERNEL.93)
1117 HANDLE16 WINAPI GetCodeHandle( FARPROC16 proc )
1119 HANDLE16 handle;
1120 BYTE *thunk = (BYTE *)PTR_SEG_TO_LIN( proc );
1122 /* Return the code segment containing 'proc'. */
1123 /* Not sure if this is really correct (shouldn't matter that much). */
1125 /* Check if it is really a thunk */
1126 if ((thunk[0] == 0xb8) && (thunk[3] == 0xea))
1127 handle = GlobalHandle16( thunk[6] + (thunk[7] << 8) );
1128 else
1129 handle = GlobalHandle16( HIWORD(proc) );
1131 return handle;
1134 /**********************************************************************
1135 * GetCodeInfo (KERNEL.104)
1137 VOID WINAPI GetCodeInfo( FARPROC16 proc, SEGINFO *segInfo )
1139 BYTE *thunk = (BYTE *)PTR_SEG_TO_LIN( proc );
1140 NE_MODULE *pModule = NULL;
1141 SEGTABLEENTRY *pSeg = NULL;
1142 WORD segNr;
1144 /* proc is either a thunk, or else a pair of module handle
1145 and segment number. In the first case, we also need to
1146 extract module and segment number. */
1148 if ((thunk[0] == 0xb8) && (thunk[3] == 0xea))
1150 WORD selector = thunk[6] + (thunk[7] << 8);
1151 pModule = NE_GetPtr( GlobalHandle16( selector ) );
1152 pSeg = pModule? NE_SEG_TABLE( pModule ) : NULL;
1154 if ( pModule )
1155 for ( segNr = 0; segNr < pModule->seg_count; segNr++, pSeg++ )
1156 if ( GlobalHandleToSel(pSeg->hSeg) == selector )
1157 break;
1159 if ( pModule && segNr >= pModule->seg_count )
1160 pSeg = NULL;
1162 else
1164 pModule = NE_GetPtr( HIWORD( proc ) );
1165 segNr = LOWORD( proc );
1167 if ( pModule && segNr < pModule->seg_count )
1168 pSeg = NE_SEG_TABLE( pModule ) + segNr;
1171 /* fill in segment information */
1173 segInfo->offSegment = pSeg? pSeg->filepos : 0;
1174 segInfo->cbSegment = pSeg? pSeg->size : 0;
1175 segInfo->flags = pSeg? pSeg->flags : 0;
1176 segInfo->cbAlloc = pSeg? pSeg->minsize : 0;
1177 segInfo->h = pSeg? pSeg->hSeg : 0;
1178 segInfo->alignShift = pModule? pModule->alignment : 0;
1182 /**********************************************************************
1183 * DefineHandleTable16 (KERNEL.94)
1185 BOOL16 WINAPI DefineHandleTable16( WORD wOffset )
1187 return TRUE; /* FIXME */
1191 /***********************************************************************
1192 * SetTaskQueue (KERNEL.34)
1194 HQUEUE16 WINAPI SetTaskQueue( HTASK16 hTask, HQUEUE16 hQueue )
1196 HQUEUE16 hPrev;
1197 TDB *pTask;
1199 if (!hTask) hTask = GetCurrentTask();
1200 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
1202 hPrev = pTask->hQueue;
1203 pTask->hQueue = hQueue;
1205 TIMER_SwitchQueue( hPrev, hQueue );
1207 return hPrev;
1211 /***********************************************************************
1212 * GetTaskQueue (KERNEL.35)
1214 HQUEUE16 WINAPI GetTaskQueue( HTASK16 hTask )
1216 TDB *pTask;
1218 if (!hTask) hTask = GetCurrentTask();
1219 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
1220 return pTask->hQueue;
1223 /***********************************************************************
1224 * SetThreadQueue (KERNEL.463)
1226 HQUEUE16 WINAPI SetThreadQueue( DWORD thread, HQUEUE16 hQueue )
1228 THDB *thdb = thread? THREAD_IdToTHDB( thread ) : THREAD_Current();
1229 HQUEUE16 oldQueue = thdb? thdb->teb.queue : 0;
1231 if ( thdb )
1233 thdb->teb.queue = hQueue;
1235 if ( GetTaskQueue( thdb->process->task ) == oldQueue )
1236 SetTaskQueue( thdb->process->task, hQueue );
1239 return oldQueue;
1242 /***********************************************************************
1243 * GetThreadQueue (KERNEL.464)
1245 HQUEUE16 WINAPI GetThreadQueue( DWORD thread )
1247 THDB *thdb = NULL;
1248 if ( !thread )
1249 thdb = THREAD_Current();
1250 else if ( HIWORD(thread) )
1251 thdb = THREAD_IdToTHDB( thread );
1252 else if ( IsTask( (HTASK16)thread ) )
1253 thdb = ((TDB *)GlobalLock16( (HANDLE16)thread ))->thdb;
1255 return (HQUEUE16)(thdb? thdb->teb.queue : 0);
1258 /***********************************************************************
1259 * SetFastQueue (KERNEL.624)
1261 VOID WINAPI SetFastQueue( DWORD thread, HANDLE32 hQueue )
1263 THDB *thdb = NULL;
1264 if ( !thread )
1265 thdb = THREAD_Current();
1266 else if ( HIWORD(thread) )
1267 thdb = THREAD_IdToTHDB( thread );
1268 else if ( IsTask( (HTASK16)thread ) )
1269 thdb = ((TDB *)GlobalLock16( (HANDLE16)thread ))->thdb;
1271 if ( thdb ) thdb->teb.queue = (HQUEUE16) hQueue;
1274 /***********************************************************************
1275 * GetFastQueue (KERNEL.625)
1277 HANDLE32 WINAPI GetFastQueue( void )
1279 THDB *thdb = THREAD_Current();
1280 if (!thdb) return 0;
1282 if (!thdb->teb.queue)
1283 Callout.InitThreadInput( 0, THREAD_IsWin16(thdb)? 4 : 5 );
1285 if (!thdb->teb.queue)
1286 FIXME( task, "(): should initialize thread-local queue, expect failure!\n" );
1288 return (HANDLE32)thdb->teb.queue;
1291 /***********************************************************************
1292 * SwitchStackTo (KERNEL.108)
1294 void WINAPI SwitchStackTo( WORD seg, WORD ptr, WORD top )
1296 TDB *pTask;
1297 STACK16FRAME *oldFrame, *newFrame;
1298 INSTANCEDATA *pData;
1299 UINT16 copySize;
1301 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
1302 if (!(pData = (INSTANCEDATA *)GlobalLock16( seg ))) return;
1303 TRACE(task, "old=%04x:%04x new=%04x:%04x\n",
1304 SELECTOROF( pTask->thdb->cur_stack ),
1305 OFFSETOF( pTask->thdb->cur_stack ), seg, ptr );
1307 /* Save the old stack */
1309 oldFrame = THREAD_STACK16( pTask->thdb );
1310 /* pop frame + args and push bp */
1311 pData->old_ss_sp = pTask->thdb->cur_stack + sizeof(STACK16FRAME)
1312 + 2 * sizeof(WORD);
1313 *(WORD *)PTR_SEG_TO_LIN(pData->old_ss_sp) = oldFrame->bp;
1314 pData->stacktop = top;
1315 pData->stackmin = ptr;
1316 pData->stackbottom = ptr;
1318 /* Switch to the new stack */
1320 /* Note: we need to take the 3 arguments into account; otherwise,
1321 * the stack will underflow upon return from this function.
1323 copySize = oldFrame->bp - OFFSETOF(pData->old_ss_sp);
1324 copySize += 3 * sizeof(WORD) + sizeof(STACK16FRAME);
1325 pTask->thdb->cur_stack = PTR_SEG_OFF_TO_SEGPTR( seg, ptr - copySize );
1326 newFrame = THREAD_STACK16( pTask->thdb );
1328 /* Copy the stack frame and the local variables to the new stack */
1330 memmove( newFrame, oldFrame, copySize );
1331 newFrame->bp = ptr;
1332 *(WORD *)PTR_SEG_OFF_TO_LIN( seg, ptr ) = 0; /* clear previous bp */
1336 /***********************************************************************
1337 * SwitchStackBack (KERNEL.109)
1339 void WINAPI SwitchStackBack( CONTEXT *context )
1341 TDB *pTask;
1342 STACK16FRAME *oldFrame, *newFrame;
1343 INSTANCEDATA *pData;
1345 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
1346 if (!(pData = (INSTANCEDATA *)GlobalLock16(SELECTOROF(pTask->thdb->cur_stack))))
1347 return;
1348 if (!pData->old_ss_sp)
1350 WARN( task, "No previous SwitchStackTo\n" );
1351 return;
1353 TRACE(task, "restoring stack %04x:%04x\n",
1354 SELECTOROF(pData->old_ss_sp), OFFSETOF(pData->old_ss_sp) );
1356 oldFrame = THREAD_STACK16( pTask->thdb );
1358 /* Pop bp from the previous stack */
1360 BP_reg(context) = *(WORD *)PTR_SEG_TO_LIN(pData->old_ss_sp);
1361 pData->old_ss_sp += sizeof(WORD);
1363 /* Switch back to the old stack */
1365 pTask->thdb->cur_stack = pData->old_ss_sp - sizeof(STACK16FRAME);
1366 SS_reg(context) = SELECTOROF(pData->old_ss_sp);
1367 ESP_reg(context) = OFFSETOF(pData->old_ss_sp) - sizeof(DWORD); /*ret addr*/
1368 pData->old_ss_sp = 0;
1370 /* Build a stack frame for the return */
1372 newFrame = THREAD_STACK16( pTask->thdb );
1373 newFrame->frame32 = oldFrame->frame32;
1374 if (TRACE_ON(relay))
1376 newFrame->entry_ip = oldFrame->entry_ip;
1377 newFrame->entry_cs = oldFrame->entry_cs;
1382 /***********************************************************************
1383 * GetTaskQueueDS (KERNEL.118)
1385 void WINAPI GetTaskQueueDS( CONTEXT *context )
1387 DS_reg(context) = GlobalHandleToSel( GetTaskQueue(0) );
1391 /***********************************************************************
1392 * GetTaskQueueES (KERNEL.119)
1394 void WINAPI GetTaskQueueES( CONTEXT *context )
1396 ES_reg(context) = GlobalHandleToSel( GetTaskQueue(0) );
1400 /***********************************************************************
1401 * GetCurrentTask (KERNEL.36)
1403 HTASK16 WINAPI GetCurrentTask(void)
1405 return THREAD_InitDone? PROCESS_Current()->task : 0;
1408 DWORD WINAPI WIN16_GetCurrentTask(void)
1410 /* This is the version used by relay code; the first task is */
1411 /* returned in the high word of the result */
1412 return MAKELONG( GetCurrentTask(), hFirstTask );
1416 /***********************************************************************
1417 * GetCurrentPDB (KERNEL.37)
1419 * UNDOC: returns PSP of KERNEL in high word
1421 DWORD WINAPI GetCurrentPDB(void)
1423 TDB *pTask;
1425 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1426 return MAKELONG(pTask->hPDB, 0); /* FIXME */
1430 /***********************************************************************
1431 * GetInstanceData (KERNEL.54)
1433 INT16 WINAPI GetInstanceData( HINSTANCE16 instance, WORD buffer, INT16 len )
1435 char *ptr = (char *)GlobalLock16( instance );
1436 if (!ptr || !len) return 0;
1437 if ((int)buffer + len >= 0x10000) len = 0x10000 - buffer;
1438 memcpy( (char *)GlobalLock16(CURRENT_DS) + buffer, ptr + buffer, len );
1439 return len;
1443 /***********************************************************************
1444 * GetExeVersion (KERNEL.105)
1446 WORD WINAPI GetExeVersion(void)
1448 TDB *pTask;
1450 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1451 return pTask->version;
1455 /***********************************************************************
1456 * SetErrorMode16 (KERNEL.107)
1458 UINT16 WINAPI SetErrorMode16( UINT16 mode )
1460 TDB *pTask;
1461 UINT16 oldMode;
1463 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1464 oldMode = pTask->error_mode;
1465 pTask->error_mode = mode;
1466 pTask->thdb->process->error_mode = mode;
1467 return oldMode;
1471 /***********************************************************************
1472 * SetErrorMode32 (KERNEL32.486)
1474 UINT32 WINAPI SetErrorMode32( UINT32 mode )
1476 return SetErrorMode16( (UINT16)mode );
1480 /***********************************************************************
1481 * GetDOSEnvironment (KERNEL.131)
1483 SEGPTR WINAPI GetDOSEnvironment(void)
1485 TDB *pTask;
1487 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1488 return PTR_SEG_OFF_TO_SEGPTR( pTask->pdb.environment, 0 );
1492 /***********************************************************************
1493 * GetNumTasks (KERNEL.152)
1495 UINT16 WINAPI GetNumTasks(void)
1497 return nTaskCount;
1501 /***********************************************************************
1502 * GetTaskDS (KERNEL.155)
1504 * Note: this function apparently returns a DWORD with LOWORD == HIWORD.
1505 * I don't think we need to bother with this.
1507 HINSTANCE16 WINAPI GetTaskDS(void)
1509 TDB *pTask;
1511 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1512 return pTask->hInstance;
1515 /***********************************************************************
1516 * GetDummyModuleHandleDS (KERNEL.602)
1518 VOID WINAPI GetDummyModuleHandleDS( CONTEXT *context )
1520 TDB *pTask;
1521 WORD selector;
1523 AX_reg( context ) = 0;
1524 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
1525 if (!(pTask->flags & TDBF_WIN32)) return;
1527 selector = GlobalHandleToSel( pTask->hModule );
1528 DS_reg( context ) = selector;
1529 AX_reg( context ) = selector;
1532 /***********************************************************************
1533 * IsTask (KERNEL.320)
1535 BOOL16 WINAPI IsTask( HTASK16 hTask )
1537 TDB *pTask;
1539 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return FALSE;
1540 if (GlobalSize16( hTask ) < sizeof(TDB)) return FALSE;
1541 return (pTask->magic == TDB_MAGIC);
1545 /***********************************************************************
1546 * SetTaskSignalProc (KERNEL.38)
1548 * Real 16-bit interface is provided by the THUNK_SetTaskSignalProc.
1550 FARPROC16 WINAPI SetTaskSignalProc( HTASK16 hTask, FARPROC16 proc )
1552 TDB *pTask;
1553 FARPROC16 oldProc;
1555 if (!hTask) hTask = GetCurrentTask();
1556 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return NULL;
1557 oldProc = (FARPROC16)pTask->userhandler;
1558 pTask->userhandler = (USERSIGNALPROC)proc;
1559 return oldProc;
1563 /***********************************************************************
1564 * SetSigHandler (KERNEL.140)
1566 WORD WINAPI SetSigHandler( FARPROC16 newhandler, FARPROC16* oldhandler,
1567 UINT16 *oldmode, UINT16 newmode, UINT16 flag )
1569 FIXME(task,"(%p,%p,%p,%d,%d), unimplemented.\n",
1570 newhandler,oldhandler,oldmode,newmode,flag );
1572 if (flag != 1) return 0;
1573 if (!newmode) newhandler = NULL; /* Default handler */
1574 if (newmode != 4)
1576 TDB *pTask;
1578 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1579 if (oldmode) *oldmode = pTask->signal_flags;
1580 pTask->signal_flags = newmode;
1581 if (oldhandler) *oldhandler = pTask->sighandler;
1582 pTask->sighandler = newhandler;
1584 return 0;
1588 /***********************************************************************
1589 * GlobalNotify (KERNEL.154)
1591 * Note that GlobalNotify does _not_ return the old NotifyProc
1592 * -- contrary to LocalNotify !!
1594 VOID WINAPI GlobalNotify( FARPROC16 proc )
1596 TDB *pTask;
1598 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
1599 pTask->discardhandler = proc;
1603 /***********************************************************************
1604 * GetExePtr (KERNEL.133)
1606 static HMODULE16 GetExePtrHelper( HANDLE16 handle, HTASK16 *hTask )
1608 char *ptr;
1609 HANDLE16 owner;
1611 /* Check for module handle */
1613 if (!(ptr = GlobalLock16( handle ))) return 0;
1614 if (((NE_MODULE *)ptr)->magic == IMAGE_OS2_SIGNATURE) return handle;
1616 /* Search for this handle inside all tasks */
1618 *hTask = hFirstTask;
1619 while (*hTask)
1621 TDB *pTask = (TDB *)GlobalLock16( *hTask );
1622 if ((*hTask == handle) ||
1623 (pTask->hInstance == handle) ||
1624 (pTask->hQueue == handle) ||
1625 (pTask->hPDB == handle)) return pTask->hModule;
1626 *hTask = pTask->hNext;
1629 /* Check the owner for module handle */
1631 owner = FarGetOwner( handle );
1632 if (!(ptr = GlobalLock16( owner ))) return 0;
1633 if (((NE_MODULE *)ptr)->magic == IMAGE_OS2_SIGNATURE) return owner;
1635 /* Search for the owner inside all tasks */
1637 *hTask = hFirstTask;
1638 while (*hTask)
1640 TDB *pTask = (TDB *)GlobalLock16( *hTask );
1641 if ((*hTask == owner) ||
1642 (pTask->hInstance == owner) ||
1643 (pTask->hQueue == owner) ||
1644 (pTask->hPDB == owner)) return pTask->hModule;
1645 *hTask = pTask->hNext;
1648 return 0;
1651 HMODULE16 WINAPI GetExePtr( HANDLE16 handle )
1653 HTASK16 dummy;
1654 return GetExePtrHelper( handle, &dummy );
1657 void WINAPI WIN16_GetExePtr( CONTEXT *context )
1659 WORD *stack = PTR_SEG_OFF_TO_LIN(SS_reg(context), SP_reg(context));
1660 HANDLE16 handle = (HANDLE16)stack[2];
1661 HTASK16 hTask = 0;
1662 HMODULE16 hModule;
1664 hModule = GetExePtrHelper( handle, &hTask );
1666 AX_reg(context) = CX_reg(context) = hModule;
1667 if (hTask) ES_reg(context) = hTask;
1670 /***********************************************************************
1671 * TaskFirst (TOOLHELP.63)
1673 BOOL16 WINAPI TaskFirst( TASKENTRY *lpte )
1675 lpte->hNext = hFirstTask;
1676 return TaskNext( lpte );
1680 /***********************************************************************
1681 * TaskNext (TOOLHELP.64)
1683 BOOL16 WINAPI TaskNext( TASKENTRY *lpte )
1685 TDB *pTask;
1686 INSTANCEDATA *pInstData;
1688 TRACE(toolhelp, "(%p): task=%04x\n", lpte, lpte->hNext );
1689 if (!lpte->hNext) return FALSE;
1690 pTask = (TDB *)GlobalLock16( lpte->hNext );
1691 if (!pTask || pTask->magic != TDB_MAGIC) return FALSE;
1692 pInstData = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( pTask->hInstance, 0 );
1693 lpte->hTask = lpte->hNext;
1694 lpte->hTaskParent = pTask->hParent;
1695 lpte->hInst = pTask->hInstance;
1696 lpte->hModule = pTask->hModule;
1697 lpte->wSS = SELECTOROF( pTask->thdb->cur_stack );
1698 lpte->wSP = OFFSETOF( pTask->thdb->cur_stack );
1699 lpte->wStackTop = pInstData->stacktop;
1700 lpte->wStackMinimum = pInstData->stackmin;
1701 lpte->wStackBottom = pInstData->stackbottom;
1702 lpte->wcEvents = pTask->nEvents;
1703 lpte->hQueue = pTask->hQueue;
1704 strncpy( lpte->szModule, pTask->module_name, 8 );
1705 lpte->szModule[8] = '\0';
1706 lpte->wPSPOffset = 0x100; /*??*/
1707 lpte->hNext = pTask->hNext;
1708 return TRUE;
1712 /***********************************************************************
1713 * TaskFindHandle (TOOLHELP.65)
1715 BOOL16 WINAPI TaskFindHandle( TASKENTRY *lpte, HTASK16 hTask )
1717 lpte->hNext = hTask;
1718 return TaskNext( lpte );
1722 /***********************************************************************
1723 * GetAppCompatFlags16 (KERNEL.354)
1725 DWORD WINAPI GetAppCompatFlags16( HTASK16 hTask )
1727 return GetAppCompatFlags32( hTask );
1731 /***********************************************************************
1732 * GetAppCompatFlags32 (USER32.206)
1734 DWORD WINAPI GetAppCompatFlags32( HTASK32 hTask )
1736 TDB *pTask;
1738 if (!hTask) hTask = GetCurrentTask();
1739 if (!(pTask=(TDB *)GlobalLock16( (HTASK16)hTask ))) return 0;
1740 if (GlobalSize16(hTask) < sizeof(TDB)) return 0;
1741 return pTask->compat_flags;