
Sun Aug 4 18:18:48 1996 Alexandre Julliard <julliard@lrc.epfl.ch> * [controls/scroll.c] Attempted to implement undocumented scroll-bar messages. * [tools/build.c] [include/callback.h] [windows/winproc.c] CallTo16_* functions now get DS from the previous 16-bit stackframe. * [if1632/thunk.c] [include/callback.h] Added thunks to implement callbacks for the emulator. Now all the Enum* functions always take a real function pointer even in the emulator. * [loader/builtin.c] Removed binary search in BUILTIN_GetEntryPoint32(), as it didn't work with NULL entry points. Using linear search now. * [loader/module.c] Implemented IsSharedSelector(). * [loader/task.c] Changed SwitchStackTo/Back to use the instance data to store the previous stack instead of the TDB. Also copy the local variables onto the new stack. Added GetExeVersion() and SetTackSignalProc(). Implemented SetSigHandler(). Sat Aug 3 22:19:00 1996 Thomas Sandford <t.d.g.sandford@prds-grn.demon.co.uk> * [controls/menu.c] "Fixed" problem in deleting menus where win32 could get into an endless loop when it crashed. I think I've fixed the symptoms, not the original problem so it might be worth further investigation. * [debugger/registers.c] Added missing #ifdefs on FS_REG and GS_REG, needed for FreeBSD to compile wine properly. * [files/dos_fs.c] Made DOSFS_Hash and DOSFS_Match non-static so they can be called from win32/findfile.c (q.v.). * [if1632/gdi32.spec] Added SetMapMode (call existing function) * [if1632/kernel32.spec] Added FindFirstFileA and SetFileAttributesA. * [if1632/user32.spec] Added BringWindowToTop, CreatePopupMenu, GetKeyState, GetParent, IsDlgButtonChecked (call existing functions) and IsDialogMessageA, UnhookWindowsHookEx. * [win32/file.c] Added stub function SetFileAttributes32A. It's a stub because I can't really work out what this function should do with most attributes in a Unix environment. Anyone care to expand it? * [win32/findfile.c] New file. Initial stab at FindFirstFile. This doesn't work as specified yet, though a lot of the groundwork is there. I haven't time to work on this for the next month, so if anyone wants to pick it up and run with it please do. * [win32/memory.c] Removed malloc.h from includes (covered by stdlib.h include, and gets rid of a warning in FreeBSD). * [win32/newfns.c] UnhookWindowsHookEx32A stub added. * [win32/user32.c] Added implementation of IsDialogMessage32A. * [windows/dialog.c] IsDlgButtonChecked now calls SendDlgItemMessage32A instead of SendDlgItemMessage16. Sat Aug 3 18:00:00 1996 Alex Korobka <alex@phm30.pharm.sunysb.edu> * [windows/graphics.c] Removed rectangle checking (conflicted with nonstandard mapping modes). * [windows/dialog.c] Added check for child-style dialogs to DS_ABSALIGN coordinate conversion. * [objects/color.c] Cleaned up misc. bits Thu Aug 1 10:51:45 1996 Andrew Lewycky <plewycky@oise.utoronto.ca> * [windows/focus.c] [windows/event.c] [windows/win.c] [windows/defdlg.c] Fixes to focusing and activation. * [windows/defdlg.c] Properly(?) handle DM_GETDEFID. * [controls/combo.c] Handle CB_FINDSTRINGEXACT, CB_SETEDITSEL, CB_GETEDITSEL. CBCheckSize: Adjust edit position. Tue Jul 30 09:46:36 1996 Marcus Meissner <msmeissn@cip.informatik.uni-erlangen.de> * [files/file.c] [include/file.h] [loader/module.c] [loader/ne_image.c] Pass HFILEs instead of unix-fds to self-loader code. Mon Jul 29 21:48:25 1996 Albrecht Kleine <kleine@ak.sax.de> * [include/metafile.h] [objects/metafile.c] [objects/text.c] Implemented handling of some new metafile records (META_...) in PlayMetaFileRecord(), rewrite of 'case META_EXTTEXTOUT'. Added functions like MF_StretchBlt() for usage in metafiles. Minor bugfix in CopyMetafile(). * [objects/bitmap.c][objects/dib.c] Added check for metafile-DC in Bitmap and DIB functions: CreateCompatibleBitmap() etc.
1428 lines
39 KiB
C
1428 lines
39 KiB
C
/*
|
|
* Task functions
|
|
*
|
|
* Copyright 1995 Alexandre Julliard
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "windows.h"
|
|
#include "task.h"
|
|
#include "callback.h"
|
|
#include "directory.h"
|
|
#include "dos_fs.h"
|
|
#include "file.h"
|
|
#include "debugger.h"
|
|
#include "global.h"
|
|
#include "instance.h"
|
|
#include "message.h"
|
|
#include "miscemu.h"
|
|
#include "module.h"
|
|
#include "neexe.h"
|
|
#include "options.h"
|
|
#include "peexe.h"
|
|
#include "pe_image.h"
|
|
#include "queue.h"
|
|
#include "stackframe.h"
|
|
#include "toolhelp.h"
|
|
#include "stddebug.h"
|
|
#include "debug.h"
|
|
#include "dde_proc.h"
|
|
|
|
/* Min. number of thunks allocated when creating a new segment */
|
|
#define MIN_THUNKS 32
|
|
|
|
/* 32-bit stack size for each task */
|
|
/* Must not be greater than 64k, or MAKE_SEGPTR won't work */
|
|
#define STACK32_SIZE 0x10000
|
|
|
|
extern void USER_AppExit(HTASK, HINSTANCE, HQUEUE );
|
|
|
|
/* Saved 16-bit stack for current process (Win16 only) */
|
|
WORD IF1632_Saved16_ss = 0;
|
|
WORD IF1632_Saved16_sp = 0;
|
|
|
|
/* Saved 32-bit stack for current process (Win16 only) */
|
|
DWORD IF1632_Saved32_esp = 0;
|
|
SEGPTR IF1632_Stack32_base = 0;
|
|
|
|
/* Original Unix stack */
|
|
DWORD IF1632_Original32_esp;
|
|
|
|
static HTASK16 hFirstTask = 0;
|
|
static HTASK16 hCurrentTask = 0;
|
|
static HTASK16 hTaskToKill = 0;
|
|
static HTASK16 hLockedTask = 0;
|
|
static WORD nTaskCount = 0;
|
|
static HANDLE hDOSEnvironment = 0;
|
|
|
|
/* TASK_Reschedule() 16-bit entry point */
|
|
static FARPROC16 TASK_RescheduleProc;
|
|
|
|
#ifdef WINELIB
|
|
#define TASK_SCHEDULE() TASK_Reschedule()
|
|
#else
|
|
#define TASK_SCHEDULE() CallTo16_word_(TASK_RescheduleProc)
|
|
#endif
|
|
|
|
static HANDLE TASK_CreateDOSEnvironment(void);
|
|
|
|
/***********************************************************************
|
|
* TASK_Init
|
|
*/
|
|
BOOL32 TASK_Init(void)
|
|
{
|
|
TASK_RescheduleProc = MODULE_GetWndProcEntry16( "TASK_Reschedule" );
|
|
if (!(hDOSEnvironment = TASK_CreateDOSEnvironment()))
|
|
fprintf( stderr, "Not enough memory for DOS Environment\n" );
|
|
return (hDOSEnvironment != 0);
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* TASK_GetNextTask
|
|
*/
|
|
HTASK TASK_GetNextTask( HTASK hTask )
|
|
{
|
|
TDB* pTask = (TDB*)GlobalLock16(hTask);
|
|
|
|
if (pTask->hNext) return pTask->hNext;
|
|
return (hFirstTask != hTask) ? hFirstTask : 0;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* TASK_CreateDOSEnvironment
|
|
*
|
|
* Create the original DOS environment.
|
|
*/
|
|
static HANDLE TASK_CreateDOSEnvironment(void)
|
|
{
|
|
static const char program_name[] = "KRNL386.EXE";
|
|
char **e, *p;
|
|
int initial_size, size, i, winpathlen, sysdirlen;
|
|
HANDLE handle;
|
|
|
|
extern char **environ;
|
|
|
|
/* DOS environment format:
|
|
* ASCIIZ string 1
|
|
* ASCIIZ string 2
|
|
* ...
|
|
* ASCIIZ string n
|
|
* ASCIIZ PATH=xxx
|
|
* BYTE 0
|
|
* WORD 1
|
|
* ASCIIZ program name (e.g. C:\WINDOWS\SYSTEM\KRNL386.EXE)
|
|
*/
|
|
|
|
/* First compute the size of the fixed part of the environment */
|
|
|
|
for (i = winpathlen = 0; ; i++)
|
|
{
|
|
int len = DIR_GetDosPath( i, NULL, 0 );
|
|
if (!len) break;
|
|
winpathlen += len + 1;
|
|
}
|
|
if (!winpathlen) winpathlen = 1;
|
|
sysdirlen = GetSystemDirectory32A( NULL, 0 ) + 1;
|
|
initial_size = 5 + winpathlen + /* PATH=xxxx */
|
|
1 + /* BYTE 0 at end */
|
|
sizeof(WORD) + /* WORD 1 */
|
|
sysdirlen + /* program directory */
|
|
strlen(program_name) + 1; /* program name */
|
|
|
|
/* Compute the total size of the Unix environment (except path) */
|
|
|
|
for (e = environ, size = initial_size; *e; e++)
|
|
{
|
|
if (lstrncmpi32A(*e, "path=", 5))
|
|
{
|
|
int len = strlen(*e) + 1;
|
|
if (size + len >= 32767)
|
|
{
|
|
fprintf( stderr, "Warning: environment larger than 32k.\n" );
|
|
break;
|
|
}
|
|
size += len;
|
|
}
|
|
}
|
|
|
|
|
|
/* Now allocate the environment */
|
|
|
|
if (!(handle = GlobalAlloc16( GMEM_FIXED, size ))) return 0;
|
|
p = (char *)GlobalLock16( handle );
|
|
|
|
/* And fill it with the Unix environment */
|
|
|
|
for (e = environ, size = initial_size; *e; e++)
|
|
{
|
|
if (lstrncmpi32A(*e, "path=", 5))
|
|
{
|
|
int len = strlen(*e) + 1;
|
|
if (size + len >= 32767) break;
|
|
strcpy( p, *e );
|
|
size += len;
|
|
p += len;
|
|
}
|
|
}
|
|
|
|
/* Now add the path */
|
|
|
|
strcpy( p, "PATH=" );
|
|
for (i = 0, p += 5; ; i++)
|
|
{
|
|
if (!DIR_GetDosPath( i, p, winpathlen )) break;
|
|
p += strlen(p);
|
|
*p++ = ';';
|
|
}
|
|
if (p[-1] == ';') p[-1] = '\0';
|
|
else p++;
|
|
|
|
/* Now add the program name */
|
|
|
|
*p++ = '\0';
|
|
PUT_WORD( p, 1 );
|
|
p += sizeof(WORD);
|
|
GetSystemDirectory32A( p, sysdirlen );
|
|
strcat( p, "\\" );
|
|
strcat( p, program_name );
|
|
|
|
/* Display it */
|
|
|
|
p = (char *) GlobalLock16( handle );
|
|
dprintf_task(stddeb, "Master DOS environment at %p\n", p);
|
|
for (; *p; p += strlen(p) + 1) dprintf_task(stddeb, " %s\n", p);
|
|
dprintf_task( stddeb, "Progname: %s\n", p+3 );
|
|
|
|
return handle;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* 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( HGLOBAL 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().
|
|
*/
|
|
#ifndef WINELIB32
|
|
static SEGPTR TASK_AllocThunk( HTASK 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 );
|
|
}
|
|
#endif
|
|
|
|
|
|
/***********************************************************************
|
|
* TASK_FreeThunk
|
|
*
|
|
* Free a MakeProcInstance() thunk.
|
|
*/
|
|
#ifndef WINELIB32
|
|
static BOOL TASK_FreeThunk( HTASK 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;
|
|
}
|
|
#endif
|
|
|
|
|
|
/***********************************************************************
|
|
* 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.
|
|
*/
|
|
#ifndef WINELIB
|
|
static void TASK_CallToStart(void)
|
|
{
|
|
int cs_reg, ds_reg, ip_reg;
|
|
TDB *pTask = (TDB *)GlobalLock16( hCurrentTask );
|
|
NE_MODULE *pModule = MODULE_GetPtr( pTask->hModule );
|
|
SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
|
|
|
|
/* 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
|
|
*/
|
|
|
|
cs_reg = pSegTable[pModule->cs - 1].selector;
|
|
ip_reg = pModule->ip;
|
|
ds_reg = pSegTable[pModule->dgroup - 1].selector;
|
|
|
|
IF1632_Saved16_ss = pTask->ss;
|
|
IF1632_Saved16_sp = pTask->sp;
|
|
dprintf_task( stddeb, "Starting main program: cs:ip=%04x:%04x ds=%04x ss:sp=%04x:%04x\n",
|
|
cs_reg, ip_reg, ds_reg,
|
|
IF1632_Saved16_ss, IF1632_Saved16_sp);
|
|
|
|
CallTo16_regs_( (FARPROC16)(cs_reg << 16 | ip_reg), ds_reg,
|
|
pTask->hPDB /*es*/, 0 /*bp*/, 0 /*ax*/,
|
|
pModule->stack_size /*bx*/, pModule->heap_size /*cx*/,
|
|
0 /*dx*/, 0 /*si*/, ds_reg /*di*/ );
|
|
|
|
/* This should never return */
|
|
fprintf( stderr, "TASK_CallToStart: Main program returned!\n" );
|
|
TASK_KillCurrentTask( 1 );
|
|
}
|
|
#endif
|
|
|
|
|
|
/***********************************************************************
|
|
* TASK_CreateTask
|
|
*/
|
|
HTASK16 TASK_CreateTask( HMODULE16 hModule, HINSTANCE16 hInstance,
|
|
HINSTANCE16 hPrevInstance, HANDLE16 hEnvironment,
|
|
LPCSTR cmdLine, UINT16 cmdShow )
|
|
{
|
|
HTASK hTask;
|
|
TDB *pTask;
|
|
HANDLE hParentEnv;
|
|
NE_MODULE *pModule;
|
|
SEGTABLEENTRY *pSegTable;
|
|
LPSTR name;
|
|
char filename[256];
|
|
char *stack16Top, *stack32Top;
|
|
STACK16FRAME *frame16;
|
|
STACK32FRAME *frame32;
|
|
#ifndef WINELIB32
|
|
extern DWORD CALLTO16_RetAddr_word;
|
|
#endif
|
|
|
|
if (!(pModule = MODULE_GetPtr( hModule ))) return 0;
|
|
pSegTable = NE_SEG_TABLE( pModule );
|
|
|
|
/* Allocate the task structure */
|
|
|
|
hTask = GLOBAL_Alloc( GMEM_FIXED | GMEM_ZEROINIT, sizeof(TDB),
|
|
hModule, FALSE, FALSE, FALSE );
|
|
if (!hTask) return 0;
|
|
pTask = (TDB *)GlobalLock16( hTask );
|
|
|
|
/* Allocate the new environment block */
|
|
|
|
if (!(hParentEnv = hEnvironment))
|
|
{
|
|
TDB *pParent = (TDB *)GlobalLock16( hCurrentTask );
|
|
hParentEnv = pParent ? pParent->pdb.environment : hDOSEnvironment;
|
|
}
|
|
/* FIXME: do we really need to make a copy also when */
|
|
/* we don't use the parent environment? */
|
|
if (!(hEnvironment = GlobalAlloc16( GMEM_FIXED, GlobalSize16(hParentEnv))))
|
|
{
|
|
GlobalFree16( hTask );
|
|
return 0;
|
|
}
|
|
memcpy( GlobalLock16( hEnvironment ), GlobalLock16( hParentEnv ),
|
|
GlobalSize16( hParentEnv ) );
|
|
|
|
/* Get current directory */
|
|
|
|
GetModuleFileName( hModule, filename, sizeof(filename) );
|
|
name = strrchr(filename, '\\');
|
|
if (name) *(name+1) = 0;
|
|
|
|
/* Fill the task structure */
|
|
|
|
pTask->nEvents = 1; /* So the task can be started */
|
|
pTask->hSelf = hTask;
|
|
pTask->flags = 0;
|
|
pTask->version = pModule->expected_version;
|
|
pTask->hInstance = hInstance;
|
|
pTask->hPrevInstance = hPrevInstance;
|
|
pTask->hModule = hModule;
|
|
pTask->hParent = hCurrentTask;
|
|
pTask->curdrive = filename[0] - 'A' + 0x80;
|
|
strcpy( pTask->curdir, filename+2 );
|
|
pTask->magic = TDB_MAGIC;
|
|
pTask->nCmdShow = cmdShow;
|
|
|
|
/* Create the thunks block */
|
|
|
|
TASK_CreateThunks( hTask, (int)&pTask->thunks - (int)pTask, 7 );
|
|
|
|
/* Copy the module name */
|
|
|
|
name = MODULE_GetModuleName( hModule );
|
|
strncpy( pTask->module_name, name, sizeof(pTask->module_name) );
|
|
|
|
/* Allocate a selector for the PDB */
|
|
|
|
pTask->hPDB = GLOBAL_CreateBlock( GMEM_FIXED, &pTask->pdb, sizeof(PDB),
|
|
hModule, FALSE, FALSE, FALSE, NULL );
|
|
|
|
/* Fill the PDB */
|
|
|
|
pTask->pdb.int20 = 0x20cd;
|
|
pTask->pdb.dispatcher[0] = 0x9a; /* ljmp */
|
|
#ifndef WINELIB
|
|
*(FARPROC16 *)&pTask->pdb.dispatcher[1] = MODULE_GetEntryPoint( GetModuleHandle("KERNEL"), 102 ); /* KERNEL.102 is DOS3Call() */
|
|
pTask->pdb.savedint22 = INT_GetHandler( 0x22 );
|
|
pTask->pdb.savedint23 = INT_GetHandler( 0x23 );
|
|
pTask->pdb.savedint24 = INT_GetHandler( 0x24 );
|
|
#endif
|
|
pTask->pdb.fileHandlesPtr =
|
|
PTR_SEG_OFF_TO_SEGPTR( GlobalHandleToSel(pTask->hPDB),
|
|
(int)&((PDB *)0)->fileHandles );
|
|
memset( pTask->pdb.fileHandles, 0xff, sizeof(pTask->pdb.fileHandles) );
|
|
pTask->pdb.environment = hEnvironment;
|
|
pTask->pdb.nbFiles = 20;
|
|
lstrcpyn32A( pTask->pdb.cmdLine + 1, cmdLine, 127 );
|
|
pTask->pdb.cmdLine[0] = strlen( pTask->pdb.cmdLine + 1 );
|
|
|
|
/* Get the compatibility flags */
|
|
|
|
pTask->compat_flags = GetProfileInt( name, "Compatibility", 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 */
|
|
|
|
FarSetOwner( 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 );
|
|
|
|
/* Allocate the 32-bit stack */
|
|
|
|
pTask->hStack32 = GLOBAL_Alloc( GMEM_FIXED, STACK32_SIZE, pTask->hPDB,
|
|
FALSE, FALSE, FALSE );
|
|
|
|
/* Create the 32-bit stack frame */
|
|
|
|
*(DWORD *)GlobalLock16(pTask->hStack32) = 0xDEADBEEF;
|
|
stack32Top = (char*)GlobalLock16(pTask->hStack32) + STACK32_SIZE;
|
|
frame32 = (STACK32FRAME *)stack32Top - 1;
|
|
frame32->saved_esp = (DWORD)stack32Top;
|
|
frame32->edi = 0;
|
|
frame32->esi = 0;
|
|
frame32->edx = 0;
|
|
frame32->ecx = 0;
|
|
frame32->ebx = 0;
|
|
frame32->ebp = 0;
|
|
#ifndef WINELIB
|
|
frame32->retaddr = (DWORD)TASK_CallToStart;
|
|
frame32->codeselector = WINE_CODE_SELECTOR;
|
|
#endif
|
|
pTask->esp = (DWORD)frame32;
|
|
|
|
/* Create the 16-bit stack frame */
|
|
|
|
pTask->ss = hInstance;
|
|
pTask->sp = ((pModule->sp != 0) ? pModule->sp :
|
|
pSegTable[pModule->ss-1].minsize + pModule->stack_size) & ~1;
|
|
stack16Top = (char *)PTR_SEG_OFF_TO_LIN( pTask->ss, pTask->sp );
|
|
frame16 = (STACK16FRAME *)stack16Top - 1;
|
|
frame16->saved_ss = 0;
|
|
frame16->saved_sp = 0;
|
|
frame16->ds = frame16->es = pTask->hInstance;
|
|
frame16->entry_point = 0;
|
|
frame16->entry_ip = OFFSETOF(TASK_RescheduleProc) + 14;
|
|
frame16->entry_cs = SELECTOROF(TASK_RescheduleProc);
|
|
frame16->bp = 0;
|
|
#ifndef WINELIB
|
|
frame16->ip = LOWORD( CALLTO16_RetAddr_word );
|
|
frame16->cs = HIWORD( CALLTO16_RetAddr_word );
|
|
#endif /* WINELIB */
|
|
pTask->sp -= sizeof(STACK16FRAME);
|
|
|
|
/* If there's no 16-bit stack yet, use a part of the new task stack */
|
|
/* This is only needed to have a stack to switch from on the first */
|
|
/* call to DirectedYield(). */
|
|
|
|
if (!IF1632_Saved16_ss)
|
|
{
|
|
IF1632_Saved16_ss = pTask->ss;
|
|
IF1632_Saved16_sp = pTask->sp;
|
|
}
|
|
|
|
/* Add a breakpoint at the start of the task */
|
|
|
|
#ifndef WINELIB
|
|
if (Options.debug)
|
|
{
|
|
if (pModule->flags & NE_FFLAGS_WIN32)
|
|
{
|
|
DBG_ADDR addr = { 0, pModule->pe_module->load_addr +
|
|
pModule->pe_module->pe_header->opt_coff.AddressOfEntryPoint };
|
|
fprintf( stderr, "Win32 task '%s': ", name );
|
|
DEBUG_AddBreakpoint( &addr );
|
|
}
|
|
else
|
|
{
|
|
DBG_ADDR addr = { pSegTable[pModule->cs-1].selector, pModule->ip };
|
|
fprintf( stderr, "Win16 task '%s': ", name );
|
|
DEBUG_AddBreakpoint( &addr );
|
|
}
|
|
}
|
|
#endif /* WINELIB */
|
|
|
|
/* Add the task to the linked list */
|
|
|
|
TASK_LinkTask( hTask );
|
|
|
|
dprintf_task( stddeb, "CreateTask: module='%s' cmdline='%s' task=%04x\n",
|
|
name, cmdLine, hTask );
|
|
|
|
return hTask;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* TASK_DeleteTask
|
|
*/
|
|
static void TASK_DeleteTask( HTASK hTask )
|
|
{
|
|
TDB *pTask;
|
|
HANDLE hPDB;
|
|
|
|
if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
|
|
hPDB = pTask->hPDB;
|
|
|
|
/* Free the task module */
|
|
|
|
FreeModule16( pTask->hModule );
|
|
|
|
/* Close all open files of this task */
|
|
|
|
FILE_CloseAllFiles( pTask->hPDB );
|
|
|
|
/* Free the selector aliases */
|
|
|
|
GLOBAL_FreeBlock( pTask->hCSAlias );
|
|
GLOBAL_FreeBlock( pTask->hPDB );
|
|
|
|
/* 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). */
|
|
|
|
GlobalFreeAll( hPDB );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* TASK_KillCurrentTask
|
|
*
|
|
* Kill the currently running task. As it's not possible to kill the
|
|
* current task like this, it is simply marked for destruction, and will
|
|
* be killed when either TASK_Reschedule or this function is called again
|
|
* in the context of another task.
|
|
*/
|
|
void TASK_KillCurrentTask( INT16 exitCode )
|
|
{
|
|
extern void EXEC_ExitWindows( int retCode );
|
|
|
|
TDB* pTask = (TDB*) GlobalLock16( hCurrentTask );
|
|
if (!pTask) EXEC_ExitWindows(0); /* No current task yet */
|
|
|
|
/* Perform USER cleanup */
|
|
|
|
USER_AppExit( hCurrentTask, pTask->hInstance, pTask->hQueue );
|
|
|
|
if (hTaskToKill && (hTaskToKill != hCurrentTask))
|
|
{
|
|
/* If another task is already marked for destruction, */
|
|
/* we can kill it now, as we are in another context. */
|
|
TASK_DeleteTask( hTaskToKill );
|
|
}
|
|
|
|
if (nTaskCount <= 1)
|
|
{
|
|
dprintf_task( stddeb, "Killing the last task, exiting\n" );
|
|
EXEC_ExitWindows( 0 );
|
|
}
|
|
|
|
/* Remove the task from the list to be sure we never switch back to it */
|
|
TASK_UnlinkTask( hCurrentTask );
|
|
|
|
hTaskToKill = hCurrentTask;
|
|
hLockedTask = 0;
|
|
|
|
Yield();
|
|
/* We should never return from this Yield() */
|
|
|
|
fprintf(stderr,"It's alive! Alive!!!\n");
|
|
exit(1);
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* TASK_Reschedule
|
|
*
|
|
* This is where all the magic of task-switching happens!
|
|
*
|
|
* This function should only be called via the TASK_SCHEDULE() macro, to make
|
|
* sure that all the context is saved correctly.
|
|
*/
|
|
void TASK_Reschedule(void)
|
|
{
|
|
TDB *pOldTask = NULL, *pNewTask;
|
|
HTASK hTask = 0;
|
|
|
|
#ifdef CONFIG_IPC
|
|
dde_reschedule();
|
|
#endif
|
|
/* First check if there's a task to kill */
|
|
|
|
if (hTaskToKill && (hTaskToKill != hCurrentTask))
|
|
{
|
|
TASK_DeleteTask( hTaskToKill );
|
|
hTaskToKill = 0;
|
|
}
|
|
|
|
/* Flush any X events that happened in the meantime */
|
|
|
|
EVENT_WaitXEvent( FALSE );
|
|
|
|
/* Find a task to yield to */
|
|
|
|
pOldTask = (TDB *)GlobalLock16( hCurrentTask );
|
|
if (pOldTask && pOldTask->hYieldTo)
|
|
{
|
|
/* If a task is stored in hYieldTo of the current task (put there */
|
|
/* by DirectedYield), yield to it only if it has events pending. */
|
|
hTask = pOldTask->hYieldTo;
|
|
if (!(pNewTask = (TDB *)GlobalLock16( hTask )) || !pNewTask->nEvents)
|
|
hTask = 0;
|
|
}
|
|
|
|
while (!hTask)
|
|
{
|
|
/* Find a task that has an event pending */
|
|
|
|
hTask = hFirstTask;
|
|
while (hTask)
|
|
{
|
|
pNewTask = (TDB *)GlobalLock16( hTask );
|
|
if (pNewTask->nEvents) break;
|
|
hTask = pNewTask->hNext;
|
|
}
|
|
if (hLockedTask && (hTask != hLockedTask)) hTask = 0;
|
|
if (hTask) break;
|
|
|
|
/* No task found, wait for some events to come in */
|
|
|
|
EVENT_WaitXEvent( TRUE );
|
|
}
|
|
|
|
if (hTask == hCurrentTask) return; /* Nothing to do */
|
|
|
|
pNewTask = (TDB *)GlobalLock16( hTask );
|
|
dprintf_task( stddeb, "Switching to task %04x (%.8s)\n",
|
|
hTask, pNewTask->module_name );
|
|
|
|
/* Save the stacks of the previous task (if any) */
|
|
|
|
if (pOldTask)
|
|
{
|
|
pOldTask->ss = IF1632_Saved16_ss;
|
|
pOldTask->sp = IF1632_Saved16_sp;
|
|
pOldTask->esp = IF1632_Saved32_esp;
|
|
}
|
|
else IF1632_Original32_esp = IF1632_Saved32_esp;
|
|
|
|
/* Make the task the last in the linked list (round-robin scheduling) */
|
|
|
|
pNewTask->priority++;
|
|
TASK_UnlinkTask( hTask );
|
|
TASK_LinkTask( hTask );
|
|
pNewTask->priority--;
|
|
|
|
/* Switch to the new stack */
|
|
|
|
hCurrentTask = hTask;
|
|
IF1632_Saved16_ss = pNewTask->ss;
|
|
IF1632_Saved16_sp = pNewTask->sp;
|
|
IF1632_Saved32_esp = pNewTask->esp;
|
|
IF1632_Stack32_base = WIN16_GlobalLock16( pNewTask->hStack32 );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* InitTask (KERNEL.91)
|
|
*/
|
|
#ifdef WINELIB
|
|
void InitTask(void)
|
|
#else
|
|
void InitTask( SIGCONTEXT *context )
|
|
#endif
|
|
{
|
|
TDB *pTask;
|
|
NE_MODULE *pModule;
|
|
SEGTABLEENTRY *pSegTable;
|
|
INSTANCEDATA *pinstance;
|
|
LONG stacklow, stackhi;
|
|
|
|
#ifndef WINELIB
|
|
EAX_reg(context) = 0;
|
|
#endif
|
|
if (!(pTask = (TDB *)GlobalLock16( hCurrentTask ))) return;
|
|
if (!(pModule = MODULE_GetPtr( pTask->hModule ))) return;
|
|
|
|
#ifndef WINELIB
|
|
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
|
|
*/
|
|
EAX_reg(context) = 1;
|
|
EBX_reg(context) = 0x81;
|
|
ECX_reg(context) = pModule->stack_size;
|
|
EDX_reg(context) = pTask->nCmdShow;
|
|
ESI_reg(context) = (DWORD)pTask->hPrevInstance;
|
|
EDI_reg(context) = (DWORD)pTask->hInstance;
|
|
ES_reg (context) = (WORD)pTask->hPDB;
|
|
|
|
/* Initialize the local heap */
|
|
if ( pModule->heap_size )
|
|
{
|
|
LocalInit( pTask->hInstance, 0, pModule->heap_size );
|
|
}
|
|
#endif
|
|
|
|
/* Initialize the INSTANCEDATA structure */
|
|
pSegTable = NE_SEG_TABLE( pModule );
|
|
stacklow = pSegTable[pModule->ss - 1].minsize;
|
|
stackhi = stacklow + pModule->stack_size;
|
|
if (stackhi > 0xffff) stackhi = 0xffff;
|
|
pinstance = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN(CURRENT_DS, 0);
|
|
pinstance->stackbottom = stackhi; /* yup, that's right. Confused me too. */
|
|
pinstance->stacktop = stacklow;
|
|
#ifndef WINELIB
|
|
pinstance->stackmin = IF1632_Saved16_sp;
|
|
#endif
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* WaitEvent (KERNEL.30)
|
|
*/
|
|
BOOL WaitEvent( HTASK hTask )
|
|
{
|
|
TDB *pTask;
|
|
|
|
if (!hTask) hTask = hCurrentTask;
|
|
pTask = (TDB *)GlobalLock16( hTask );
|
|
if (pTask->nEvents > 0)
|
|
{
|
|
pTask->nEvents--;
|
|
return FALSE;
|
|
}
|
|
TASK_SCHEDULE();
|
|
/* When we get back here, we have an event */
|
|
if (pTask->nEvents > 0) pTask->nEvents--;
|
|
else fprintf( stderr, "WaitEvent: reschedule returned without event\n" );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* PostEvent (KERNEL.31)
|
|
*/
|
|
void PostEvent( HTASK hTask )
|
|
{
|
|
TDB *pTask;
|
|
|
|
if (!hTask) hTask = hCurrentTask;
|
|
if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
|
|
pTask->nEvents++;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* SetPriority (KERNEL.32)
|
|
*/
|
|
void SetPriority( HTASK hTask, int delta )
|
|
{
|
|
TDB *pTask;
|
|
int newpriority;
|
|
|
|
if (!hTask) hTask = hCurrentTask;
|
|
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)
|
|
*/
|
|
HTASK LockCurrentTask( BOOL bLock )
|
|
{
|
|
if (bLock) hLockedTask = hCurrentTask;
|
|
else hLockedTask = 0;
|
|
return hLockedTask;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* IsTaskLocked (KERNEL.122)
|
|
*/
|
|
HTASK IsTaskLocked(void)
|
|
{
|
|
return hLockedTask;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* OldYield (KERNEL.117)
|
|
*/
|
|
void OldYield(void)
|
|
{
|
|
TDB *pCurTask;
|
|
|
|
pCurTask = (TDB *)GlobalLock16( hCurrentTask );
|
|
if (pCurTask) pCurTask->nEvents++; /* Make sure we get back here */
|
|
TASK_SCHEDULE();
|
|
if (pCurTask) pCurTask->nEvents--;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* DirectedYield (KERNEL.150)
|
|
*/
|
|
void DirectedYield( HTASK hTask )
|
|
{
|
|
TDB *pCurTask = (TDB *)GlobalLock16( hCurrentTask );
|
|
pCurTask->hYieldTo = hTask;
|
|
OldYield();
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* UserYield (USER.332)
|
|
*/
|
|
void UserYield(void)
|
|
{
|
|
TDB *pCurTask = (TDB *)GlobalLock16( hCurrentTask );
|
|
MESSAGEQUEUE *queue = (MESSAGEQUEUE *)GlobalLock16( pCurTask->hQueue );
|
|
/* Handle sent messages */
|
|
if (queue && (queue->wakeBits & QS_SENDMESSAGE))
|
|
QUEUE_ReceiveMessage( queue );
|
|
|
|
OldYield();
|
|
|
|
queue = (MESSAGEQUEUE *)GlobalLock16( pCurTask->hQueue );
|
|
if (queue && (queue->wakeBits & QS_SENDMESSAGE))
|
|
QUEUE_ReceiveMessage( queue );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* Yield (KERNEL.29)
|
|
*/
|
|
void Yield(void)
|
|
{
|
|
TDB *pCurTask = (TDB *)GlobalLock16( hCurrentTask );
|
|
if (pCurTask) pCurTask->hYieldTo = 0;
|
|
if (pCurTask && pCurTask->hQueue) UserYield();
|
|
else OldYield();
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* MakeProcInstance16 (KERNEL.51)
|
|
*/
|
|
FARPROC16 MakeProcInstance16( FARPROC16 func, HANDLE16 hInstance )
|
|
{
|
|
#ifdef WINELIB
|
|
return func; /* func can be called directly in Winelib */
|
|
#else
|
|
BYTE *thunk;
|
|
SEGPTR thunkaddr;
|
|
|
|
thunkaddr = TASK_AllocThunk( hCurrentTask );
|
|
if (!thunkaddr) return (FARPROC16)0;
|
|
thunk = PTR_SEG_TO_LIN( thunkaddr );
|
|
|
|
dprintf_task( stddeb, "MakeProcInstance(%08lx,%04x): got thunk %08lx\n",
|
|
(DWORD)func, hInstance, (DWORD)thunkaddr );
|
|
|
|
*thunk++ = 0xb8; /* movw instance, %ax */
|
|
*thunk++ = (BYTE)(hInstance & 0xff);
|
|
*thunk++ = (BYTE)(hInstance >> 8);
|
|
*thunk++ = 0xea; /* ljmp func */
|
|
*(DWORD *)thunk = (DWORD)func;
|
|
return (FARPROC16)thunkaddr;
|
|
#endif
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* FreeProcInstance16 (KERNEL.52)
|
|
*/
|
|
void FreeProcInstance16( FARPROC16 func )
|
|
{
|
|
#ifndef WINELIB
|
|
dprintf_task( stddeb, "FreeProcInstance(%08lx)\n", (DWORD)func );
|
|
TASK_FreeThunk( hCurrentTask, (SEGPTR)func );
|
|
#endif
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* GetCodeHandle (KERNEL.93)
|
|
*/
|
|
HANDLE GetCodeHandle( FARPROC16 proc )
|
|
{
|
|
#ifndef WINELIB32
|
|
HANDLE 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;
|
|
#else
|
|
return (HANDLE)proc;
|
|
#endif
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* SetTaskQueue (KERNEL.34)
|
|
*/
|
|
HQUEUE SetTaskQueue( HANDLE hTask, HQUEUE hQueue )
|
|
{
|
|
HQUEUE hPrev;
|
|
TDB *pTask;
|
|
|
|
if (!hTask) hTask = hCurrentTask;
|
|
if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
|
|
|
|
hPrev = pTask->hQueue;
|
|
pTask->hQueue = hQueue;
|
|
|
|
TIMER_SwitchQueue( hPrev, hQueue );
|
|
|
|
return hPrev;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetTaskQueue (KERNEL.35)
|
|
*/
|
|
HQUEUE16 GetTaskQueue( HTASK16 hTask )
|
|
{
|
|
TDB *pTask;
|
|
|
|
if (!hTask) hTask = hCurrentTask;
|
|
if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
|
|
return pTask->hQueue;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* SwitchStackTo (KERNEL.108)
|
|
*/
|
|
void SwitchStackTo( WORD seg, WORD ptr, WORD top )
|
|
{
|
|
TDB *pTask;
|
|
STACK16FRAME *oldFrame, *newFrame;
|
|
INSTANCEDATA *pData;
|
|
UINT16 copySize;
|
|
|
|
if (!(pTask = (TDB *)GlobalLock16( hCurrentTask ))) return;
|
|
if (!(pData = (INSTANCEDATA *)GlobalLock16( seg ))) return;
|
|
dprintf_task( stddeb, "SwitchStackTo: old=%04x:%04x new=%04x:%04x\n",
|
|
IF1632_Saved16_ss, IF1632_Saved16_sp, seg, ptr );
|
|
|
|
/* Save the old stack */
|
|
|
|
oldFrame = CURRENT_STACK16;
|
|
pData->old_sp = IF1632_Saved16_sp;
|
|
pData->old_ss = IF1632_Saved16_ss;
|
|
pData->stacktop = top;
|
|
pData->stackmin = ptr;
|
|
pData->stackbottom = ptr;
|
|
|
|
/* Switch to the new stack */
|
|
|
|
IF1632_Saved16_ss = pTask->ss = seg;
|
|
IF1632_Saved16_sp = pTask->sp = ptr - sizeof(STACK16FRAME);
|
|
newFrame = CURRENT_STACK16;
|
|
|
|
/* Copy the stack frame and the local variables to the new stack */
|
|
|
|
copySize = oldFrame->bp - pData->old_sp;
|
|
memcpy( newFrame, oldFrame, MAX( copySize, sizeof(STACK16FRAME) ));
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* SwitchStackBack (KERNEL.109)
|
|
*
|
|
* Note: the function is declared as 'register' in the spec file in order
|
|
* to make sure all registers are preserved, but we don't use them in any
|
|
* way, so we don't need a SIGCONTEXT* argument.
|
|
*/
|
|
void SwitchStackBack(void)
|
|
{
|
|
TDB *pTask;
|
|
STACK16FRAME *oldFrame, *newFrame;
|
|
INSTANCEDATA *pData;
|
|
|
|
if (!(pTask = (TDB *)GlobalLock16( hCurrentTask ))) return;
|
|
if (!(pData = (INSTANCEDATA *)GlobalLock16( IF1632_Saved16_ss ))) return;
|
|
if (!pData->old_ss)
|
|
{
|
|
fprintf( stderr, "SwitchStackBack: no previous SwitchStackTo\n" );
|
|
return;
|
|
}
|
|
dprintf_task( stddeb, "SwitchStackBack: restoring stack %04x:%04x\n",
|
|
pData->old_ss, pData->old_sp );
|
|
|
|
oldFrame = CURRENT_STACK16;
|
|
|
|
/* Switch back to the old stack */
|
|
|
|
IF1632_Saved16_ss = pTask->ss = pData->old_ss;
|
|
IF1632_Saved16_sp = pTask->sp = pData->old_sp;
|
|
pData->old_ss = pData->old_sp = 0;
|
|
|
|
/* Build a stack frame for the return */
|
|
|
|
newFrame = CURRENT_STACK16;
|
|
newFrame->saved_ss = oldFrame->saved_ss;
|
|
newFrame->saved_sp = oldFrame->saved_sp;
|
|
newFrame->entry_ip = oldFrame->entry_ip;
|
|
newFrame->entry_cs = oldFrame->entry_cs;
|
|
newFrame->bp = oldFrame->bp;
|
|
newFrame->ip = oldFrame->ip;
|
|
newFrame->cs = oldFrame->cs;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetTaskQueueDS (KERNEL.118)
|
|
*/
|
|
#ifndef WINELIB
|
|
void GetTaskQueueDS( SIGCONTEXT *context )
|
|
{
|
|
DS_reg(context) = GlobalHandleToSel( GetTaskQueue(0) );
|
|
}
|
|
#endif /* WINELIB */
|
|
|
|
|
|
/***********************************************************************
|
|
* GetTaskQueueES (KERNEL.119)
|
|
*/
|
|
#ifndef WINELIB
|
|
void GetTaskQueueES( SIGCONTEXT *context )
|
|
{
|
|
ES_reg(context) = GlobalHandleToSel( GetTaskQueue(0) );
|
|
}
|
|
#endif /* WINELIB */
|
|
|
|
|
|
/***********************************************************************
|
|
* GetCurrentTask (KERNEL.36)
|
|
*/
|
|
HTASK16 GetCurrentTask(void)
|
|
{
|
|
return hCurrentTask;
|
|
}
|
|
|
|
DWORD 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( hCurrentTask, hFirstTask );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetCurrentPDB (KERNEL.37)
|
|
*/
|
|
HANDLE GetCurrentPDB(void)
|
|
{
|
|
TDB *pTask;
|
|
|
|
if (!(pTask = (TDB *)GlobalLock16( hCurrentTask ))) return 0;
|
|
return pTask->hPDB;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetInstanceData (KERNEL.54)
|
|
*/
|
|
int GetInstanceData( HANDLE instance, WORD buffer, int 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 GetExeVersion(void)
|
|
{
|
|
TDB *pTask;
|
|
|
|
if (!(pTask = (TDB *)GlobalLock16( hCurrentTask ))) return 0;
|
|
return pTask->version;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* SetErrorMode (KERNEL.107)
|
|
*/
|
|
UINT SetErrorMode( UINT mode )
|
|
{
|
|
TDB *pTask;
|
|
UINT oldMode;
|
|
|
|
if (!(pTask = (TDB *)GlobalLock16( hCurrentTask ))) return 0;
|
|
oldMode = pTask->error_mode;
|
|
pTask->error_mode = mode;
|
|
return oldMode;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetDOSEnvironment (KERNEL.131)
|
|
*/
|
|
SEGPTR GetDOSEnvironment(void)
|
|
{
|
|
TDB *pTask;
|
|
|
|
if (!(pTask = (TDB *)GlobalLock16( hCurrentTask ))) return 0;
|
|
return (SEGPTR)WIN16_GlobalLock16( pTask->pdb.environment );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetNumTasks (KERNEL.152)
|
|
*/
|
|
WORD GetNumTasks(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 GetTaskDS(void)
|
|
{
|
|
TDB *pTask;
|
|
|
|
if (!(pTask = (TDB *)GlobalLock16( hCurrentTask ))) return 0;
|
|
return pTask->hInstance;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* IsTask (KERNEL.320)
|
|
*/
|
|
BOOL IsTask( HTASK hTask )
|
|
{
|
|
TDB *pTask;
|
|
|
|
if (!(pTask = (TDB *)GlobalLock16( hTask ))) return FALSE;
|
|
if (GlobalSize16( hTask ) < sizeof(TDB)) return FALSE;
|
|
return (pTask->magic == TDB_MAGIC);
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* SetTaskSignalProc (KERNEL.38)
|
|
*/
|
|
FARPROC16 SetTaskSignalProc( HTASK16 hTask, FARPROC16 proc )
|
|
{
|
|
TDB *pTask;
|
|
FARPROC16 oldProc;
|
|
|
|
if (!hTask) hTask = hCurrentTask;
|
|
if (!(pTask = (TDB *)GlobalLock16( hTask ))) return NULL;
|
|
oldProc = pTask->userhandler;
|
|
pTask->userhandler = proc;
|
|
return oldProc;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* SetSigHandler (KERNEL.140)
|
|
*/
|
|
WORD SetSigHandler( FARPROC16 newhandler, FARPROC16* oldhandler,
|
|
UINT16 *oldmode, UINT16 newmode, UINT16 flag )
|
|
{
|
|
fprintf(stdnimp,"SetSigHandler(%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( hCurrentTask ))) return 0;
|
|
if (oldmode) *oldmode = pTask->signal_flags;
|
|
pTask->signal_flags = newmode;
|
|
if (oldhandler) *oldhandler = pTask->sighandler;
|
|
pTask->sighandler = newhandler;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetExePtr (KERNEL.133)
|
|
*/
|
|
HMODULE16 GetExePtr( HANDLE16 handle )
|
|
{
|
|
char *ptr;
|
|
HTASK16 hTask;
|
|
HANDLE16 owner;
|
|
|
|
/* Check for module handle */
|
|
|
|
if (!(ptr = GlobalLock16( handle ))) return 0;
|
|
if (((NE_MODULE *)ptr)->magic == NE_SIGNATURE) return handle;
|
|
|
|
/* Check the owner for module handle */
|
|
|
|
owner = FarGetOwner( handle );
|
|
if (!(ptr = GlobalLock16( owner ))) return 0;
|
|
if (((NE_MODULE *)ptr)->magic == NE_SIGNATURE) return owner;
|
|
|
|
/* Search for this handle and its owner 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;
|
|
if ((hTask == owner) ||
|
|
(pTask->hInstance == owner) ||
|
|
(pTask->hQueue == owner) ||
|
|
(pTask->hPDB == owner)) return pTask->hModule;
|
|
hTask = pTask->hNext;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* TaskFirst (TOOLHELP.63)
|
|
*/
|
|
BOOL16 TaskFirst( TASKENTRY *lpte )
|
|
{
|
|
lpte->hNext = hFirstTask;
|
|
return TaskNext( lpte );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* TaskNext (TOOLHELP.64)
|
|
*/
|
|
BOOL16 TaskNext( TASKENTRY *lpte )
|
|
{
|
|
TDB *pTask;
|
|
INSTANCEDATA *pInstData;
|
|
|
|
dprintf_toolhelp( stddeb, "TaskNext(%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 = pTask->ss;
|
|
lpte->wSP = pTask->sp;
|
|
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 TaskFindHandle( TASKENTRY *lpte, HTASK16 hTask )
|
|
{
|
|
lpte->hNext = hTask;
|
|
return TaskNext( lpte );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetAppCompatFlags (KERNEL.354) (USER32.205)
|
|
*/
|
|
DWORD GetAppCompatFlags( HTASK32 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;
|
|
}
|