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( void );
62 extern BOOL 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 BOOL
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 ERR( task
, "Called for Win32 task!\n" );
246 else if (pModule
->dos_image
)
253 /* Registers at initialization must be:
255 * bx stack size in bytes
256 * cx heap size in bytes
257 * si previous app instance
258 * di current app instance
260 * es selector to the PSP
261 * ds dgroup of the application
263 * sp top of the stack
267 memset( &context
, 0, sizeof(context
) );
268 CS_reg(&context
) = GlobalHandleToSel16(pSegTable
[pModule
->cs
- 1].hSeg
);
269 DS_reg(&context
) = GlobalHandleToSel16(pSegTable
[pModule
->dgroup
- 1].hSeg
);
270 ES_reg(&context
) = pTask
->hPDB
;
271 EIP_reg(&context
) = pModule
->ip
;
272 EBX_reg(&context
) = pModule
->stack_size
;
273 ECX_reg(&context
) = pModule
->heap_size
;
274 EDI_reg(&context
) = context
.SegDs
;
276 TRACE(task
, "Starting main program: cs:ip=%04lx:%04x ds=%04lx ss:sp=%04x:%04x\n",
277 CS_reg(&context
), IP_reg(&context
), DS_reg(&context
),
278 SELECTOROF(pTask
->thdb
->cur_stack
),
279 OFFSETOF(pTask
->thdb
->cur_stack
) );
281 Callbacks
->CallRegisterShortProc( &context
, 0 );
282 /* This should never return */
283 ERR( task
, "Main program returned! (should never happen)\n" );
289 /***********************************************************************
292 * NOTE: This routine might be called by a Win32 thread. We don't have
293 * any real problems with that, since we operated merely on a private
294 * TDB structure that is not yet linked into the task list.
296 BOOL
TASK_Create( THDB
*thdb
, NE_MODULE
*pModule
, HINSTANCE16 hInstance
,
297 HINSTANCE16 hPrevInstance
, UINT16 cmdShow
)
300 TDB
*pTask
, *pInitialTask
;
305 STACK16FRAME
*frame16
;
306 STACK32FRAME
*frame32
;
307 PDB
*pdb32
= thdb
->process
;
308 SEGTABLEENTRY
*pSegTable
= NE_SEG_TABLE( pModule
);
310 /* Allocate the task structure */
312 hTask
= GLOBAL_Alloc( GMEM_FIXED
| GMEM_ZEROINIT
, sizeof(TDB
),
313 pModule
->self
, FALSE
, FALSE
, FALSE
);
314 if (!hTask
) return FALSE
;
315 pTask
= (TDB
*)GlobalLock16( hTask
);
317 /* Fill the task structure */
320 pTask
->hSelf
= hTask
;
323 if (pModule
->flags
& NE_FFLAGS_WIN32
)
324 pTask
->flags
|= TDBF_WIN32
;
325 if (pModule
->lpDosTask
)
326 pTask
->flags
|= TDBF_WINOLDAP
;
328 pTask
->version
= pModule
->expected_version
;
329 pTask
->hInstance
= hInstance
? hInstance
: pModule
->self
;
330 pTask
->hPrevInstance
= hPrevInstance
;
331 pTask
->hModule
= pModule
->self
;
332 pTask
->hParent
= GetCurrentTask();
333 pTask
->magic
= TDB_MAGIC
;
334 pTask
->nCmdShow
= cmdShow
;
336 pTask
->curdrive
= DRIVE_GetCurrentDrive() | 0x80;
337 strcpy( pTask
->curdir
, "\\" );
338 lstrcpynA( pTask
->curdir
+ 1, DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() ),
339 sizeof(pTask
->curdir
) - 1 );
341 /* Create the thunks block */
343 TASK_CreateThunks( hTask
, (int)&pTask
->thunks
- (int)pTask
, 7 );
345 /* Copy the module name */
347 GetModuleName16( pModule
->self
, name
, sizeof(name
) );
348 strncpy( pTask
->module_name
, name
, sizeof(pTask
->module_name
) );
350 /* Allocate a selector for the PDB */
352 pTask
->hPDB
= GLOBAL_CreateBlock( GMEM_FIXED
, &pTask
->pdb
, sizeof(PDB16
),
353 pModule
->self
, FALSE
, FALSE
, FALSE
, NULL
);
357 pTask
->pdb
.int20
= 0x20cd;
358 pTask
->pdb
.dispatcher
[0] = 0x9a; /* ljmp */
359 PUT_DWORD(&pTask
->pdb
.dispatcher
[1], (DWORD
)NE_GetEntryPoint(
360 GetModuleHandle16("KERNEL"), 102 )); /* KERNEL.102 is DOS3Call() */
361 pTask
->pdb
.savedint22
= INT_GetPMHandler( 0x22 );
362 pTask
->pdb
.savedint23
= INT_GetPMHandler( 0x23 );
363 pTask
->pdb
.savedint24
= INT_GetPMHandler( 0x24 );
364 pTask
->pdb
.fileHandlesPtr
=
365 PTR_SEG_OFF_TO_SEGPTR( GlobalHandleToSel16(pTask
->hPDB
),
366 (int)&((PDB16
*)0)->fileHandles
);
367 pTask
->pdb
.hFileHandles
= 0;
368 memset( pTask
->pdb
.fileHandles
, 0xff, sizeof(pTask
->pdb
.fileHandles
) );
369 pTask
->pdb
.environment
= pdb32
->env_db
->env_sel
;
370 pTask
->pdb
.nbFiles
= 20;
372 /* Fill the command line */
374 cmd_line
= pdb32
->env_db
->cmd_line
;
375 while (*cmd_line
&& (*cmd_line
!= ' ') && (*cmd_line
!= '\t')) cmd_line
++;
376 while ((*cmd_line
== ' ') || (*cmd_line
== '\t')) cmd_line
++;
377 lstrcpynA( pTask
->pdb
.cmdLine
+1, cmd_line
, sizeof(pTask
->pdb
.cmdLine
)-1);
378 pTask
->pdb
.cmdLine
[0] = strlen( pTask
->pdb
.cmdLine
+ 1 );
380 /* Get the compatibility flags */
382 pTask
->compat_flags
= GetProfileIntA( "Compatibility", name
, 0 );
384 /* Allocate a code segment alias for the TDB */
386 pTask
->hCSAlias
= GLOBAL_CreateBlock( GMEM_FIXED
, (void *)pTask
,
387 sizeof(TDB
), pTask
->hPDB
, TRUE
,
388 FALSE
, FALSE
, NULL
);
390 /* Set the owner of the environment block */
392 FarSetOwner16( pTask
->pdb
.environment
, pTask
->hPDB
);
394 /* Default DTA overwrites command-line */
396 pTask
->dta
= PTR_SEG_OFF_TO_SEGPTR( pTask
->hPDB
,
397 (int)&pTask
->pdb
.cmdLine
- (int)&pTask
->pdb
);
399 /* Inherit default UserSignalHandler from initial process */
401 pInitialTask
= (TDB
*)GlobalLock16( PROCESS_Initial()->task
);
403 pTask
->userhandler
= pInitialTask
->userhandler
;
405 /* If we have a DGROUP/hInstance, use it for 16-bit stack */
409 if (!(sp
= pModule
->sp
))
410 sp
= pSegTable
[pModule
->ss
-1].minsize
+ pModule
->stack_size
;
411 sp
&= ~1; sp
-= sizeof(STACK16FRAME
);
412 pTask
->thdb
->cur_stack
= PTR_SEG_OFF_TO_SEGPTR( hInstance
, sp
);
415 /* Create the 16-bit stack frame */
417 pTask
->thdb
->cur_stack
-= sizeof(STACK16FRAME
);
418 frame16
= (STACK16FRAME
*)PTR_SEG_TO_LIN( pTask
->thdb
->cur_stack
);
419 frame16
->ebp
= OFFSETOF( pTask
->thdb
->cur_stack
) + (int)&((STACK16FRAME
*)0)->bp
;
420 frame16
->bp
= LOWORD(frame16
->ebp
);
421 frame16
->ds
= frame16
->es
= hInstance
;
423 frame16
->entry_point
= 0;
424 frame16
->entry_cs
= 0;
425 frame16
->mutex_count
= 1; /* TASK_Reschedule is called from 16-bit code */
426 /* The remaining fields will be initialized in TASK_Reschedule */
428 /* Create the 32-bit stack frame */
430 stack32Top
= (char*)pTask
->thdb
->teb
.stack_top
;
431 frame16
->frame32
= frame32
= (STACK32FRAME
*)stack32Top
- 1;
432 frame32
->frame16
= pTask
->thdb
->cur_stack
+ sizeof(STACK16FRAME
);
438 frame32
->retaddr
= (DWORD
)TASK_CallToStart
;
439 /* The remaining fields will be initialized in TASK_Reschedule */
441 /* Enter task handle into thread and process */
443 pTask
->thdb
->teb
.htask16
= pTask
->thdb
->process
->task
= hTask
;
445 TRACE(task
, "module='%s' cmdline='%s' task=%04x\n",
446 name
, cmd_line
, hTask
);
451 /***********************************************************************
454 * NOTE: This routine might be called by a Win32 thread. Thus, we need
455 * to be careful to protect global data structures. We do this
456 * by entering the Win16Lock while linking the task into the
459 void TASK_StartTask( HTASK16 hTask
)
461 TDB
*pTask
= (TDB
*)GlobalLock16( hTask
);
462 if ( !pTask
) return;
464 /* Add the task to the linked list */
466 SYSLEVEL_EnterWin16Lock();
467 TASK_LinkTask( hTask
);
468 SYSLEVEL_LeaveWin16Lock();
470 TRACE(task
, "linked task %04x\n", hTask
);
472 /* If requested, add entry point breakpoint */
474 if ( TASK_AddTaskEntryBreakpoint
)
475 TASK_AddTaskEntryBreakpoint( hTask
);
477 /* Get the task up and running. */
479 if ( THREAD_IsWin16( pTask
->thdb
) )
483 /* If we ourselves are a 16-bit task, we simply Yield().
484 If we are 32-bit however, we need to signal the scheduler. */
486 if ( THREAD_IsWin16( THREAD_Current() ) )
493 /* To start a 32-bit task, we spawn its initial thread. */
495 SYSDEPS_SpawnThread( pTask
->thdb
);
500 /***********************************************************************
503 static void TASK_DeleteTask( HTASK16 hTask
)
508 if (!(pTask
= (TDB
*)GlobalLock16( hTask
))) return;
511 pTask
->magic
= 0xdead; /* invalidate signature */
513 /* Delete the Win32 part of the task */
515 /* PROCESS_FreePDB( pTask->thdb->process ); FIXME */
516 /* K32OBJ_DecCount( &pTask->thdb->header ); FIXME */
518 /* Free the selector aliases */
520 GLOBAL_FreeBlock( pTask
->hCSAlias
);
521 GLOBAL_FreeBlock( pTask
->hPDB
);
523 /* Free the task module */
525 FreeModule16( pTask
->hModule
);
527 /* Free the task structure itself */
529 GlobalFree16( hTask
);
531 /* Free all memory used by this task (including the 32-bit stack, */
532 /* the environment block and the thunk segments). */
534 GlobalFreeAll16( hPDB
);
537 /***********************************************************************
540 void TASK_KillTask( HTASK16 hTask
)
544 /* Enter the Win16Lock to protect global data structures */
545 SYSLEVEL_EnterWin16Lock();
547 if ( !hTask
) hTask
= GetCurrentTask();
548 pTask
= (TDB
*)GlobalLock16( hTask
);
551 SYSLEVEL_LeaveWin16Lock();
555 TRACE(task
, "Killing task %04x\n", hTask
);
557 /* Delete active sockets */
560 WINSOCK_DeleteTaskWSI( pTask
, pTask
->pwsi
);
564 /* Kill DOS VM task */
565 NE_MODULE
*pModule
= NE_GetPtr( pTask
->hModule
);
566 if ( pModule
->lpDosTask
)
567 MZ_KillModule( pModule
->lpDosTask
);
571 /* Perform USER cleanup */
573 if (pTask
->userhandler
)
574 pTask
->userhandler( hCurrentTask
, USIG_TERMINATION
, 0,
575 pTask
->hInstance
, pTask
->hQueue
);
579 TRACE(task
, "this is the last task, exiting\n" );
583 /* FIXME: Hack! Send a message to the initial task so that
584 * the GetMessage wakes up and the initial task can check whether
585 * it is the only remaining one and terminate itself ...
586 * The initial task should probably install hooks or something
587 * to get informed about task termination :-/
589 Callout
.PostAppMessage16( PROCESS_Initial()->task
, WM_NULL
, 0, 0 );
591 /* Remove the task from the list to be sure we never switch back to it */
592 TASK_UnlinkTask( hTask
);
595 TDB
* p
= (TDB
*)GlobalLock16( hFirstTask
);
598 if( p
->hYieldTo
== hTask
) p
->hYieldTo
= 0;
599 p
= (TDB
*)GlobalLock16( p
->hNext
);
605 if ( hLockedTask
== hTask
)
608 if ( hTaskToKill
&& ( hTaskToKill
!= hCurrentTask
) )
610 /* If another task is already marked for destruction, */
611 /* we can kill it now, as we are in another context. */
612 TASK_DeleteTask( hTaskToKill
);
617 * If hTask is not the task currently scheduled by the Win16
618 * scheduler, we simply delete it; otherwise we mark it for
619 * destruction. Note that if the current task is a 32-bit
620 * one, hCurrentTask is *different* from GetCurrentTask()!
622 if ( hTask
== hCurrentTask
)
624 assert( hTaskToKill
== 0 || hTaskToKill
== hCurrentTask
);
625 hTaskToKill
= hCurrentTask
;
628 TASK_DeleteTask( hTask
);
630 SYSLEVEL_LeaveWin16Lock();
634 /***********************************************************************
635 * TASK_KillCurrentTask
637 * Kill the currently running task. As it's not possible to kill the
638 * current task like this, it is simply marked for destruction, and will
639 * be killed when either TASK_Reschedule or this function is called again
640 * in the context of another task.
642 void TASK_KillCurrentTask( INT16 exitCode
)
644 if ( !THREAD_IsWin16( THREAD_Current() ) )
646 FIXME(task
, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel
);
650 assert(hCurrentTask
== GetCurrentTask());
652 TRACE(task
, "Killing current task %04x\n", hCurrentTask
);
656 TASK_YieldToSystem();
658 /* We should never return from this Yield() */
660 ERR(task
,"Return of the living dead %04x!!!\n", hCurrentTask
);
664 /***********************************************************************
667 * This is where all the magic of task-switching happens!
669 * Note: This function should only be called via the TASK_YieldToSystem()
670 * wrapper, to make sure that all the context is saved correctly.
672 * It must not call functions that may yield control.
674 BOOL
TASK_Reschedule(void)
676 TDB
*pOldTask
= NULL
, *pNewTask
;
678 STACK16FRAME
*newframe16
;
679 BOOL pending
= FALSE
;
681 /* Get the initial task up and running */
682 if (!hCurrentTask
&& GetCurrentTask())
684 /* We need to remove one pair of stackframes (exept for Winelib) */
685 STACK16FRAME
*oldframe16
= CURRENT_STACK16
;
686 STACK32FRAME
*oldframe32
= oldframe16
->frame32
;
687 STACK16FRAME
*newframe16
= PTR_SEG_TO_LIN( oldframe32
->frame16
);
688 STACK32FRAME
*newframe32
= newframe16
->frame32
;
691 newframe16
->entry_ip
= oldframe16
->entry_ip
;
692 newframe16
->entry_cs
= oldframe16
->entry_cs
;
693 newframe16
->ip
= oldframe16
->ip
;
694 newframe16
->cs
= oldframe16
->cs
;
695 newframe32
->ebp
= oldframe32
->ebp
;
696 newframe32
->restore_addr
= oldframe32
->restore_addr
;
697 newframe32
->codeselector
= oldframe32
->codeselector
;
699 THREAD_Current()->cur_stack
= oldframe32
->frame16
;
702 hCurrentTask
= GetCurrentTask();
703 pNewTask
= (TDB
*)GlobalLock16( hCurrentTask
);
704 pNewTask
->ss_sp
= pNewTask
->thdb
->cur_stack
;
708 /* NOTE: As we are entered from 16-bit code, we hold the Win16Lock.
709 We hang onto it thoughout most of this routine, so that accesses
710 to global variables (most notably the task list) are protected. */
711 assert(hCurrentTask
== GetCurrentTask());
713 TRACE(task
, "entered with hTask %04x (pid %d)\n", hCurrentTask
, getpid());
716 /* FIXME: What about the Win16Lock ??? */
719 /* First check if there's a task to kill */
721 if (hTaskToKill
&& (hTaskToKill
!= hCurrentTask
))
723 TASK_DeleteTask( hTaskToKill
);
727 /* Find a task to yield to */
729 pOldTask
= (TDB
*)GlobalLock16( hCurrentTask
);
730 if (pOldTask
&& pOldTask
->hYieldTo
)
732 /* check for DirectedYield() */
734 hTask
= pOldTask
->hYieldTo
;
735 pNewTask
= (TDB
*)GlobalLock16( hTask
);
736 if( !pNewTask
|| !pNewTask
->nEvents
) hTask
= 0;
737 pOldTask
->hYieldTo
= 0;
740 /* extract hardware events only! */
742 if (!hTask
) pending
= EVENT_WaitNetEvent( FALSE
, TRUE
);
746 /* Find a task that has an event pending */
751 pNewTask
= (TDB
*)GlobalLock16( hTask
);
753 TRACE(task
, "\ttask = %04x, events = %i\n", hTask
, pNewTask
->nEvents
);
755 if (pNewTask
->nEvents
) break;
756 hTask
= pNewTask
->hNext
;
758 if (hLockedTask
&& (hTask
!= hLockedTask
)) hTask
= 0;
761 /* If a non-hardware event is pending, return to TASK_YieldToSystem
762 temporarily to process it safely */
763 if (pending
) return TRUE
;
765 /* No task found, wait for some events to come in */
767 /* NOTE: We release the Win16Lock while waiting for events. This is to enable
768 Win32 threads to thunk down to 16-bit temporarily. Since Win16
769 tasks won't execute and Win32 threads are not allowed to enter
770 TASK_Reschedule anyway, there should be no re-entrancy problem ... */
772 SYSLEVEL_ReleaseWin16Lock();
773 pending
= EVENT_WaitNetEvent( TRUE
, TRUE
);
774 SYSLEVEL_RestoreWin16Lock();
777 if (hTask
== hCurrentTask
)
779 TRACE(task
, "returning to the current task(%04x)\n", hTask
);
780 return FALSE
; /* Nothing to do */
782 pNewTask
= (TDB
*)GlobalLock16( hTask
);
783 TRACE(task
, "Switching to task %04x (%.8s)\n",
784 hTask
, pNewTask
->module_name
);
786 /* Make the task the last in the linked list (round-robin scheduling) */
788 pNewTask
->priority
++;
789 TASK_UnlinkTask( hTask
);
790 TASK_LinkTask( hTask
);
791 pNewTask
->priority
--;
793 /* Finish initializing the new task stack if necessary */
795 newframe16
= THREAD_STACK16( pNewTask
->thdb
);
796 if (!newframe16
->entry_cs
)
798 STACK16FRAME
*oldframe16
= CURRENT_STACK16
;
799 STACK32FRAME
*oldframe32
= oldframe16
->frame32
;
800 STACK32FRAME
*newframe32
= newframe16
->frame32
;
801 newframe16
->entry_ip
= oldframe16
->entry_ip
;
802 newframe16
->entry_cs
= oldframe16
->entry_cs
;
803 newframe16
->ip
= oldframe16
->ip
;
804 newframe16
->cs
= oldframe16
->cs
;
805 newframe32
->ebp
= oldframe32
->ebp
;
806 newframe32
->restore_addr
= oldframe32
->restore_addr
;
807 newframe32
->codeselector
= oldframe32
->codeselector
;
810 /* Switch to the new stack */
812 /* NOTE: We need to release/restore the Win16Lock, as the task
813 switched to might be at another recursion level than
816 SYSLEVEL_ReleaseWin16Lock();
818 hCurrentTask
= hTask
;
819 SET_CUR_THREAD( pNewTask
->thdb
);
820 pNewTask
->ss_sp
= pNewTask
->thdb
->cur_stack
;
822 SYSLEVEL_RestoreWin16Lock();
828 /***********************************************************************
831 * Scheduler interface, this way we ensure that all "unsafe" events are
832 * processed outside the scheduler.
834 static void TASK_YieldToSystem( void )
836 if ( !THREAD_IsWin16( THREAD_Current() ) )
838 FIXME(task
, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel
);
842 if ( Callbacks
->CallTaskRescheduleProc() )
844 /* NOTE: We get here only when no task has an event. This means also
845 the current task, so we shouldn't actually return to the
846 caller here. But, we need to do so, as the EVENT_WaitNetEvent
847 call could lead to a complex series of inter-task SendMessage
848 calls which might leave this task in a state where it again
849 has no event, but where its queue's wakeMask is also reset
850 to zero. Reentering TASK_Reschedule in this state would be
851 suicide. Hence, we do return to the caller after processing
852 non-hardware events. Actually, this should not hurt anyone,
853 as the caller must be WaitEvent, and thus the QUEUE_WaitBits
854 loop in USER. Should there actually be no message pending
855 for this task after processing non-hardware events, that loop
856 will simply return to WaitEvent. */
858 EVENT_WaitNetEvent( FALSE
, FALSE
);
863 /***********************************************************************
864 * InitTask (KERNEL.91)
866 * Called by the application startup code.
868 void WINAPI
InitTask16( CONTEXT
*context
)
872 SEGTABLEENTRY
*pSegTable
;
873 INSTANCEDATA
*pinstance
;
874 LONG stacklow
, stackhi
;
876 if (context
) EAX_reg(context
) = 0;
877 if (!(pTask
= (TDB
*)GlobalLock16( GetCurrentTask() ))) return;
878 if (!(pModule
= NE_GetPtr( pTask
->hModule
))) return;
880 /* Initialize implicitly loaded DLLs */
881 NE_InitializeDLLs( pTask
->hModule
);
885 /* Registers on return are:
886 * ax 1 if OK, 0 on error
887 * cx stack limit in bytes
888 * dx cmdShow parameter
889 * si instance handle of the previous instance
890 * di instance handle of the new task
891 * es:bx pointer to command-line inside PSP
893 * 0 (=%bp) is pushed on the stack
895 SEGPTR ptr
= STACK16_PUSH( pTask
->thdb
, sizeof(WORD
) );
896 *(WORD
*)PTR_SEG_TO_LIN(ptr
) = 0;
897 SP_reg(context
) -= 2;
899 EAX_reg(context
) = 1;
901 if (!pTask
->pdb
.cmdLine
[0]) EBX_reg(context
) = 0x80;
904 LPBYTE p
= &pTask
->pdb
.cmdLine
[1];
905 while ((*p
== ' ') || (*p
== '\t')) p
++;
906 EBX_reg(context
) = 0x80 + (p
- pTask
->pdb
.cmdLine
);
908 ECX_reg(context
) = pModule
->stack_size
;
909 EDX_reg(context
) = pTask
->nCmdShow
;
910 ESI_reg(context
) = (DWORD
)pTask
->hPrevInstance
;
911 EDI_reg(context
) = (DWORD
)pTask
->hInstance
;
912 ES_reg (context
) = (WORD
)pTask
->hPDB
;
915 /* Initialize the local heap */
916 if ( pModule
->heap_size
)
918 LocalInit16( pTask
->hInstance
, 0, pModule
->heap_size
);
921 /* Initialize the INSTANCEDATA structure */
922 pSegTable
= NE_SEG_TABLE( pModule
);
923 stacklow
= pSegTable
[pModule
->ss
- 1].minsize
;
924 stackhi
= stacklow
+ pModule
->stack_size
;
925 if (stackhi
> 0xffff) stackhi
= 0xffff;
926 pinstance
= (INSTANCEDATA
*)PTR_SEG_OFF_TO_LIN(CURRENT_DS
, 0);
927 pinstance
->stackbottom
= stackhi
; /* yup, that's right. Confused me too. */
928 pinstance
->stacktop
= stacklow
;
929 pinstance
->stackmin
= OFFSETOF( pTask
->thdb
->cur_stack
);
933 /***********************************************************************
934 * WaitEvent (KERNEL.30)
936 BOOL16 WINAPI
WaitEvent16( HTASK16 hTask
)
940 if (!hTask
) hTask
= GetCurrentTask();
941 pTask
= (TDB
*)GlobalLock16( hTask
);
943 if ( !THREAD_IsWin16( THREAD_Current() ) )
945 FIXME(task
, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel
);
949 if (pTask
->nEvents
> 0)
954 TASK_YieldToSystem();
956 /* When we get back here, we have an event */
958 if (pTask
->nEvents
> 0) pTask
->nEvents
--;
963 /***********************************************************************
964 * PostEvent (KERNEL.31)
966 void WINAPI
PostEvent16( HTASK16 hTask
)
970 if (!hTask
) hTask
= GetCurrentTask();
971 if (!(pTask
= (TDB
*)GlobalLock16( hTask
))) return;
973 if ( !THREAD_IsWin16( pTask
->thdb
) )
975 FIXME( task
, "called for Win32 thread (%04x)!\n", pTask
->thdb
->teb_sel
);
981 if ( !THREAD_IsWin16( THREAD_Current() ) )
983 /* wake-up the scheduler waiting in EVENT_WaitNetEvent */
989 /***********************************************************************
990 * SetPriority (KERNEL.32)
992 void WINAPI
SetPriority16( HTASK16 hTask
, INT16 delta
)
997 if (!hTask
) hTask
= GetCurrentTask();
998 if (!(pTask
= (TDB
*)GlobalLock16( hTask
))) return;
999 newpriority
= pTask
->priority
+ delta
;
1000 if (newpriority
< -32) newpriority
= -32;
1001 else if (newpriority
> 15) newpriority
= 15;
1003 pTask
->priority
= newpriority
+ 1;
1004 TASK_UnlinkTask( hTask
);
1005 TASK_LinkTask( hTask
);
1010 /***********************************************************************
1011 * LockCurrentTask (KERNEL.33)
1013 HTASK16 WINAPI
LockCurrentTask16( BOOL16 bLock
)
1015 if (bLock
) hLockedTask
= GetCurrentTask();
1016 else hLockedTask
= 0;
1021 /***********************************************************************
1022 * IsTaskLocked (KERNEL.122)
1024 HTASK16 WINAPI
IsTaskLocked16(void)
1030 /***********************************************************************
1031 * OldYield (KERNEL.117)
1033 void WINAPI
OldYield16(void)
1035 TDB
*pCurTask
= (TDB
*)GlobalLock16( GetCurrentTask() );
1037 if ( !THREAD_IsWin16( THREAD_Current() ) )
1039 FIXME(task
, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel
);
1043 if (pCurTask
) pCurTask
->nEvents
++; /* Make sure we get back here */
1044 TASK_YieldToSystem();
1045 if (pCurTask
) pCurTask
->nEvents
--;
1049 /***********************************************************************
1050 * DirectedYield (KERNEL.150)
1052 void WINAPI
DirectedYield16( HTASK16 hTask
)
1054 TDB
*pCurTask
= (TDB
*)GlobalLock16( GetCurrentTask() );
1056 if ( !THREAD_IsWin16( THREAD_Current() ) )
1058 FIXME(task
, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel
);
1062 TRACE(task
, "%04x: DirectedYield(%04x)\n", pCurTask
->hSelf
, hTask
);
1064 pCurTask
->hYieldTo
= hTask
;
1067 TRACE(task
, "%04x: back from DirectedYield(%04x)\n", pCurTask
->hSelf
, hTask
);
1070 /***********************************************************************
1071 * Yield16 (KERNEL.29)
1073 void WINAPI
Yield16(void)
1075 TDB
*pCurTask
= (TDB
*)GlobalLock16( GetCurrentTask() );
1077 if (pCurTask
) pCurTask
->hYieldTo
= 0;
1078 if (pCurTask
&& pCurTask
->hQueue
) Callout
.UserYield16();
1082 /***********************************************************************
1083 * KERNEL_490 (KERNEL.490)
1085 HTASK16 WINAPI
KERNEL_490( HTASK16 someTask
)
1087 if ( !someTask
) return 0;
1089 FIXME( task
, "(%04x): stub\n", someTask
);
1093 /***********************************************************************
1094 * MakeProcInstance16 (KERNEL.51)
1096 FARPROC16 WINAPI
MakeProcInstance16( FARPROC16 func
, HANDLE16 hInstance
)
1102 ERR(task
, "Ouch ! MakeProcInstance called with func == NULL !\n");
1103 return (FARPROC16
)0; /* Windows seems to do the same */
1105 if (!hInstance
) hInstance
= CURRENT_DS
;
1106 thunkaddr
= TASK_AllocThunk( GetCurrentTask() );
1107 if (!thunkaddr
) return (FARPROC16
)0;
1108 thunk
= PTR_SEG_TO_LIN( thunkaddr
);
1109 lfunc
= PTR_SEG_TO_LIN( func
);
1111 TRACE(task
, "(%08lx,%04x): got thunk %08lx\n",
1112 (DWORD
)func
, hInstance
, (DWORD
)thunkaddr
);
1113 if (((lfunc
[0]==0x8c) && (lfunc
[1]==0xd8)) ||
1114 ((lfunc
[0]==0x1e) && (lfunc
[1]==0x58))
1116 FIXME(task
,"thunk would be useless for %p, overwriting with nop;nop;\n", func
);
1117 lfunc
[0]=0x90; /* nop */
1118 lfunc
[1]=0x90; /* nop */
1121 *thunk
++ = 0xb8; /* movw instance, %ax */
1122 *thunk
++ = (BYTE
)(hInstance
& 0xff);
1123 *thunk
++ = (BYTE
)(hInstance
>> 8);
1124 *thunk
++ = 0xea; /* ljmp func */
1125 *(DWORD
*)thunk
= (DWORD
)func
;
1126 return (FARPROC16
)thunkaddr
;
1130 /***********************************************************************
1131 * FreeProcInstance16 (KERNEL.52)
1133 void WINAPI
FreeProcInstance16( FARPROC16 func
)
1135 TRACE(task
, "(%08lx)\n", (DWORD
)func
);
1136 TASK_FreeThunk( GetCurrentTask(), (SEGPTR
)func
);
1140 /**********************************************************************
1141 * GetCodeHandle (KERNEL.93)
1143 HANDLE16 WINAPI
GetCodeHandle16( FARPROC16 proc
)
1146 BYTE
*thunk
= (BYTE
*)PTR_SEG_TO_LIN( proc
);
1148 /* Return the code segment containing 'proc'. */
1149 /* Not sure if this is really correct (shouldn't matter that much). */
1151 /* Check if it is really a thunk */
1152 if ((thunk
[0] == 0xb8) && (thunk
[3] == 0xea))
1153 handle
= GlobalHandle16( thunk
[6] + (thunk
[7] << 8) );
1155 handle
= GlobalHandle16( HIWORD(proc
) );
1160 /**********************************************************************
1161 * GetCodeInfo (KERNEL.104)
1163 VOID WINAPI
GetCodeInfo16( FARPROC16 proc
, SEGINFO
*segInfo
)
1165 BYTE
*thunk
= (BYTE
*)PTR_SEG_TO_LIN( proc
);
1166 NE_MODULE
*pModule
= NULL
;
1167 SEGTABLEENTRY
*pSeg
= NULL
;
1170 /* proc is either a thunk, or else a pair of module handle
1171 and segment number. In the first case, we also need to
1172 extract module and segment number. */
1174 if ((thunk
[0] == 0xb8) && (thunk
[3] == 0xea))
1176 WORD selector
= thunk
[6] + (thunk
[7] << 8);
1177 pModule
= NE_GetPtr( GlobalHandle16( selector
) );
1178 pSeg
= pModule
? NE_SEG_TABLE( pModule
) : NULL
;
1181 for ( segNr
= 0; segNr
< pModule
->seg_count
; segNr
++, pSeg
++ )
1182 if ( GlobalHandleToSel16(pSeg
->hSeg
) == selector
)
1185 if ( pModule
&& segNr
>= pModule
->seg_count
)
1190 pModule
= NE_GetPtr( HIWORD( proc
) );
1191 segNr
= LOWORD( proc
);
1193 if ( pModule
&& segNr
< pModule
->seg_count
)
1194 pSeg
= NE_SEG_TABLE( pModule
) + segNr
;
1197 /* fill in segment information */
1199 segInfo
->offSegment
= pSeg
? pSeg
->filepos
: 0;
1200 segInfo
->cbSegment
= pSeg
? pSeg
->size
: 0;
1201 segInfo
->flags
= pSeg
? pSeg
->flags
: 0;
1202 segInfo
->cbAlloc
= pSeg
? pSeg
->minsize
: 0;
1203 segInfo
->h
= pSeg
? pSeg
->hSeg
: 0;
1204 segInfo
->alignShift
= pModule
? pModule
->alignment
: 0;
1208 /**********************************************************************
1209 * DefineHandleTable16 (KERNEL.94)
1211 BOOL16 WINAPI
DefineHandleTable16( WORD wOffset
)
1213 return TRUE
; /* FIXME */
1217 /***********************************************************************
1218 * SetTaskQueue (KERNEL.34)
1220 HQUEUE16 WINAPI
SetTaskQueue16( HTASK16 hTask
, HQUEUE16 hQueue
)
1225 if (!hTask
) hTask
= GetCurrentTask();
1226 if (!(pTask
= (TDB
*)GlobalLock16( hTask
))) return 0;
1228 hPrev
= pTask
->hQueue
;
1229 pTask
->hQueue
= hQueue
;
1231 TIMER_SwitchQueue( hPrev
, hQueue
);
1237 /***********************************************************************
1238 * GetTaskQueue (KERNEL.35)
1240 HQUEUE16 WINAPI
GetTaskQueue16( HTASK16 hTask
)
1244 if (!hTask
) hTask
= GetCurrentTask();
1245 if (!(pTask
= (TDB
*)GlobalLock16( hTask
))) return 0;
1246 return pTask
->hQueue
;
1249 /***********************************************************************
1250 * SetThreadQueue (KERNEL.463)
1252 HQUEUE16 WINAPI
SetThreadQueue16( DWORD thread
, HQUEUE16 hQueue
)
1254 THDB
*thdb
= thread
? THREAD_IdToTHDB( thread
) : THREAD_Current();
1255 HQUEUE16 oldQueue
= thdb
? thdb
->teb
.queue
: 0;
1259 thdb
->teb
.queue
= hQueue
;
1261 if ( GetTaskQueue16( thdb
->process
->task
) == oldQueue
)
1262 SetTaskQueue16( thdb
->process
->task
, hQueue
);
1268 /***********************************************************************
1269 * GetThreadQueue (KERNEL.464)
1271 HQUEUE16 WINAPI
GetThreadQueue16( DWORD thread
)
1275 thdb
= THREAD_Current();
1276 else if ( HIWORD(thread
) )
1277 thdb
= THREAD_IdToTHDB( thread
);
1278 else if ( IsTask16( (HTASK16
)thread
) )
1279 thdb
= ((TDB
*)GlobalLock16( (HANDLE16
)thread
))->thdb
;
1281 return (HQUEUE16
)(thdb
? thdb
->teb
.queue
: 0);
1284 /***********************************************************************
1285 * SetFastQueue (KERNEL.624)
1287 VOID WINAPI
SetFastQueue16( DWORD thread
, HANDLE hQueue
)
1291 thdb
= THREAD_Current();
1292 else if ( HIWORD(thread
) )
1293 thdb
= THREAD_IdToTHDB( thread
);
1294 else if ( IsTask16( (HTASK16
)thread
) )
1295 thdb
= ((TDB
*)GlobalLock16( (HANDLE16
)thread
))->thdb
;
1297 if ( thdb
) thdb
->teb
.queue
= (HQUEUE16
) hQueue
;
1300 /***********************************************************************
1301 * GetFastQueue (KERNEL.625)
1303 HANDLE WINAPI
GetFastQueue16( void )
1305 THDB
*thdb
= THREAD_Current();
1306 if (!thdb
) return 0;
1308 if (!thdb
->teb
.queue
)
1309 Callout
.InitThreadInput16( 0, THREAD_IsWin16(thdb
)? 4 : 5 );
1311 if (!thdb
->teb
.queue
)
1312 FIXME( task
, "(): should initialize thread-local queue, expect failure!\n" );
1314 return (HANDLE
)thdb
->teb
.queue
;
1317 /***********************************************************************
1318 * SwitchStackTo (KERNEL.108)
1320 void WINAPI
SwitchStackTo16( WORD seg
, WORD ptr
, WORD top
)
1323 STACK16FRAME
*oldFrame
, *newFrame
;
1324 INSTANCEDATA
*pData
;
1327 if (!(pTask
= (TDB
*)GlobalLock16( GetCurrentTask() ))) return;
1328 if (!(pData
= (INSTANCEDATA
*)GlobalLock16( seg
))) return;
1329 TRACE(task
, "old=%04x:%04x new=%04x:%04x\n",
1330 SELECTOROF( pTask
->thdb
->cur_stack
),
1331 OFFSETOF( pTask
->thdb
->cur_stack
), seg
, ptr
);
1333 /* Save the old stack */
1335 oldFrame
= THREAD_STACK16( pTask
->thdb
);
1336 /* pop frame + args and push bp */
1337 pData
->old_ss_sp
= pTask
->thdb
->cur_stack
+ sizeof(STACK16FRAME
)
1339 *(WORD
*)PTR_SEG_TO_LIN(pData
->old_ss_sp
) = oldFrame
->bp
;
1340 pData
->stacktop
= top
;
1341 pData
->stackmin
= ptr
;
1342 pData
->stackbottom
= ptr
;
1344 /* Switch to the new stack */
1346 /* Note: we need to take the 3 arguments into account; otherwise,
1347 * the stack will underflow upon return from this function.
1349 copySize
= oldFrame
->bp
- OFFSETOF(pData
->old_ss_sp
);
1350 copySize
+= 3 * sizeof(WORD
) + sizeof(STACK16FRAME
);
1351 pTask
->thdb
->cur_stack
= PTR_SEG_OFF_TO_SEGPTR( seg
, ptr
- copySize
);
1352 newFrame
= THREAD_STACK16( pTask
->thdb
);
1354 /* Copy the stack frame and the local variables to the new stack */
1356 memmove( newFrame
, oldFrame
, copySize
);
1358 *(WORD
*)PTR_SEG_OFF_TO_LIN( seg
, ptr
) = 0; /* clear previous bp */
1362 /***********************************************************************
1363 * SwitchStackBack (KERNEL.109)
1365 void WINAPI
SwitchStackBack16( CONTEXT
*context
)
1368 STACK16FRAME
*oldFrame
, *newFrame
;
1369 INSTANCEDATA
*pData
;
1371 if (!(pTask
= (TDB
*)GlobalLock16( GetCurrentTask() ))) return;
1372 if (!(pData
= (INSTANCEDATA
*)GlobalLock16(SELECTOROF(pTask
->thdb
->cur_stack
))))
1374 if (!pData
->old_ss_sp
)
1376 WARN( task
, "No previous SwitchStackTo\n" );
1379 TRACE(task
, "restoring stack %04x:%04x\n",
1380 SELECTOROF(pData
->old_ss_sp
), OFFSETOF(pData
->old_ss_sp
) );
1382 oldFrame
= THREAD_STACK16( pTask
->thdb
);
1384 /* Pop bp from the previous stack */
1386 BP_reg(context
) = *(WORD
*)PTR_SEG_TO_LIN(pData
->old_ss_sp
);
1387 pData
->old_ss_sp
+= sizeof(WORD
);
1389 /* Switch back to the old stack */
1391 pTask
->thdb
->cur_stack
= pData
->old_ss_sp
- sizeof(STACK16FRAME
);
1392 SS_reg(context
) = SELECTOROF(pData
->old_ss_sp
);
1393 ESP_reg(context
) = OFFSETOF(pData
->old_ss_sp
) - sizeof(DWORD
); /*ret addr*/
1394 pData
->old_ss_sp
= 0;
1396 /* Build a stack frame for the return */
1398 newFrame
= THREAD_STACK16( pTask
->thdb
);
1399 newFrame
->frame32
= oldFrame
->frame32
;
1400 if (TRACE_ON(relay
))
1402 newFrame
->entry_ip
= oldFrame
->entry_ip
;
1403 newFrame
->entry_cs
= oldFrame
->entry_cs
;
1408 /***********************************************************************
1409 * GetTaskQueueDS (KERNEL.118)
1411 void WINAPI
GetTaskQueueDS16( CONTEXT
*context
)
1413 DS_reg(context
) = GlobalHandleToSel16( GetTaskQueue16(0) );
1417 /***********************************************************************
1418 * GetTaskQueueES (KERNEL.119)
1420 void WINAPI
GetTaskQueueES16( CONTEXT
*context
)
1422 ES_reg(context
) = GlobalHandleToSel16( GetTaskQueue16(0) );
1426 /***********************************************************************
1427 * GetCurrentTask (KERNEL.36)
1429 HTASK16 WINAPI
GetCurrentTask(void)
1431 return THREAD_InitDone
? PROCESS_Current()->task
: 0;
1434 DWORD WINAPI
WIN16_GetCurrentTask(void)
1436 /* This is the version used by relay code; the first task is */
1437 /* returned in the high word of the result */
1438 return MAKELONG( GetCurrentTask(), hFirstTask
);
1442 /***********************************************************************
1443 * GetCurrentPDB (KERNEL.37)
1445 * UNDOC: returns PSP of KERNEL in high word
1447 DWORD WINAPI
GetCurrentPDB16(void)
1451 if (!(pTask
= (TDB
*)GlobalLock16( GetCurrentTask() ))) return 0;
1452 return MAKELONG(pTask
->hPDB
, 0); /* FIXME */
1456 /***********************************************************************
1457 * GetInstanceData (KERNEL.54)
1459 INT16 WINAPI
GetInstanceData16( HINSTANCE16 instance
, WORD buffer
, INT16 len
)
1461 char *ptr
= (char *)GlobalLock16( instance
);
1462 if (!ptr
|| !len
) return 0;
1463 if ((int)buffer
+ len
>= 0x10000) len
= 0x10000 - buffer
;
1464 memcpy( (char *)GlobalLock16(CURRENT_DS
) + buffer
, ptr
+ buffer
, len
);
1469 /***********************************************************************
1470 * GetExeVersion (KERNEL.105)
1472 WORD WINAPI
GetExeVersion16(void)
1476 if (!(pTask
= (TDB
*)GlobalLock16( GetCurrentTask() ))) return 0;
1477 return pTask
->version
;
1481 /***********************************************************************
1482 * SetErrorMode16 (KERNEL.107)
1484 UINT16 WINAPI
SetErrorMode16( UINT16 mode
)
1489 if (!(pTask
= (TDB
*)GlobalLock16( GetCurrentTask() ))) return 0;
1490 oldMode
= pTask
->error_mode
;
1491 pTask
->error_mode
= mode
;
1492 pTask
->thdb
->process
->error_mode
= mode
;
1497 /***********************************************************************
1498 * SetErrorMode32 (KERNEL32.486)
1500 UINT WINAPI
SetErrorMode( UINT mode
)
1502 return SetErrorMode16( (UINT16
)mode
);
1506 /***********************************************************************
1507 * GetDOSEnvironment (KERNEL.131)
1509 SEGPTR WINAPI
GetDOSEnvironment16(void)
1513 if (!(pTask
= (TDB
*)GlobalLock16( GetCurrentTask() ))) return 0;
1514 return PTR_SEG_OFF_TO_SEGPTR( pTask
->pdb
.environment
, 0 );
1518 /***********************************************************************
1519 * GetNumTasks (KERNEL.152)
1521 UINT16 WINAPI
GetNumTasks16(void)
1527 /***********************************************************************
1528 * GetTaskDS (KERNEL.155)
1530 * Note: this function apparently returns a DWORD with LOWORD == HIWORD.
1531 * I don't think we need to bother with this.
1533 HINSTANCE16 WINAPI
GetTaskDS16(void)
1537 if (!(pTask
= (TDB
*)GlobalLock16( GetCurrentTask() ))) return 0;
1538 return pTask
->hInstance
;
1541 /***********************************************************************
1542 * GetDummyModuleHandleDS (KERNEL.602)
1544 VOID WINAPI
GetDummyModuleHandleDS16( CONTEXT
*context
)
1549 AX_reg( context
) = 0;
1550 if (!(pTask
= (TDB
*)GlobalLock16( GetCurrentTask() ))) return;
1551 if (!(pTask
->flags
& TDBF_WIN32
)) return;
1553 selector
= GlobalHandleToSel16( pTask
->hModule
);
1554 DS_reg( context
) = selector
;
1555 AX_reg( context
) = selector
;
1558 /***********************************************************************
1559 * IsTask (KERNEL.320)
1561 BOOL16 WINAPI
IsTask16( HTASK16 hTask
)
1565 if (!(pTask
= (TDB
*)GlobalLock16( hTask
))) return FALSE
;
1566 if (GlobalSize16( hTask
) < sizeof(TDB
)) return FALSE
;
1567 return (pTask
->magic
== TDB_MAGIC
);
1571 /***********************************************************************
1572 * SetTaskSignalProc (KERNEL.38)
1574 * Real 16-bit interface is provided by the THUNK_SetTaskSignalProc.
1576 FARPROC16 WINAPI
SetTaskSignalProc( HTASK16 hTask
, FARPROC16 proc
)
1581 if (!hTask
) hTask
= GetCurrentTask();
1582 if (!(pTask
= (TDB
*)GlobalLock16( hTask
))) return NULL
;
1583 oldProc
= (FARPROC16
)pTask
->userhandler
;
1584 pTask
->userhandler
= (USERSIGNALPROC
)proc
;
1589 /***********************************************************************
1590 * SetSigHandler (KERNEL.140)
1592 WORD WINAPI
SetSigHandler16( FARPROC16 newhandler
, FARPROC16
* oldhandler
,
1593 UINT16
*oldmode
, UINT16 newmode
, UINT16 flag
)
1595 FIXME(task
,"(%p,%p,%p,%d,%d), unimplemented.\n",
1596 newhandler
,oldhandler
,oldmode
,newmode
,flag
);
1598 if (flag
!= 1) return 0;
1599 if (!newmode
) newhandler
= NULL
; /* Default handler */
1604 if (!(pTask
= (TDB
*)GlobalLock16( GetCurrentTask() ))) return 0;
1605 if (oldmode
) *oldmode
= pTask
->signal_flags
;
1606 pTask
->signal_flags
= newmode
;
1607 if (oldhandler
) *oldhandler
= pTask
->sighandler
;
1608 pTask
->sighandler
= newhandler
;
1614 /***********************************************************************
1615 * GlobalNotify (KERNEL.154)
1617 * Note that GlobalNotify does _not_ return the old NotifyProc
1618 * -- contrary to LocalNotify !!
1620 VOID WINAPI
GlobalNotify16( FARPROC16 proc
)
1624 if (!(pTask
= (TDB
*)GlobalLock16( GetCurrentTask() ))) return;
1625 pTask
->discardhandler
= proc
;
1629 /***********************************************************************
1630 * GetExePtr (KERNEL.133)
1632 static HMODULE16
GetExePtrHelper( HANDLE16 handle
, HTASK16
*hTask
)
1637 /* Check for module handle */
1639 if (!(ptr
= GlobalLock16( handle
))) return 0;
1640 if (((NE_MODULE
*)ptr
)->magic
== IMAGE_OS2_SIGNATURE
) return handle
;
1642 /* Search for this handle inside all tasks */
1644 *hTask
= hFirstTask
;
1647 TDB
*pTask
= (TDB
*)GlobalLock16( *hTask
);
1648 if ((*hTask
== handle
) ||
1649 (pTask
->hInstance
== handle
) ||
1650 (pTask
->hQueue
== handle
) ||
1651 (pTask
->hPDB
== handle
)) return pTask
->hModule
;
1652 *hTask
= pTask
->hNext
;
1655 /* Check the owner for module handle */
1657 owner
= FarGetOwner16( handle
);
1658 if (!(ptr
= GlobalLock16( owner
))) return 0;
1659 if (((NE_MODULE
*)ptr
)->magic
== IMAGE_OS2_SIGNATURE
) return owner
;
1661 /* Search for the owner inside all tasks */
1663 *hTask
= hFirstTask
;
1666 TDB
*pTask
= (TDB
*)GlobalLock16( *hTask
);
1667 if ((*hTask
== owner
) ||
1668 (pTask
->hInstance
== owner
) ||
1669 (pTask
->hQueue
== owner
) ||
1670 (pTask
->hPDB
== owner
)) return pTask
->hModule
;
1671 *hTask
= pTask
->hNext
;
1677 HMODULE16 WINAPI
GetExePtr( HANDLE16 handle
)
1680 return GetExePtrHelper( handle
, &dummy
);
1683 void WINAPI
WIN16_GetExePtr( CONTEXT
*context
)
1685 WORD
*stack
= PTR_SEG_OFF_TO_LIN(SS_reg(context
), SP_reg(context
));
1686 HANDLE16 handle
= (HANDLE16
)stack
[2];
1690 hModule
= GetExePtrHelper( handle
, &hTask
);
1692 AX_reg(context
) = CX_reg(context
) = hModule
;
1693 if (hTask
) ES_reg(context
) = hTask
;
1696 /***********************************************************************
1697 * TaskFirst (TOOLHELP.63)
1699 BOOL16 WINAPI
TaskFirst16( TASKENTRY
*lpte
)
1701 lpte
->hNext
= hFirstTask
;
1702 return TaskNext16( lpte
);
1706 /***********************************************************************
1707 * TaskNext (TOOLHELP.64)
1709 BOOL16 WINAPI
TaskNext16( TASKENTRY
*lpte
)
1712 INSTANCEDATA
*pInstData
;
1714 TRACE(toolhelp
, "(%p): task=%04x\n", lpte
, lpte
->hNext
);
1715 if (!lpte
->hNext
) return FALSE
;
1716 pTask
= (TDB
*)GlobalLock16( lpte
->hNext
);
1717 if (!pTask
|| pTask
->magic
!= TDB_MAGIC
) return FALSE
;
1718 pInstData
= (INSTANCEDATA
*)PTR_SEG_OFF_TO_LIN( pTask
->hInstance
, 0 );
1719 lpte
->hTask
= lpte
->hNext
;
1720 lpte
->hTaskParent
= pTask
->hParent
;
1721 lpte
->hInst
= pTask
->hInstance
;
1722 lpte
->hModule
= pTask
->hModule
;
1723 lpte
->wSS
= SELECTOROF( pTask
->thdb
->cur_stack
);
1724 lpte
->wSP
= OFFSETOF( pTask
->thdb
->cur_stack
);
1725 lpte
->wStackTop
= pInstData
->stacktop
;
1726 lpte
->wStackMinimum
= pInstData
->stackmin
;
1727 lpte
->wStackBottom
= pInstData
->stackbottom
;
1728 lpte
->wcEvents
= pTask
->nEvents
;
1729 lpte
->hQueue
= pTask
->hQueue
;
1730 strncpy( lpte
->szModule
, pTask
->module_name
, 8 );
1731 lpte
->szModule
[8] = '\0';
1732 lpte
->wPSPOffset
= 0x100; /*??*/
1733 lpte
->hNext
= pTask
->hNext
;
1738 /***********************************************************************
1739 * TaskFindHandle (TOOLHELP.65)
1741 BOOL16 WINAPI
TaskFindHandle16( TASKENTRY
*lpte
, HTASK16 hTask
)
1743 lpte
->hNext
= hTask
;
1744 return TaskNext16( lpte
);
1748 /***********************************************************************
1749 * GetAppCompatFlags16 (KERNEL.354)
1751 DWORD WINAPI
GetAppCompatFlags16( HTASK16 hTask
)
1753 return GetAppCompatFlags( hTask
);
1757 /***********************************************************************
1758 * GetAppCompatFlags32 (USER32.206)
1760 DWORD WINAPI
GetAppCompatFlags( HTASK hTask
)
1764 if (!hTask
) hTask
= GetCurrentTask();
1765 if (!(pTask
=(TDB
*)GlobalLock16( (HTASK16
)hTask
))) return 0;
1766 if (GlobalSize16(hTask
) < sizeof(TDB
)) return 0;
1767 return pTask
->compat_flags
;