4 * Copyright 1995 Alexandre Julliard
12 #include "wine/winbase16.h"
27 #include "selectors.h"
28 #include "stackframe.h"
41 /* Min. number of thunks allocated when creating a new segment */
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 /***********************************************************************
68 void TASK_InstallTHHook( THHOOK
*pNewThhook
)
70 THHOOK
*pOldThhook
= pThhook
;
72 pThhook
= pNewThhook
? pNewThhook
: &DefaultThhook
;
74 *pThhook
= *pOldThhook
;
77 /***********************************************************************
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 /***********************************************************************
91 static void TASK_LinkTask( HTASK16 hTask
)
96 if (!(pTask
= (TDB
*)GlobalLock16( hTask
))) return;
97 prevTask
= &hFirstTask
;
100 TDB
*prevTaskPtr
= (TDB
*)GlobalLock16( *prevTask
);
101 if (prevTaskPtr
->priority
>= pTask
->priority
) break;
102 prevTask
= &prevTaskPtr
->hNext
;
104 pTask
->hNext
= *prevTask
;
110 /***********************************************************************
113 static void TASK_UnlinkTask( HTASK16 hTask
)
118 prevTask
= &hFirstTask
;
119 while (*prevTask
&& (*prevTask
!= hTask
))
121 pTask
= (TDB
*)GlobalLock16( *prevTask
);
122 prevTask
= &pTask
->hNext
;
126 pTask
= (TDB
*)GlobalLock16( *prevTask
);
127 *prevTask
= pTask
->hNext
;
134 /***********************************************************************
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
)
146 pThunk
= (THUNKS
*)((BYTE
*)GlobalLock16( handle
) + offset
);
148 pThunk
->magic
= THUNK_MAGIC
;
149 pThunk
->free
= (int)&pThunk
->thunks
- (int)pThunk
;
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 /***********************************************************************
163 * Allocate a thunk for MakeProcInstance().
165 static SEGPTR
TASK_AllocThunk( HTASK16 hTask
)
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
)
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
);
186 pThunk
= (THUNKS
*)GlobalLock16( sel
);
189 base
+= pThunk
->free
;
190 pThunk
->free
= *(WORD
*)((BYTE
*)pThunk
+ pThunk
->free
);
191 return PTR_SEG_OFF_TO_SEGPTR( sel
, base
);
195 /***********************************************************************
198 * Free a MakeProcInstance() thunk.
200 static BOOL32
TASK_FreeThunk( HTASK16 hTask
, SEGPTR thunk
)
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
)))
213 pThunk
= (THUNKS
*)GlobalLock16( sel
);
216 if (!sel
) return FALSE
;
217 *(WORD
*)((BYTE
*)pThunk
+ LOWORD(thunk
) - base
) = pThunk
->free
;
218 pThunk
->free
= LOWORD(thunk
) - base
;
223 /***********************************************************************
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
);
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
)
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
);
258 ExitProcess( entry(NULL
) );
261 DWORD size
= PE_HEADER(pModule
->module32
)->OptionalHeader
.SizeOfStackReserve
;
265 CreateThread( NULL
, size
, entry
, NULL
, 0, &id
);
266 thdb
= THREAD_ID_TO_THDB( id
);
268 while ( thdb
->exit_code
== 0x103 )
271 QUEUE_Signal( pTask
->hSelf
);
274 ExitProcess( thdb
->exit_code
);
278 else if (pModule
->dos_image
)
285 /* Registers at initialization must be:
287 * bx stack size in bytes
288 * cx heap size in bytes
289 * si previous app instance
290 * di current app instance
292 * es selector to the PSP
293 * ds dgroup of the application
295 * sp top of the stack
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" );
321 /***********************************************************************
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
)
332 TDB
*pTask
, *pInitialTask
;
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
;
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
;
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
);
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
);
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
;
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
);
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
);
473 /***********************************************************************
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
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() ) )
503 /* wake-up the scheduler waiting in EVENT_WaitNetEvent */
508 /***********************************************************************
511 static void TASK_DeleteTask( HTASK16 hTask
)
516 if (!(pTask
= (TDB
*)GlobalLock16( hTask
))) return;
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
);
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 */
580 WINSOCK_DeleteTaskWSI( pTask
, pTask
->pwsi
);
583 /* Kill DOS VM task */
584 if ( pModule
->lpDosTask
)
585 MZ_KillModule( pModule
->lpDosTask
);
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
);
603 TRACE(task
, "this is the last task, exiting\n" );
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
);
619 TDB
* p
= (TDB
*)GlobalLock16( hFirstTask
);
622 if( p
->hYieldTo
== hCurrentTask
) p
->hYieldTo
= 0;
623 p
= (TDB
*)GlobalLock16( p
->hNext
);
627 hTaskToKill
= hCurrentTask
;
631 TASK_YieldToSystem(pTask
);
633 /* We should never return from this Yield() */
635 ERR(task
,"Return of the living dead %04x!!!\n", hCurrentTask
);
639 /***********************************************************************
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
;
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
;
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
;
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());
691 /* FIXME: What about the Win16Lock ??? */
694 /* First check if there's a task to kill */
696 if (hTaskToKill
&& (hTaskToKill
!= hCurrentTask
))
698 TASK_DeleteTask( hTaskToKill
);
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
);
721 /* Find a task that has an event pending */
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;
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
791 SYSLEVEL_ReleaseWin16Lock();
793 hCurrentTask
= hTask
;
794 SET_CUR_THREAD( pNewTask
->thdb
);
795 pNewTask
->ss_sp
= pNewTask
->thdb
->cur_stack
;
797 SYSLEVEL_RestoreWin16Lock();
803 /***********************************************************************
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
);
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
)
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
);
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;
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
)
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
);
924 if (pTask
->nEvents
> 0)
929 TASK_YieldToSystem(pTask
);
931 /* When we get back here, we have an event */
933 if (pTask
->nEvents
> 0) pTask
->nEvents
--;
938 /***********************************************************************
939 * PostEvent (KERNEL.31)
941 void WINAPI
PostEvent( HTASK16 hTask
)
945 if (!hTask
) hTask
= GetCurrentTask();
946 if (!(pTask
= (TDB
*)GlobalLock16( hTask
))) return;
950 if ( !THREAD_IsWin16( THREAD_Current() ) )
952 /* wake-up the scheduler waiting in EVENT_WaitNetEvent */
958 /***********************************************************************
959 * SetPriority (KERNEL.32)
961 void WINAPI
SetPriority( HTASK16 hTask
, INT16 delta
)
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
);
979 /***********************************************************************
980 * LockCurrentTask (KERNEL.33)
982 HTASK16 WINAPI
LockCurrentTask( BOOL16 bLock
)
984 if (bLock
) hLockedTask
= GetCurrentTask();
985 else hLockedTask
= 0;
990 /***********************************************************************
991 * IsTaskLocked (KERNEL.122)
993 HTASK16 WINAPI
IsTaskLocked(void)
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
);
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
);
1031 TRACE(task
, "%04x: DirectedYield(%04x)\n", pCurTask
->hSelf
, hTask
);
1033 pCurTask
->hYieldTo
= hTask
;
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
);
1052 if (pCurTask
) pCurTask
->hYieldTo
= 0;
1053 if (pCurTask
&& pCurTask
->hQueue
) Callout
.UserYield();
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
);
1068 /***********************************************************************
1069 * MakeProcInstance16 (KERNEL.51)
1071 FARPROC16 WINAPI
MakeProcInstance16( FARPROC16 func
, HANDLE16 hInstance
)
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
)
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) );
1130 handle
= GlobalHandle16( HIWORD(proc
) );
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
;
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
;
1156 for ( segNr
= 0; segNr
< pModule
->seg_count
; segNr
++, pSeg
++ )
1157 if ( GlobalHandleToSel(pSeg
->hSeg
) == selector
)
1160 if ( pModule
&& segNr
>= pModule
->seg_count
)
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
)
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
);
1212 /***********************************************************************
1213 * GetTaskQueue (KERNEL.35)
1215 HQUEUE16 WINAPI
GetTaskQueue( HTASK16 hTask
)
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;
1234 thdb
->teb
.queue
= hQueue
;
1236 if ( GetTaskQueue( thdb
->process
->task
) == oldQueue
)
1237 SetTaskQueue( thdb
->process
->task
, hQueue
);
1243 /***********************************************************************
1244 * GetThreadQueue (KERNEL.464)
1246 HQUEUE16 WINAPI
GetThreadQueue( DWORD thread
)
1250 thdb
= THREAD_Current();
1251 else if ( HIWORD(thread
) )
1252 thdb
= THREAD_IdToTHDB( thread
);
1253 else if ( IsTask( (HTASK16
)thread
) )
1254 thdb
= ((TDB
*)GlobalLock16( (HANDLE16
)thread
))->thdb
;
1256 return (HQUEUE16
)(thdb
? thdb
->teb
.queue
: 0);
1259 /***********************************************************************
1260 * SetFastQueue (KERNEL.624)
1262 VOID WINAPI
SetFastQueue( DWORD thread
, HANDLE32 hQueue
)
1266 thdb
= THREAD_Current();
1267 else if ( HIWORD(thread
) )
1268 thdb
= THREAD_IdToTHDB( thread
);
1269 else if ( IsTask( (HTASK16
)thread
) )
1270 thdb
= ((TDB
*)GlobalLock16( (HANDLE16
)thread
))->thdb
;
1272 if ( thdb
) thdb
->teb
.queue
= (HQUEUE16
) hQueue
;
1275 /***********************************************************************
1276 * GetFastQueue (KERNEL.625)
1278 HANDLE32 WINAPI
GetFastQueue( void )
1280 THDB
*thdb
= THREAD_Current();
1281 if (!thdb
) return 0;
1283 if (!thdb
->teb
.queue
)
1284 Callout
.InitThreadInput( 0, THREAD_IsWin16(thdb
)? 4 : 5 );
1286 if (!thdb
->teb
.queue
)
1287 FIXME( task
, "(): should initialize thread-local queue, expect failure!\n" );
1289 return (HANDLE32
)thdb
->teb
.queue
;
1292 /***********************************************************************
1293 * SwitchStackTo (KERNEL.108)
1295 void WINAPI
SwitchStackTo( WORD seg
, WORD ptr
, WORD top
)
1298 STACK16FRAME
*oldFrame
, *newFrame
;
1299 INSTANCEDATA
*pData
;
1302 if (!(pTask
= (TDB
*)GlobalLock16( GetCurrentTask() ))) return;
1303 if (!(pData
= (INSTANCEDATA
*)GlobalLock16( seg
))) return;
1304 TRACE(task
, "old=%04x:%04x new=%04x:%04x\n",
1305 SELECTOROF( pTask
->thdb
->cur_stack
),
1306 OFFSETOF( pTask
->thdb
->cur_stack
), seg
, ptr
);
1308 /* Save the old stack */
1310 oldFrame
= THREAD_STACK16( pTask
->thdb
);
1311 /* pop frame + args and push bp */
1312 pData
->old_ss_sp
= pTask
->thdb
->cur_stack
+ sizeof(STACK16FRAME
)
1314 *(WORD
*)PTR_SEG_TO_LIN(pData
->old_ss_sp
) = oldFrame
->bp
;
1315 pData
->stacktop
= top
;
1316 pData
->stackmin
= ptr
;
1317 pData
->stackbottom
= ptr
;
1319 /* Switch to the new stack */
1321 /* Note: we need to take the 3 arguments into account; otherwise,
1322 * the stack will underflow upon return from this function.
1324 copySize
= oldFrame
->bp
- OFFSETOF(pData
->old_ss_sp
);
1325 copySize
+= 3 * sizeof(WORD
) + sizeof(STACK16FRAME
);
1326 pTask
->thdb
->cur_stack
= PTR_SEG_OFF_TO_SEGPTR( seg
, ptr
- copySize
);
1327 newFrame
= THREAD_STACK16( pTask
->thdb
);
1329 /* Copy the stack frame and the local variables to the new stack */
1331 memmove( newFrame
, oldFrame
, copySize
);
1333 *(WORD
*)PTR_SEG_OFF_TO_LIN( seg
, ptr
) = 0; /* clear previous bp */
1337 /***********************************************************************
1338 * SwitchStackBack (KERNEL.109)
1340 void WINAPI
SwitchStackBack( CONTEXT
*context
)
1343 STACK16FRAME
*oldFrame
, *newFrame
;
1344 INSTANCEDATA
*pData
;
1346 if (!(pTask
= (TDB
*)GlobalLock16( GetCurrentTask() ))) return;
1347 if (!(pData
= (INSTANCEDATA
*)GlobalLock16(SELECTOROF(pTask
->thdb
->cur_stack
))))
1349 if (!pData
->old_ss_sp
)
1351 WARN( task
, "No previous SwitchStackTo\n" );
1354 TRACE(task
, "restoring stack %04x:%04x\n",
1355 SELECTOROF(pData
->old_ss_sp
), OFFSETOF(pData
->old_ss_sp
) );
1357 oldFrame
= THREAD_STACK16( pTask
->thdb
);
1359 /* Pop bp from the previous stack */
1361 BP_reg(context
) = *(WORD
*)PTR_SEG_TO_LIN(pData
->old_ss_sp
);
1362 pData
->old_ss_sp
+= sizeof(WORD
);
1364 /* Switch back to the old stack */
1366 pTask
->thdb
->cur_stack
= pData
->old_ss_sp
- sizeof(STACK16FRAME
);
1367 SS_reg(context
) = SELECTOROF(pData
->old_ss_sp
);
1368 ESP_reg(context
) = OFFSETOF(pData
->old_ss_sp
) - sizeof(DWORD
); /*ret addr*/
1369 pData
->old_ss_sp
= 0;
1371 /* Build a stack frame for the return */
1373 newFrame
= THREAD_STACK16( pTask
->thdb
);
1374 newFrame
->frame32
= oldFrame
->frame32
;
1375 if (TRACE_ON(relay
))
1377 newFrame
->entry_ip
= oldFrame
->entry_ip
;
1378 newFrame
->entry_cs
= oldFrame
->entry_cs
;
1383 /***********************************************************************
1384 * GetTaskQueueDS (KERNEL.118)
1386 void WINAPI
GetTaskQueueDS( CONTEXT
*context
)
1388 DS_reg(context
) = GlobalHandleToSel( GetTaskQueue(0) );
1392 /***********************************************************************
1393 * GetTaskQueueES (KERNEL.119)
1395 void WINAPI
GetTaskQueueES( CONTEXT
*context
)
1397 ES_reg(context
) = GlobalHandleToSel( GetTaskQueue(0) );
1401 /***********************************************************************
1402 * GetCurrentTask (KERNEL.36)
1404 HTASK16 WINAPI
GetCurrentTask(void)
1406 return THREAD_InitDone
? PROCESS_Current()->task
: 0;
1409 DWORD WINAPI
WIN16_GetCurrentTask(void)
1411 /* This is the version used by relay code; the first task is */
1412 /* returned in the high word of the result */
1413 return MAKELONG( GetCurrentTask(), hFirstTask
);
1417 /***********************************************************************
1418 * GetCurrentPDB (KERNEL.37)
1420 * UNDOC: returns PSP of KERNEL in high word
1422 DWORD WINAPI
GetCurrentPDB(void)
1426 if (!(pTask
= (TDB
*)GlobalLock16( GetCurrentTask() ))) return 0;
1427 return MAKELONG(pTask
->hPDB
, 0); /* FIXME */
1431 /***********************************************************************
1432 * GetInstanceData (KERNEL.54)
1434 INT16 WINAPI
GetInstanceData( HINSTANCE16 instance
, WORD buffer
, INT16 len
)
1436 char *ptr
= (char *)GlobalLock16( instance
);
1437 if (!ptr
|| !len
) return 0;
1438 if ((int)buffer
+ len
>= 0x10000) len
= 0x10000 - buffer
;
1439 memcpy( (char *)GlobalLock16(CURRENT_DS
) + buffer
, ptr
+ buffer
, len
);
1444 /***********************************************************************
1445 * GetExeVersion (KERNEL.105)
1447 WORD WINAPI
GetExeVersion(void)
1451 if (!(pTask
= (TDB
*)GlobalLock16( GetCurrentTask() ))) return 0;
1452 return pTask
->version
;
1456 /***********************************************************************
1457 * SetErrorMode16 (KERNEL.107)
1459 UINT16 WINAPI
SetErrorMode16( UINT16 mode
)
1464 if (!(pTask
= (TDB
*)GlobalLock16( GetCurrentTask() ))) return 0;
1465 oldMode
= pTask
->error_mode
;
1466 pTask
->error_mode
= mode
;
1467 pTask
->thdb
->process
->error_mode
= mode
;
1472 /***********************************************************************
1473 * SetErrorMode32 (KERNEL32.486)
1475 UINT32 WINAPI
SetErrorMode32( UINT32 mode
)
1477 return SetErrorMode16( (UINT16
)mode
);
1481 /***********************************************************************
1482 * GetDOSEnvironment (KERNEL.131)
1484 SEGPTR WINAPI
GetDOSEnvironment(void)
1488 if (!(pTask
= (TDB
*)GlobalLock16( GetCurrentTask() ))) return 0;
1489 return PTR_SEG_OFF_TO_SEGPTR( pTask
->pdb
.environment
, 0 );
1493 /***********************************************************************
1494 * GetNumTasks (KERNEL.152)
1496 UINT16 WINAPI
GetNumTasks(void)
1502 /***********************************************************************
1503 * GetTaskDS (KERNEL.155)
1505 * Note: this function apparently returns a DWORD with LOWORD == HIWORD.
1506 * I don't think we need to bother with this.
1508 HINSTANCE16 WINAPI
GetTaskDS(void)
1512 if (!(pTask
= (TDB
*)GlobalLock16( GetCurrentTask() ))) return 0;
1513 return pTask
->hInstance
;
1516 /***********************************************************************
1517 * GetDummyModuleHandleDS (KERNEL.602)
1519 VOID WINAPI
GetDummyModuleHandleDS( CONTEXT
*context
)
1524 AX_reg( context
) = 0;
1525 if (!(pTask
= (TDB
*)GlobalLock16( GetCurrentTask() ))) return;
1526 if (!(pTask
->flags
& TDBF_WIN32
)) return;
1528 selector
= GlobalHandleToSel( pTask
->hModule
);
1529 DS_reg( context
) = selector
;
1530 AX_reg( context
) = selector
;
1533 /***********************************************************************
1534 * IsTask (KERNEL.320)
1536 BOOL16 WINAPI
IsTask( HTASK16 hTask
)
1540 if (!(pTask
= (TDB
*)GlobalLock16( hTask
))) return FALSE
;
1541 if (GlobalSize16( hTask
) < sizeof(TDB
)) return FALSE
;
1542 return (pTask
->magic
== TDB_MAGIC
);
1546 /***********************************************************************
1547 * SetTaskSignalProc (KERNEL.38)
1549 * Real 16-bit interface is provided by the THUNK_SetTaskSignalProc.
1551 FARPROC16 WINAPI
SetTaskSignalProc( HTASK16 hTask
, FARPROC16 proc
)
1556 if (!hTask
) hTask
= GetCurrentTask();
1557 if (!(pTask
= (TDB
*)GlobalLock16( hTask
))) return NULL
;
1558 oldProc
= (FARPROC16
)pTask
->userhandler
;
1559 pTask
->userhandler
= (USERSIGNALPROC
)proc
;
1564 /***********************************************************************
1565 * SetSigHandler (KERNEL.140)
1567 WORD WINAPI
SetSigHandler( FARPROC16 newhandler
, FARPROC16
* oldhandler
,
1568 UINT16
*oldmode
, UINT16 newmode
, UINT16 flag
)
1570 FIXME(task
,"(%p,%p,%p,%d,%d), unimplemented.\n",
1571 newhandler
,oldhandler
,oldmode
,newmode
,flag
);
1573 if (flag
!= 1) return 0;
1574 if (!newmode
) newhandler
= NULL
; /* Default handler */
1579 if (!(pTask
= (TDB
*)GlobalLock16( GetCurrentTask() ))) return 0;
1580 if (oldmode
) *oldmode
= pTask
->signal_flags
;
1581 pTask
->signal_flags
= newmode
;
1582 if (oldhandler
) *oldhandler
= pTask
->sighandler
;
1583 pTask
->sighandler
= newhandler
;
1589 /***********************************************************************
1590 * GlobalNotify (KERNEL.154)
1592 * Note that GlobalNotify does _not_ return the old NotifyProc
1593 * -- contrary to LocalNotify !!
1595 VOID WINAPI
GlobalNotify( FARPROC16 proc
)
1599 if (!(pTask
= (TDB
*)GlobalLock16( GetCurrentTask() ))) return;
1600 pTask
->discardhandler
= proc
;
1604 /***********************************************************************
1605 * GetExePtr (KERNEL.133)
1607 static HMODULE16
GetExePtrHelper( HANDLE16 handle
, HTASK16
*hTask
)
1612 /* Check for module handle */
1614 if (!(ptr
= GlobalLock16( handle
))) return 0;
1615 if (((NE_MODULE
*)ptr
)->magic
== IMAGE_OS2_SIGNATURE
) return handle
;
1617 /* Search for this handle inside all tasks */
1619 *hTask
= hFirstTask
;
1622 TDB
*pTask
= (TDB
*)GlobalLock16( *hTask
);
1623 if ((*hTask
== handle
) ||
1624 (pTask
->hInstance
== handle
) ||
1625 (pTask
->hQueue
== handle
) ||
1626 (pTask
->hPDB
== handle
)) return pTask
->hModule
;
1627 *hTask
= pTask
->hNext
;
1630 /* Check the owner for module handle */
1632 owner
= FarGetOwner( handle
);
1633 if (!(ptr
= GlobalLock16( owner
))) return 0;
1634 if (((NE_MODULE
*)ptr
)->magic
== IMAGE_OS2_SIGNATURE
) return owner
;
1636 /* Search for the owner inside all tasks */
1638 *hTask
= hFirstTask
;
1641 TDB
*pTask
= (TDB
*)GlobalLock16( *hTask
);
1642 if ((*hTask
== owner
) ||
1643 (pTask
->hInstance
== owner
) ||
1644 (pTask
->hQueue
== owner
) ||
1645 (pTask
->hPDB
== owner
)) return pTask
->hModule
;
1646 *hTask
= pTask
->hNext
;
1652 HMODULE16 WINAPI
GetExePtr( HANDLE16 handle
)
1655 return GetExePtrHelper( handle
, &dummy
);
1658 void WINAPI
WIN16_GetExePtr( CONTEXT
*context
)
1660 WORD
*stack
= PTR_SEG_OFF_TO_LIN(SS_reg(context
), SP_reg(context
));
1661 HANDLE16 handle
= (HANDLE16
)stack
[2];
1665 hModule
= GetExePtrHelper( handle
, &hTask
);
1667 AX_reg(context
) = CX_reg(context
) = hModule
;
1668 if (hTask
) ES_reg(context
) = hTask
;
1671 /***********************************************************************
1672 * TaskFirst (TOOLHELP.63)
1674 BOOL16 WINAPI
TaskFirst( TASKENTRY
*lpte
)
1676 lpte
->hNext
= hFirstTask
;
1677 return TaskNext( lpte
);
1681 /***********************************************************************
1682 * TaskNext (TOOLHELP.64)
1684 BOOL16 WINAPI
TaskNext( TASKENTRY
*lpte
)
1687 INSTANCEDATA
*pInstData
;
1689 TRACE(toolhelp
, "(%p): task=%04x\n", lpte
, lpte
->hNext
);
1690 if (!lpte
->hNext
) return FALSE
;
1691 pTask
= (TDB
*)GlobalLock16( lpte
->hNext
);
1692 if (!pTask
|| pTask
->magic
!= TDB_MAGIC
) return FALSE
;
1693 pInstData
= (INSTANCEDATA
*)PTR_SEG_OFF_TO_LIN( pTask
->hInstance
, 0 );
1694 lpte
->hTask
= lpte
->hNext
;
1695 lpte
->hTaskParent
= pTask
->hParent
;
1696 lpte
->hInst
= pTask
->hInstance
;
1697 lpte
->hModule
= pTask
->hModule
;
1698 lpte
->wSS
= SELECTOROF( pTask
->thdb
->cur_stack
);
1699 lpte
->wSP
= OFFSETOF( pTask
->thdb
->cur_stack
);
1700 lpte
->wStackTop
= pInstData
->stacktop
;
1701 lpte
->wStackMinimum
= pInstData
->stackmin
;
1702 lpte
->wStackBottom
= pInstData
->stackbottom
;
1703 lpte
->wcEvents
= pTask
->nEvents
;
1704 lpte
->hQueue
= pTask
->hQueue
;
1705 strncpy( lpte
->szModule
, pTask
->module_name
, 8 );
1706 lpte
->szModule
[8] = '\0';
1707 lpte
->wPSPOffset
= 0x100; /*??*/
1708 lpte
->hNext
= pTask
->hNext
;
1713 /***********************************************************************
1714 * TaskFindHandle (TOOLHELP.65)
1716 BOOL16 WINAPI
TaskFindHandle( TASKENTRY
*lpte
, HTASK16 hTask
)
1718 lpte
->hNext
= hTask
;
1719 return TaskNext( lpte
);
1723 /***********************************************************************
1724 * GetAppCompatFlags16 (KERNEL.354)
1726 DWORD WINAPI
GetAppCompatFlags16( HTASK16 hTask
)
1728 return GetAppCompatFlags32( hTask
);
1732 /***********************************************************************
1733 * GetAppCompatFlags32 (USER32.206)
1735 DWORD WINAPI
GetAppCompatFlags32( HTASK32 hTask
)
1739 if (!hTask
) hTask
= GetCurrentTask();
1740 if (!(pTask
=(TDB
*)GlobalLock16( (HTASK16
)hTask
))) return 0;
1741 if (GlobalSize16(hTask
) < sizeof(TDB
)) return 0;
1742 return pTask
->compat_flags
;