Alexandre Julliard 4220b29be9 Take advantage of the new registers saved in the STACK16FRAME to turn
some 'register' functions into normal functions.
Removed the few remaining 'return' functions to simplify relay
handling.
1999-07-11 17:20:01 +00:00

1595 lines
46 KiB
C

/*
* Task functions
*
* Copyright 1995 Alexandre Julliard
*/
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include "wine/winbase16.h"
#include "user.h"
#include "callback.h"
#include "drive.h"
#include "file.h"
#include "global.h"
#include "instance.h"
#include "message.h"
#include "miscemu.h"
#include "module.h"
#include "neexe.h"
#include "peexe.h"
#include "pe_image.h"
#include "process.h"
#include "queue.h"
#include "selectors.h"
#include "stackframe.h"
#include "task.h"
#include "thread.h"
#include "toolhelp.h"
#include "winnt.h"
#include "winsock.h"
#include "thread.h"
#include "syslevel.h"
#include "debugtools.h"
#include "dosexe.h"
#include "services.h"
#include "server.h"
DECLARE_DEBUG_CHANNEL(relay)
DECLARE_DEBUG_CHANNEL(task)
DECLARE_DEBUG_CHANNEL(toolhelp)
/* Min. number of thunks allocated when creating a new segment */
#define MIN_THUNKS 32
/* Pointer to function to switch to a larger stack */
int (*IF1632_CallLargeStack)( int (*func)(), void *arg ) = NULL;
/* Pointer to debugger callback routine */
void (*TASK_AddTaskEntryBreakpoint)( HTASK16 hTask ) = NULL;
static THHOOK DefaultThhook = { 0 };
THHOOK *pThhook = &DefaultThhook;
#define hCurrentTask (pThhook->CurTDB)
#define hFirstTask (pThhook->HeadTDB)
#define hLockedTask (pThhook->LockTDB)
static UINT16 nTaskCount = 0;
/***********************************************************************
* TASK_InstallTHHook
*/
void TASK_InstallTHHook( THHOOK *pNewThhook )
{
THHOOK *pOldThhook = pThhook;
pThhook = pNewThhook? pNewThhook : &DefaultThhook;
*pThhook = *pOldThhook;
}
/***********************************************************************
* TASK_GetNextTask
*/
HTASK16 TASK_GetNextTask( HTASK16 hTask )
{
TDB* pTask = (TDB*)GlobalLock16(hTask);
if (pTask->hNext) return pTask->hNext;
return (hFirstTask != hTask) ? hFirstTask : 0;
}
/***********************************************************************
* TASK_LinkTask
*/
static void TASK_LinkTask( HTASK16 hTask )
{
HTASK16 *prevTask;
TDB *pTask;
if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
prevTask = &hFirstTask;
while (*prevTask)
{
TDB *prevTaskPtr = (TDB *)GlobalLock16( *prevTask );
if (prevTaskPtr->priority >= pTask->priority) break;
prevTask = &prevTaskPtr->hNext;
}
pTask->hNext = *prevTask;
*prevTask = hTask;
nTaskCount++;
}
/***********************************************************************
* TASK_UnlinkTask
*/
static void TASK_UnlinkTask( HTASK16 hTask )
{
HTASK16 *prevTask;
TDB *pTask;
prevTask = &hFirstTask;
while (*prevTask && (*prevTask != hTask))
{
pTask = (TDB *)GlobalLock16( *prevTask );
prevTask = &pTask->hNext;
}
if (*prevTask)
{
pTask = (TDB *)GlobalLock16( *prevTask );
*prevTask = pTask->hNext;
pTask->hNext = 0;
nTaskCount--;
}
}
/***********************************************************************
* TASK_CreateThunks
*
* Create a thunk free-list in segment 'handle', starting from offset 'offset'
* and containing 'count' entries.
*/
static void TASK_CreateThunks( HGLOBAL16 handle, WORD offset, WORD count )
{
int i;
WORD free;
THUNKS *pThunk;
pThunk = (THUNKS *)((BYTE *)GlobalLock16( handle ) + offset);
pThunk->next = 0;
pThunk->magic = THUNK_MAGIC;
pThunk->free = (int)&pThunk->thunks - (int)pThunk;
free = pThunk->free;
for (i = 0; i < count-1; i++)
{
free += 8; /* Offset of next thunk */
pThunk->thunks[4*i] = free;
}
pThunk->thunks[4*i] = 0; /* Last thunk */
}
/***********************************************************************
* TASK_AllocThunk
*
* Allocate a thunk for MakeProcInstance().
*/
static SEGPTR TASK_AllocThunk( HTASK16 hTask )
{
TDB *pTask;
THUNKS *pThunk;
WORD sel, base;
if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
sel = pTask->hCSAlias;
pThunk = &pTask->thunks;
base = (int)pThunk - (int)pTask;
while (!pThunk->free)
{
sel = pThunk->next;
if (!sel) /* Allocate a new segment */
{
sel = GLOBAL_Alloc( GMEM_FIXED, sizeof(THUNKS) + (MIN_THUNKS-1)*8,
pTask->hPDB, TRUE, FALSE, FALSE );
if (!sel) return (SEGPTR)0;
TASK_CreateThunks( sel, 0, MIN_THUNKS );
pThunk->next = sel;
}
pThunk = (THUNKS *)GlobalLock16( sel );
base = 0;
}
base += pThunk->free;
pThunk->free = *(WORD *)((BYTE *)pThunk + pThunk->free);
return PTR_SEG_OFF_TO_SEGPTR( sel, base );
}
/***********************************************************************
* TASK_FreeThunk
*
* Free a MakeProcInstance() thunk.
*/
static BOOL TASK_FreeThunk( HTASK16 hTask, SEGPTR thunk )
{
TDB *pTask;
THUNKS *pThunk;
WORD sel, base;
if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
sel = pTask->hCSAlias;
pThunk = &pTask->thunks;
base = (int)pThunk - (int)pTask;
while (sel && (sel != HIWORD(thunk)))
{
sel = pThunk->next;
pThunk = (THUNKS *)GlobalLock16( sel );
base = 0;
}
if (!sel) return FALSE;
*(WORD *)((BYTE *)pThunk + LOWORD(thunk) - base) = pThunk->free;
pThunk->free = LOWORD(thunk) - base;
return TRUE;
}
/***********************************************************************
* TASK_CallToStart
*
* 32-bit entry point for a new task. This function is responsible for
* setting up the registers and jumping to the 16-bit entry point.
*/
void TASK_CallToStart(void)
{
TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
NE_MODULE *pModule = NE_GetPtr( pTask->hModule );
SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
CONTEXT86 context;
/* Add task to 16-bit scheduler pool */
TASK_Reschedule();
/* Registers at initialization must be:
* ax zero
* bx stack size in bytes
* cx heap size in bytes
* si previous app instance
* di current app instance
* bp zero
* es selector to the PSP
* ds dgroup of the application
* ss stack selector
* sp top of the stack
*/
memset( &context, 0, sizeof(context) );
CS_reg(&context) = GlobalHandleToSel16(pSegTable[pModule->cs - 1].hSeg);
DS_reg(&context) = GlobalHandleToSel16(pTask->hInstance);
ES_reg(&context) = pTask->hPDB;
EIP_reg(&context) = pModule->ip;
EBX_reg(&context) = pModule->stack_size;
ECX_reg(&context) = pModule->heap_size;
EDI_reg(&context) = pTask->hInstance;
ESI_reg(&context) = pTask->hPrevInstance;
TRACE_(task)("Starting main program: cs:ip=%04lx:%04x ds=%04lx ss:sp=%04x:%04x\n",
CS_reg(&context), IP_reg(&context), DS_reg(&context),
SELECTOROF(pTask->teb->cur_stack),
OFFSETOF(pTask->teb->cur_stack) );
Callbacks->CallRegisterShortProc( &context, 0 );
}
/***********************************************************************
* TASK_Create
*
* NOTE: This routine might be called by a Win32 thread. Thus, we need
* to be careful to protect global data structures. We do this
* by entering the Win16Lock while linking the task into the
* global task list.
*/
BOOL TASK_Create( NE_MODULE *pModule, UINT16 cmdShow)
{
HTASK16 hTask;
TDB *pTask;
LPSTR cmd_line;
char name[10];
PDB *pdb32 = PROCESS_Current();
/* Allocate the task structure */
hTask = GLOBAL_Alloc( GMEM_FIXED | GMEM_ZEROINIT, sizeof(TDB),
pModule->self, FALSE, FALSE, FALSE );
if (!hTask) return FALSE;
pTask = (TDB *)GlobalLock16( hTask );
/* Fill the task structure */
pTask->nEvents = 0;
pTask->hSelf = hTask;
pTask->flags = 0;
if (pModule->flags & NE_FFLAGS_WIN32)
pTask->flags |= TDBF_WIN32;
if (pModule->lpDosTask)
pTask->flags |= TDBF_WINOLDAP;
pTask->hInstance = pModule->self;
pTask->hPrevInstance = 0;
/* NOTE: for 16-bit tasks, the instance handles are updated later on
in NE_InitProcess */
pTask->version = pModule->expected_version;
pTask->hModule = pModule->self;
pTask->hParent = GetCurrentTask();
pTask->magic = TDB_MAGIC;
pTask->nCmdShow = cmdShow;
pTask->teb = NtCurrentTeb();
pTask->curdrive = DRIVE_GetCurrentDrive() | 0x80;
strcpy( pTask->curdir, "\\" );
lstrcpynA( pTask->curdir + 1, DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() ),
sizeof(pTask->curdir) - 1 );
/* Create the thunks block */
TASK_CreateThunks( hTask, (int)&pTask->thunks - (int)pTask, 7 );
/* Copy the module name */
GetModuleName16( pModule->self, name, sizeof(name) );
strncpy( pTask->module_name, name, sizeof(pTask->module_name) );
/* Allocate a selector for the PDB */
pTask->hPDB = GLOBAL_CreateBlock( GMEM_FIXED, &pTask->pdb, sizeof(PDB16),
pModule->self, FALSE, FALSE, FALSE, NULL );
/* Fill the PDB */
pTask->pdb.int20 = 0x20cd;
pTask->pdb.dispatcher[0] = 0x9a; /* ljmp */
PUT_DWORD(&pTask->pdb.dispatcher[1], (DWORD)NE_GetEntryPoint(
GetModuleHandle16("KERNEL"), 102 )); /* KERNEL.102 is DOS3Call() */
pTask->pdb.savedint22 = INT_GetPMHandler( 0x22 );
pTask->pdb.savedint23 = INT_GetPMHandler( 0x23 );
pTask->pdb.savedint24 = INT_GetPMHandler( 0x24 );
pTask->pdb.fileHandlesPtr =
PTR_SEG_OFF_TO_SEGPTR( GlobalHandleToSel16(pTask->hPDB),
(int)&((PDB16 *)0)->fileHandles );
pTask->pdb.hFileHandles = 0;
memset( pTask->pdb.fileHandles, 0xff, sizeof(pTask->pdb.fileHandles) );
pTask->pdb.environment = pdb32->env_db->env_sel;
pTask->pdb.nbFiles = 20;
/* Fill the command line */
cmd_line = pdb32->env_db->cmd_line;
while (*cmd_line && (*cmd_line != ' ') && (*cmd_line != '\t')) cmd_line++;
while ((*cmd_line == ' ') || (*cmd_line == '\t')) cmd_line++;
lstrcpynA( pTask->pdb.cmdLine+1, cmd_line, sizeof(pTask->pdb.cmdLine)-1);
pTask->pdb.cmdLine[0] = strlen( pTask->pdb.cmdLine + 1 );
/* Get the compatibility flags */
pTask->compat_flags = GetProfileIntA( "Compatibility", name, 0 );
/* Allocate a code segment alias for the TDB */
pTask->hCSAlias = GLOBAL_CreateBlock( GMEM_FIXED, (void *)pTask,
sizeof(TDB), pTask->hPDB, TRUE,
FALSE, FALSE, NULL );
/* Set the owner of the environment block */
FarSetOwner16( pTask->pdb.environment, pTask->hPDB );
/* Default DTA overwrites command-line */
pTask->dta = PTR_SEG_OFF_TO_SEGPTR( pTask->hPDB,
(int)&pTask->pdb.cmdLine - (int)&pTask->pdb );
/* Create scheduler event for 16-bit tasks */
if ( !(pTask->flags & TDBF_WIN32) )
{
pTask->hEvent = CreateEventA( NULL, TRUE, FALSE, NULL );
pTask->hEvent = ConvertToGlobalHandle( pTask->hEvent );
}
/* Enter task handle into thread and process */
pTask->teb->htask16 = pTask->teb->process->task = hTask;
TRACE_(task)("module='%s' cmdline='%s' task=%04x\n", name, cmd_line, hTask );
/* If requested, add entry point breakpoint */
if ( TASK_AddTaskEntryBreakpoint )
TASK_AddTaskEntryBreakpoint( hTask );
/* Add the task to the linked list */
SYSLEVEL_EnterWin16Lock();
TASK_LinkTask( hTask );
SYSLEVEL_LeaveWin16Lock();
return TRUE;
}
/***********************************************************************
* TASK_DeleteTask
*/
static void TASK_DeleteTask( HTASK16 hTask )
{
TDB *pTask;
HGLOBAL16 hPDB;
if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
hPDB = pTask->hPDB;
pTask->magic = 0xdead; /* invalidate signature */
/* Delete the Win32 part of the task */
/* PROCESS_FreePDB( pTask->teb->process ); FIXME */
/* K32OBJ_DecCount( &pTask->teb->header ); FIXME */
/* Free the selector aliases */
GLOBAL_FreeBlock( pTask->hCSAlias );
GLOBAL_FreeBlock( pTask->hPDB );
/* Free the task module */
FreeModule16( pTask->hModule );
/* Free the task structure itself */
GlobalFree16( hTask );
/* Free all memory used by this task (including the 32-bit stack, */
/* the environment block and the thunk segments). */
GlobalFreeAll16( hPDB );
}
/***********************************************************************
* TASK_KillTask
*/
void TASK_KillTask( HTASK16 hTask )
{
TDB *pTask;
/* Enter the Win16Lock to protect global data structures */
SYSLEVEL_EnterWin16Lock();
if ( !hTask ) hTask = GetCurrentTask();
pTask = (TDB *)GlobalLock16( hTask );
if ( !pTask )
{
SYSLEVEL_LeaveWin16Lock();
return;
}
TRACE_(task)("Killing task %04x\n", hTask );
/* Delete active sockets */
if( pTask->pwsi )
WINSOCK_DeleteTaskWSI( pTask, pTask->pwsi );
#ifdef MZ_SUPPORTED
{
/* Kill DOS VM task */
NE_MODULE *pModule = NE_GetPtr( pTask->hModule );
if ( pModule->lpDosTask )
MZ_KillModule( pModule->lpDosTask );
}
#endif
/* Perform USER cleanup */
if (pTask->userhandler)
pTask->userhandler( hTask, USIG16_TERMINATION, 0,
pTask->hInstance, pTask->hQueue );
PROCESS_CallUserSignalProc( USIG_PROCESS_EXIT, 0 );
PROCESS_CallUserSignalProc( USIG_THREAD_EXIT, 0 ); /* FIXME */
PROCESS_CallUserSignalProc( USIG_PROCESS_DESTROY, 0 );
if (nTaskCount <= 1)
{
TRACE_(task)("this is the last task, exiting\n" );
ExitKernel16();
}
/* FIXME: Hack! Send a message to the initial task so that
* the GetMessage wakes up and the initial task can check whether
* it is the only remaining one and terminate itself ...
* The initial task should probably install hooks or something
* to get informed about task termination :-/
*/
Callout.PostAppMessage16( PROCESS_Initial()->task, WM_NULL, 0, 0 );
/* Remove the task from the list to be sure we never switch back to it */
TASK_UnlinkTask( hTask );
if( nTaskCount )
{
TDB* p = (TDB *)GlobalLock16( hFirstTask );
while( p )
{
if( p->hYieldTo == hTask ) p->hYieldTo = 0;
p = (TDB *)GlobalLock16( p->hNext );
}
}
pTask->nEvents = 0;
if ( hLockedTask == hTask )
hLockedTask = 0;
TASK_DeleteTask( hTask );
/* When deleting the current task ... */
if ( hTask == hCurrentTask )
{
DWORD lockCount;
/* ... schedule another one ... */
TASK_Reschedule();
/* ... and completely release the Win16Lock, just in case. */
ReleaseThunkLock( &lockCount );
return;
}
SYSLEVEL_LeaveWin16Lock();
}
/***********************************************************************
* TASK_Reschedule
*
* This is where all the magic of task-switching happens!
*
* 16-bit Windows performs non-preemptive (cooperative) multitasking.
* This means that each 16-bit task runs until it voluntarily yields
* control, at which point the scheduler gets active and selects the
* next task to run.
*
* In Wine, all processes, even 16-bit ones, are scheduled preemptively
* by the standard scheduler of the underlying OS. As many 16-bit apps
* *rely* on the behaviour of the Windows scheduler, however, we have
* to simulate that behaviour.
*
* This is achieved as follows: every 16-bit task is at time (except
* during task creation and deletion) in one of two states: either it
* is the one currently running, then the global variable hCurrentTask
* contains its task handle, or it is not currently running, then it
* is blocked on a special scheduler event, a global handle to which
* is stored in the task struct.
*
* When the current task yields control, this routine gets called. Its
* purpose is to determine the next task to be active, signal the
* scheduler event of that task, and then put the current task to sleep
* waiting for *its* scheduler event to get signalled again.
*
* This routine can get called in a few other special situations as well:
*
* - On creation of a 16-bit task, the Unix process executing the task
* calls TASK_Reschedule once it has completed its initialization.
* At this point, the task needs to be blocked until its scheduler
* event is signalled the first time (this will be done by the parent
* process to get the task up and running).
*
* - When the task currently running terminates itself, this routine gets
* called and has to schedule another task, *without* blocking the
* terminating task.
*
* - When a 32-bit thread posts an event for a 16-bit task, it might be
* the case that *no* 16-bit task is currently running. In this case
* the task that has now an event pending is to be scheduled.
*
*/
void TASK_Reschedule(void)
{
TDB *pOldTask = NULL, *pNewTask = NULL;
HTASK16 hOldTask = 0, hNewTask = 0;
enum { MODE_YIELD, MODE_SLEEP, MODE_WAKEUP } mode;
DWORD lockCount;
SYSLEVEL_EnterWin16Lock();
/* Check what we need to do */
hOldTask = GetCurrentTask();
pOldTask = (TDB *)GlobalLock16( hOldTask );
TRACE_(task)( "entered with hCurrentTask %04x by hTask %04x (pid %ld)\n",
hCurrentTask, hOldTask, (long) getpid() );
if ( pOldTask && THREAD_IsWin16( NtCurrentTeb() ) )
{
/* We are called by an active (non-deleted) 16-bit task */
/* If we don't even have a current task, or else the current
task has yielded, we'll need to schedule a new task and
(possibly) put the calling task to sleep. Otherwise, we
only block the caller. */
if ( !hCurrentTask || hCurrentTask == hOldTask )
mode = MODE_YIELD;
else
mode = MODE_SLEEP;
}
else
{
/* We are called by a deleted 16-bit task or a 32-bit thread */
/* The only situation where we need to do something is if we
now do not have a current task. Then, we'll need to wake up
some task that has events pending. */
if ( !hCurrentTask || hCurrentTask == hOldTask )
mode = MODE_WAKEUP;
else
{
/* nothing to do */
SYSLEVEL_LeaveWin16Lock();
return;
}
}
/* Find a task to yield to: check for DirectedYield() */
if ( mode == MODE_YIELD && pOldTask && pOldTask->hYieldTo )
{
hNewTask = pOldTask->hYieldTo;
pNewTask = (TDB *)GlobalLock16( hNewTask );
if( !pNewTask || !pNewTask->nEvents) hNewTask = 0;
pOldTask->hYieldTo = 0;
}
/* Find a task to yield to: check for pending events */
if ( (mode == MODE_YIELD || mode == MODE_WAKEUP) && !hNewTask )
{
hNewTask = hFirstTask;
while (hNewTask)
{
pNewTask = (TDB *)GlobalLock16( hNewTask );
TRACE_(task)( "\ttask = %04x, events = %i\n",
hNewTask, pNewTask->nEvents );
if (pNewTask->nEvents) break;
hNewTask = pNewTask->hNext;
}
if (hLockedTask && (hNewTask != hLockedTask)) hNewTask = 0;
}
/* If we are still the task with highest priority, just return ... */
if ( mode == MODE_YIELD && hNewTask == hCurrentTask )
{
TRACE_(task)("returning to the current task (%04x)\n", hCurrentTask );
SYSLEVEL_LeaveWin16Lock();
/* Allow Win32 threads to thunk down even while a Win16 task is
in a tight PeekMessage() or Yield() loop ... */
ReleaseThunkLock( &lockCount );
RestoreThunkLock( lockCount );
return;
}
/* If no task to yield to found, suspend 16-bit scheduler ... */
if ( mode == MODE_YIELD && !hNewTask )
{
TRACE_(task)("No currently active task\n");
hCurrentTask = 0;
}
/* If we found a task to wake up, do it ... */
if ( (mode == MODE_YIELD || mode == MODE_WAKEUP) && hNewTask )
{
TRACE_(task)("Switching to task %04x (%.8s)\n",
hNewTask, pNewTask->module_name );
pNewTask->priority++;
TASK_UnlinkTask( hNewTask );
TASK_LinkTask( hNewTask );
pNewTask->priority--;
hCurrentTask = hNewTask;
SetEvent( pNewTask->hEvent );
/* This is set just in case some app reads it ... */
pNewTask->ss_sp = pNewTask->teb->cur_stack;
}
/* If we need to put the current task to sleep, do it ... */
if ( (mode == MODE_YIELD || mode == MODE_SLEEP) && hOldTask != hCurrentTask )
{
ResetEvent( pOldTask->hEvent );
ReleaseThunkLock( &lockCount );
SYSLEVEL_CheckNotLevel( 1 );
WaitForSingleObject( pOldTask->hEvent, INFINITE );
RestoreThunkLock( lockCount );
}
SYSLEVEL_LeaveWin16Lock();
}
/***********************************************************************
* InitTask (KERNEL.91)
*
* Called by the application startup code.
*/
void WINAPI InitTask16( CONTEXT86 *context )
{
TDB *pTask;
INSTANCEDATA *pinstance;
SEGPTR ptr;
EAX_reg(context) = 0;
if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
/* Note: we need to trust that BX/CX contain the stack/heap sizes,
as some apps, notably Visual Basic apps, *modify* the heap/stack
size of the instance data segment before calling InitTask() */
/* Initialize the INSTANCEDATA structure */
pinstance = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN(CURRENT_DS, 0);
pinstance->stackmin = OFFSETOF( pTask->teb->cur_stack );
pinstance->stackbottom = pinstance->stackmin; /* yup, that's right. Confused me too. */
pinstance->stacktop = ( pinstance->stackmin > BX_reg(context)?
pinstance->stackmin - BX_reg(context) : 0 ) + 150;
/* Initialize the local heap */
if ( CX_reg(context) )
LocalInit16( pTask->hInstance, 0, CX_reg(context) );
/* Initialize implicitly loaded DLLs */
NE_InitializeDLLs( pTask->hModule );
/* Registers on return are:
* ax 1 if OK, 0 on error
* cx stack limit in bytes
* dx cmdShow parameter
* si instance handle of the previous instance
* di instance handle of the new task
* es:bx pointer to command-line inside PSP
*
* 0 (=%bp) is pushed on the stack
*/
ptr = STACK16_PUSH( pTask->teb, sizeof(WORD) );
*(WORD *)PTR_SEG_TO_LIN(ptr) = 0;
SP_reg(context) -= 2;
EAX_reg(context) = 1;
if (!pTask->pdb.cmdLine[0]) EBX_reg(context) = 0x80;
else
{
LPBYTE p = &pTask->pdb.cmdLine[1];
while ((*p == ' ') || (*p == '\t')) p++;
EBX_reg(context) = 0x80 + (p - pTask->pdb.cmdLine);
}
ECX_reg(context) = pinstance->stacktop;
EDX_reg(context) = pTask->nCmdShow;
ESI_reg(context) = (DWORD)pTask->hPrevInstance;
EDI_reg(context) = (DWORD)pTask->hInstance;
ES_reg (context) = (WORD)pTask->hPDB;
}
/***********************************************************************
* WaitEvent (KERNEL.30)
*/
BOOL16 WINAPI WaitEvent16( HTASK16 hTask )
{
TDB *pTask;
if (!hTask) hTask = GetCurrentTask();
pTask = (TDB *)GlobalLock16( hTask );
if ( !THREAD_IsWin16( NtCurrentTeb() ) )
{
FIXME_(task)("called for Win32 thread (%04x)!\n", NtCurrentTeb()->teb_sel);
return TRUE;
}
if (pTask->nEvents > 0)
{
pTask->nEvents--;
return FALSE;
}
TASK_Reschedule();
/* When we get back here, we have an event */
if (pTask->nEvents > 0) pTask->nEvents--;
return TRUE;
}
/***********************************************************************
* PostEvent (KERNEL.31)
*/
void WINAPI PostEvent16( HTASK16 hTask )
{
TDB *pTask;
if (!hTask) hTask = GetCurrentTask();
if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
if ( !THREAD_IsWin16( pTask->teb ) )
{
FIXME_(task)("called for Win32 thread (%04x)!\n", pTask->teb->teb_sel );
return;
}
pTask->nEvents++;
/* If we are a 32-bit task, we might need to wake up the 16-bit scheduler */
if ( !THREAD_IsWin16( NtCurrentTeb() ) )
TASK_Reschedule();
}
/***********************************************************************
* SetPriority (KERNEL.32)
*/
void WINAPI SetPriority16( HTASK16 hTask, INT16 delta )
{
TDB *pTask;
INT16 newpriority;
if (!hTask) hTask = GetCurrentTask();
if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
newpriority = pTask->priority + delta;
if (newpriority < -32) newpriority = -32;
else if (newpriority > 15) newpriority = 15;
pTask->priority = newpriority + 1;
TASK_UnlinkTask( hTask );
TASK_LinkTask( hTask );
pTask->priority--;
}
/***********************************************************************
* LockCurrentTask (KERNEL.33)
*/
HTASK16 WINAPI LockCurrentTask16( BOOL16 bLock )
{
if (bLock) hLockedTask = GetCurrentTask();
else hLockedTask = 0;
return hLockedTask;
}
/***********************************************************************
* IsTaskLocked (KERNEL.122)
*/
HTASK16 WINAPI IsTaskLocked16(void)
{
return hLockedTask;
}
/***********************************************************************
* OldYield (KERNEL.117)
*/
void WINAPI OldYield16(void)
{
TDB *pCurTask = (TDB *)GlobalLock16( GetCurrentTask() );
if ( !THREAD_IsWin16( NtCurrentTeb() ) )
{
FIXME_(task)("called for Win32 thread (%04x)!\n", NtCurrentTeb()->teb_sel);
return;
}
if (pCurTask) pCurTask->nEvents++; /* Make sure we get back here */
TASK_Reschedule();
if (pCurTask) pCurTask->nEvents--;
}
/***********************************************************************
* DirectedYield (KERNEL.150)
*/
void WINAPI DirectedYield16( HTASK16 hTask )
{
TDB *pCurTask = (TDB *)GlobalLock16( GetCurrentTask() );
if ( !THREAD_IsWin16( NtCurrentTeb() ) )
{
FIXME_(task)("called for Win32 thread (%04x)!\n", NtCurrentTeb()->teb_sel);
return;
}
TRACE_(task)("%04x: DirectedYield(%04x)\n", pCurTask->hSelf, hTask );
pCurTask->hYieldTo = hTask;
OldYield16();
TRACE_(task)("%04x: back from DirectedYield(%04x)\n", pCurTask->hSelf, hTask );
}
/***********************************************************************
* Yield16 (KERNEL.29)
*/
void WINAPI Yield16(void)
{
TDB *pCurTask = (TDB *)GlobalLock16( GetCurrentTask() );
if (pCurTask) pCurTask->hYieldTo = 0;
if (pCurTask && pCurTask->hQueue) Callout.UserYield16();
else OldYield16();
}
/***********************************************************************
* KERNEL_490 (KERNEL.490)
*/
HTASK16 WINAPI KERNEL_490( HTASK16 someTask )
{
if ( !someTask ) return 0;
FIXME_(task)("(%04x): stub\n", someTask );
return 0;
}
/***********************************************************************
* MakeProcInstance16 (KERNEL.51)
*/
FARPROC16 WINAPI MakeProcInstance16( FARPROC16 func, HANDLE16 hInstance )
{
BYTE *thunk,*lfunc;
SEGPTR thunkaddr;
if (!func) {
ERR_(task)("Ouch ! MakeProcInstance called with func == NULL !\n");
return (FARPROC16)0; /* Windows seems to do the same */
}
if (!hInstance) hInstance = CURRENT_DS;
thunkaddr = TASK_AllocThunk( GetCurrentTask() );
if (!thunkaddr) return (FARPROC16)0;
thunk = PTR_SEG_TO_LIN( thunkaddr );
lfunc = PTR_SEG_TO_LIN( func );
TRACE_(task)("(%08lx,%04x): got thunk %08lx\n",
(DWORD)func, hInstance, (DWORD)thunkaddr );
if (((lfunc[0]==0x8c) && (lfunc[1]==0xd8)) ||
((lfunc[0]==0x1e) && (lfunc[1]==0x58))
) {
FIXME_(task)("thunk would be useless for %p, overwriting with nop;nop;\n", func );
lfunc[0]=0x90; /* nop */
lfunc[1]=0x90; /* nop */
}
*thunk++ = 0xb8; /* movw instance, %ax */
*thunk++ = (BYTE)(hInstance & 0xff);
*thunk++ = (BYTE)(hInstance >> 8);
*thunk++ = 0xea; /* ljmp func */
*(DWORD *)thunk = (DWORD)func;
return (FARPROC16)thunkaddr;
}
/***********************************************************************
* FreeProcInstance16 (KERNEL.52)
*/
void WINAPI FreeProcInstance16( FARPROC16 func )
{
TRACE_(task)("(%08lx)\n", (DWORD)func );
TASK_FreeThunk( GetCurrentTask(), (SEGPTR)func );
}
/**********************************************************************
* GetCodeHandle (KERNEL.93)
*/
HANDLE16 WINAPI GetCodeHandle16( FARPROC16 proc )
{
HANDLE16 handle;
BYTE *thunk = (BYTE *)PTR_SEG_TO_LIN( proc );
/* Return the code segment containing 'proc'. */
/* Not sure if this is really correct (shouldn't matter that much). */
/* Check if it is really a thunk */
if ((thunk[0] == 0xb8) && (thunk[3] == 0xea))
handle = GlobalHandle16( thunk[6] + (thunk[7] << 8) );
else
handle = GlobalHandle16( HIWORD(proc) );
return handle;
}
/**********************************************************************
* GetCodeInfo (KERNEL.104)
*/
VOID WINAPI GetCodeInfo16( FARPROC16 proc, SEGINFO *segInfo )
{
BYTE *thunk = (BYTE *)PTR_SEG_TO_LIN( proc );
NE_MODULE *pModule = NULL;
SEGTABLEENTRY *pSeg = NULL;
WORD segNr;
/* proc is either a thunk, or else a pair of module handle
and segment number. In the first case, we also need to
extract module and segment number. */
if ((thunk[0] == 0xb8) && (thunk[3] == 0xea))
{
WORD selector = thunk[6] + (thunk[7] << 8);
pModule = NE_GetPtr( GlobalHandle16( selector ) );
pSeg = pModule? NE_SEG_TABLE( pModule ) : NULL;
if ( pModule )
for ( segNr = 0; segNr < pModule->seg_count; segNr++, pSeg++ )
if ( GlobalHandleToSel16(pSeg->hSeg) == selector )
break;
if ( pModule && segNr >= pModule->seg_count )
pSeg = NULL;
}
else
{
pModule = NE_GetPtr( HIWORD( proc ) );
segNr = LOWORD( proc );
if ( pModule && segNr < pModule->seg_count )
pSeg = NE_SEG_TABLE( pModule ) + segNr;
}
/* fill in segment information */
segInfo->offSegment = pSeg? pSeg->filepos : 0;
segInfo->cbSegment = pSeg? pSeg->size : 0;
segInfo->flags = pSeg? pSeg->flags : 0;
segInfo->cbAlloc = pSeg? pSeg->minsize : 0;
segInfo->h = pSeg? pSeg->hSeg : 0;
segInfo->alignShift = pModule? pModule->alignment : 0;
}
/**********************************************************************
* DefineHandleTable16 (KERNEL.94)
*/
BOOL16 WINAPI DefineHandleTable16( WORD wOffset )
{
return TRUE; /* FIXME */
}
/***********************************************************************
* SetTaskQueue (KERNEL.34)
*/
HQUEUE16 WINAPI SetTaskQueue16( HTASK16 hTask, HQUEUE16 hQueue )
{
HQUEUE16 hPrev;
TDB *pTask;
if (!hTask) hTask = GetCurrentTask();
if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
hPrev = pTask->hQueue;
pTask->hQueue = hQueue;
return hPrev;
}
/***********************************************************************
* GetTaskQueue (KERNEL.35)
*/
HQUEUE16 WINAPI GetTaskQueue16( HTASK16 hTask )
{
TDB *pTask;
if (!hTask) hTask = GetCurrentTask();
if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
return pTask->hQueue;
}
/***********************************************************************
* SetThreadQueue (KERNEL.463)
*/
HQUEUE16 WINAPI SetThreadQueue16( DWORD thread, HQUEUE16 hQueue )
{
TEB *teb = thread? THREAD_IdToTEB( thread ) : NtCurrentTeb();
HQUEUE16 oldQueue = teb? teb->queue : 0;
if ( teb )
{
teb->queue = hQueue;
if ( GetTaskQueue16( teb->process->task ) == oldQueue )
SetTaskQueue16( teb->process->task, hQueue );
}
return oldQueue;
}
/***********************************************************************
* GetThreadQueue (KERNEL.464)
*/
HQUEUE16 WINAPI GetThreadQueue16( DWORD thread )
{
TEB *teb = NULL;
if ( !thread )
teb = NtCurrentTeb();
else if ( HIWORD(thread) )
teb = THREAD_IdToTEB( thread );
else if ( IsTask16( (HTASK16)thread ) )
teb = ((TDB *)GlobalLock16( (HANDLE16)thread ))->teb;
return (HQUEUE16)(teb? teb->queue : 0);
}
/***********************************************************************
* SetFastQueue (KERNEL.624)
*/
VOID WINAPI SetFastQueue16( DWORD thread, HANDLE hQueue )
{
TEB *teb = NULL;
if ( !thread )
teb = NtCurrentTeb();
else if ( HIWORD(thread) )
teb = THREAD_IdToTEB( thread );
else if ( IsTask16( (HTASK16)thread ) )
teb = ((TDB *)GlobalLock16( (HANDLE16)thread ))->teb;
if ( teb ) teb->queue = (HQUEUE16) hQueue;
}
/***********************************************************************
* GetFastQueue (KERNEL.625)
*/
HANDLE WINAPI GetFastQueue16( void )
{
TEB *teb = NtCurrentTeb();
if (!teb) return 0;
if (!teb->queue)
Callout.InitThreadInput16( 0, THREAD_IsWin16(teb)? 4 : 5 );
if (!teb->queue)
FIXME_(task)("(): should initialize thread-local queue, expect failure!\n" );
return (HANDLE)teb->queue;
}
/***********************************************************************
* SwitchStackTo (KERNEL.108)
*/
void WINAPI SwitchStackTo16( WORD seg, WORD ptr, WORD top )
{
TDB *pTask;
STACK16FRAME *oldFrame, *newFrame;
INSTANCEDATA *pData;
UINT16 copySize;
if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
if (!(pData = (INSTANCEDATA *)GlobalLock16( seg ))) return;
TRACE_(task)("old=%04x:%04x new=%04x:%04x\n",
SELECTOROF( pTask->teb->cur_stack ),
OFFSETOF( pTask->teb->cur_stack ), seg, ptr );
/* Save the old stack */
oldFrame = THREAD_STACK16( pTask->teb );
/* pop frame + args and push bp */
pData->old_ss_sp = pTask->teb->cur_stack + sizeof(STACK16FRAME)
+ 2 * sizeof(WORD);
*(WORD *)PTR_SEG_TO_LIN(pData->old_ss_sp) = oldFrame->bp;
pData->stacktop = top;
pData->stackmin = ptr;
pData->stackbottom = ptr;
/* Switch to the new stack */
/* Note: we need to take the 3 arguments into account; otherwise,
* the stack will underflow upon return from this function.
*/
copySize = oldFrame->bp - OFFSETOF(pData->old_ss_sp);
copySize += 3 * sizeof(WORD) + sizeof(STACK16FRAME);
pTask->teb->cur_stack = PTR_SEG_OFF_TO_SEGPTR( seg, ptr - copySize );
newFrame = THREAD_STACK16( pTask->teb );
/* Copy the stack frame and the local variables to the new stack */
memmove( newFrame, oldFrame, copySize );
newFrame->bp = ptr;
*(WORD *)PTR_SEG_OFF_TO_LIN( seg, ptr ) = 0; /* clear previous bp */
}
/***********************************************************************
* SwitchStackBack (KERNEL.109)
*/
void WINAPI SwitchStackBack16( CONTEXT86 *context )
{
STACK16FRAME *oldFrame, *newFrame;
INSTANCEDATA *pData;
if (!(pData = (INSTANCEDATA *)GlobalLock16(SELECTOROF(NtCurrentTeb()->cur_stack))))
return;
if (!pData->old_ss_sp)
{
WARN_(task)("No previous SwitchStackTo\n" );
return;
}
TRACE_(task)("restoring stack %04x:%04x\n",
SELECTOROF(pData->old_ss_sp), OFFSETOF(pData->old_ss_sp) );
oldFrame = CURRENT_STACK16;
/* Pop bp from the previous stack */
BP_reg(context) = *(WORD *)PTR_SEG_TO_LIN(pData->old_ss_sp);
pData->old_ss_sp += sizeof(WORD);
/* Switch back to the old stack */
NtCurrentTeb()->cur_stack = pData->old_ss_sp - sizeof(STACK16FRAME);
SS_reg(context) = SELECTOROF(pData->old_ss_sp);
ESP_reg(context) = OFFSETOF(pData->old_ss_sp) - sizeof(DWORD); /*ret addr*/
pData->old_ss_sp = 0;
/* Build a stack frame for the return */
newFrame = CURRENT_STACK16;
newFrame->frame32 = oldFrame->frame32;
if (TRACE_ON(relay))
{
newFrame->entry_ip = oldFrame->entry_ip;
newFrame->entry_cs = oldFrame->entry_cs;
}
}
/***********************************************************************
* GetTaskQueueDS16 (KERNEL.118)
*/
void WINAPI GetTaskQueueDS16(void)
{
CURRENT_STACK16->ds = GlobalHandleToSel16( GetTaskQueue16(0) );
}
/***********************************************************************
* GetTaskQueueES16 (KERNEL.119)
*/
void WINAPI GetTaskQueueES16(void)
{
CURRENT_STACK16->es = GlobalHandleToSel16( GetTaskQueue16(0) );
}
/***********************************************************************
* GetCurrentTask (KERNEL.36)
*/
HTASK16 WINAPI GetCurrentTask(void)
{
return PROCESS_Current()->task;
}
DWORD WINAPI WIN16_GetCurrentTask(void)
{
/* This is the version used by relay code; the first task is */
/* returned in the high word of the result */
return MAKELONG( GetCurrentTask(), hFirstTask );
}
/***********************************************************************
* GetCurrentPDB (KERNEL.37)
*
* UNDOC: returns PSP of KERNEL in high word
*/
DWORD WINAPI GetCurrentPDB16(void)
{
TDB *pTask;
if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
return MAKELONG(pTask->hPDB, 0); /* FIXME */
}
/***********************************************************************
* GetCurPID16 (KERNEL.157)
*/
DWORD WINAPI GetCurPID16( DWORD unused )
{
return 0;
}
/***********************************************************************
* GetInstanceData (KERNEL.54)
*/
INT16 WINAPI GetInstanceData16( HINSTANCE16 instance, WORD buffer, INT16 len )
{
char *ptr = (char *)GlobalLock16( instance );
if (!ptr || !len) return 0;
if ((int)buffer + len >= 0x10000) len = 0x10000 - buffer;
memcpy( (char *)GlobalLock16(CURRENT_DS) + buffer, ptr + buffer, len );
return len;
}
/***********************************************************************
* GetExeVersion (KERNEL.105)
*/
WORD WINAPI GetExeVersion16(void)
{
TDB *pTask;
if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
return pTask->version;
}
/***********************************************************************
* SetErrorMode16 (KERNEL.107)
*/
UINT16 WINAPI SetErrorMode16( UINT16 mode )
{
TDB *pTask;
if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
pTask->error_mode = mode;
return SetErrorMode( mode );
}
/***********************************************************************
* GetDOSEnvironment (KERNEL.131)
*/
SEGPTR WINAPI GetDOSEnvironment16(void)
{
TDB *pTask;
if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
return PTR_SEG_OFF_TO_SEGPTR( pTask->pdb.environment, 0 );
}
/***********************************************************************
* GetNumTasks (KERNEL.152)
*/
UINT16 WINAPI GetNumTasks16(void)
{
return nTaskCount;
}
/***********************************************************************
* GetTaskDS (KERNEL.155)
*
* Note: this function apparently returns a DWORD with LOWORD == HIWORD.
* I don't think we need to bother with this.
*/
HINSTANCE16 WINAPI GetTaskDS16(void)
{
TDB *pTask;
if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
return pTask->hInstance;
}
/***********************************************************************
* GetDummyModuleHandleDS (KERNEL.602)
*/
WORD WINAPI GetDummyModuleHandleDS16(void)
{
TDB *pTask;
WORD selector;
if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
if (!(pTask->flags & TDBF_WIN32)) return 0;
selector = GlobalHandleToSel16( pTask->hModule );
CURRENT_DS = selector;
return selector;
}
/***********************************************************************
* IsTask (KERNEL.320)
*/
BOOL16 WINAPI IsTask16( HTASK16 hTask )
{
TDB *pTask;
if (!(pTask = (TDB *)GlobalLock16( hTask ))) return FALSE;
if (GlobalSize16( hTask ) < sizeof(TDB)) return FALSE;
return (pTask->magic == TDB_MAGIC);
}
/***********************************************************************
* IsWinOldApTask16 (KERNEL.158)
*/
BOOL16 WINAPI IsWinOldApTask16( HTASK16 hTask )
{
/* should return bit 0 of byte 0x48 in PSP */
return FALSE;
}
/***********************************************************************
* SetTaskSignalProc (KERNEL.38)
*
* Real 16-bit interface is provided by the THUNK_SetTaskSignalProc.
*/
FARPROC16 WINAPI SetTaskSignalProc( HTASK16 hTask, FARPROC16 proc )
{
TDB *pTask;
FARPROC16 oldProc;
if (!hTask) hTask = GetCurrentTask();
if (!(pTask = (TDB *)GlobalLock16( hTask ))) return NULL;
oldProc = (FARPROC16)pTask->userhandler;
pTask->userhandler = (USERSIGNALPROC)proc;
return oldProc;
}
/***********************************************************************
* SetSigHandler (KERNEL.140)
*/
WORD WINAPI SetSigHandler16( FARPROC16 newhandler, FARPROC16* oldhandler,
UINT16 *oldmode, UINT16 newmode, UINT16 flag )
{
FIXME_(task)("(%p,%p,%p,%d,%d), unimplemented.\n",
newhandler,oldhandler,oldmode,newmode,flag );
if (flag != 1) return 0;
if (!newmode) newhandler = NULL; /* Default handler */
if (newmode != 4)
{
TDB *pTask;
if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
if (oldmode) *oldmode = pTask->signal_flags;
pTask->signal_flags = newmode;
if (oldhandler) *oldhandler = pTask->sighandler;
pTask->sighandler = newhandler;
}
return 0;
}
/***********************************************************************
* GlobalNotify (KERNEL.154)
*
* Note that GlobalNotify does _not_ return the old NotifyProc
* -- contrary to LocalNotify !!
*/
VOID WINAPI GlobalNotify16( FARPROC16 proc )
{
TDB *pTask;
if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
pTask->discardhandler = proc;
}
/***********************************************************************
* GetExePtr (KERNEL.133)
*/
static inline HMODULE16 GetExePtrHelper( HANDLE16 handle, HTASK16 *hTask )
{
char *ptr;
HANDLE16 owner;
/* Check for module handle */
if (!(ptr = GlobalLock16( handle ))) return 0;
if (((NE_MODULE *)ptr)->magic == IMAGE_OS2_SIGNATURE) return handle;
/* Search for this handle inside all tasks */
*hTask = hFirstTask;
while (*hTask)
{
TDB *pTask = (TDB *)GlobalLock16( *hTask );
if ((*hTask == handle) ||
(pTask->hInstance == handle) ||
(pTask->hQueue == handle) ||
(pTask->hPDB == handle)) return pTask->hModule;
*hTask = pTask->hNext;
}
/* Check the owner for module handle */
owner = FarGetOwner16( handle );
if (!(ptr = GlobalLock16( owner ))) return 0;
if (((NE_MODULE *)ptr)->magic == IMAGE_OS2_SIGNATURE) return owner;
/* Search for the owner inside all tasks */
*hTask = hFirstTask;
while (*hTask)
{
TDB *pTask = (TDB *)GlobalLock16( *hTask );
if ((*hTask == owner) ||
(pTask->hInstance == owner) ||
(pTask->hQueue == owner) ||
(pTask->hPDB == owner)) return pTask->hModule;
*hTask = pTask->hNext;
}
return 0;
}
HMODULE16 WINAPI GetExePtr( HANDLE16 handle )
{
STACK16FRAME *frame;
HTASK16 hTask = 0;
HMODULE16 hModule = GetExePtrHelper( handle, &hTask );
if ((frame = CURRENT_STACK16) != NULL)
{
frame->ecx = hModule;
if (hTask) frame->es = hTask;
}
return hModule;
}
/***********************************************************************
* TaskFirst (TOOLHELP.63)
*/
BOOL16 WINAPI TaskFirst16( TASKENTRY *lpte )
{
lpte->hNext = hFirstTask;
return TaskNext16( lpte );
}
/***********************************************************************
* TaskNext (TOOLHELP.64)
*/
BOOL16 WINAPI TaskNext16( TASKENTRY *lpte )
{
TDB *pTask;
INSTANCEDATA *pInstData;
TRACE_(toolhelp)("(%p): task=%04x\n", lpte, lpte->hNext );
if (!lpte->hNext) return FALSE;
pTask = (TDB *)GlobalLock16( lpte->hNext );
if (!pTask || pTask->magic != TDB_MAGIC) return FALSE;
pInstData = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( pTask->hInstance, 0 );
lpte->hTask = lpte->hNext;
lpte->hTaskParent = pTask->hParent;
lpte->hInst = pTask->hInstance;
lpte->hModule = pTask->hModule;
lpte->wSS = SELECTOROF( pTask->teb->cur_stack );
lpte->wSP = OFFSETOF( pTask->teb->cur_stack );
lpte->wStackTop = pInstData->stacktop;
lpte->wStackMinimum = pInstData->stackmin;
lpte->wStackBottom = pInstData->stackbottom;
lpte->wcEvents = pTask->nEvents;
lpte->hQueue = pTask->hQueue;
strncpy( lpte->szModule, pTask->module_name, 8 );
lpte->szModule[8] = '\0';
lpte->wPSPOffset = 0x100; /*??*/
lpte->hNext = pTask->hNext;
return TRUE;
}
/***********************************************************************
* TaskFindHandle (TOOLHELP.65)
*/
BOOL16 WINAPI TaskFindHandle16( TASKENTRY *lpte, HTASK16 hTask )
{
lpte->hNext = hTask;
return TaskNext16( lpte );
}
/***********************************************************************
* GetAppCompatFlags16 (KERNEL.354)
*/
DWORD WINAPI GetAppCompatFlags16( HTASK16 hTask )
{
return GetAppCompatFlags( hTask );
}
/***********************************************************************
* GetAppCompatFlags32 (USER32.206)
*/
DWORD WINAPI GetAppCompatFlags( HTASK hTask )
{
TDB *pTask;
if (!hTask) hTask = GetCurrentTask();
if (!(pTask=(TDB *)GlobalLock16( (HTASK16)hTask ))) return 0;
if (GlobalSize16(hTask) < sizeof(TDB)) return 0;
return pTask->compat_flags;
}