Alexandre Julliard af0bae5873 Release 951003
Sun Oct  1 15:48:34 1995  Alexandre Julliard  <julliard@sunsite.unc>

	* [controls/menu.c]
	Fixed GetMenuString() for non-string items.

	* [debugger/*.c]
	First attempt to check validity of pointers before memory
	accesses. For now only segmented pointers are checked.

	* [debugger/dbg.y] [memory/ldt.c]
	Added possibility to dump only one segment with 'info segment'.

	* [include/bitmaps/ocr_*]
	Added all OEM cursors as XPM bitmaps.

	* [include/cursoricon.h] [objects/cursoricon.c]
	Rewrote all cursor and icon management to use the same memory
 	layout as Windows, and to factor common code between icons and
	cursors. Implemented icon directory lookup to find the best
	matching icon (i.e. the color one).
  	Implemented CopyCursor() and DumpIcon().

	* [loader/module.c]
	For disabled built-in modules, we now try to load the Windows DLL
	first, and if this fails we fall back to using the built-in module
	anyway.

	* [memory/global.c]
	Fixed GlobalHandle() to return the correct selector in the high
	word even if we are passed a handle in the first place.

	* [miscemu/instr.c]
	Take into account the size of the operand and of the stack segment
	when incrementing the stack pointer.
	Avoid referencing FS_reg and GS_reg on *BSD.

	* [objects/dib.c]
	All DIB functions now accept a BITMAPCOREHEADER format bitmap.
	Monochrome DIBs are created as monochrome bitmap iff they are
	black and white.

	* [objects/oembitmap.c]
	Added support for OEM cursors, changed OBM_LoadIcon to use the new
	icon memory layout.

	* [rc/sysres_Fr.rc]
	Added French [Fr] language support.

	* [win32/environment.c]
	Fixed GetCommandLineA() to use current PDB.

	* [windows/event.c] [windows/winpos.c]
	Simulate a mouse motion event upon SetWindowPos() to force the
	cursor to be set correctly.

Sat Sep 30 17:49:32  Cameron Heide  (heide@ee.ualberta.ca)

	* [win32/*]
        New Win32 kernel functions: GetACP, GetCPInfo,
 	GetEnvironmentVariableA, GetFileType, GetLastError, GetOEMCP,
 	GetStartupInfoA, GetTimeZoneInformation, SetEnvironmentVariable,
 	SetFilePointer, SetLastError, VirtualAlloc, VirtualFree,
 	WriteFile.  Completed implementations of GetCommandLineA.

	* [include/kernel32.h]
        New file.

	* [loader/main.c]
        Call initialization function for Win32 data (doesn't currently do
 	anything).

	* [misc/main.c]
	Implemented GetEnvironmentVariableA, SetEnvironmentVariableA.

Sat Sep 30 00:26:56 1995  Niels de Carpentier  <niels@cindy.et.tudelft.nl>

	* [windows/winpos.c][miscemu/emulate.c][loader/module.c]
	  [misc/commdlg.c]
	Misc. bug fixes

Fri Sep 29 16:16:13 1995  Jim Peterson <jspeter@birch.ee.vt.edu>

	* [*/*]
	For Winelib, explicit casts have been placed where warnings were
 	usually generated.
	printf formats which give the format for printing a handle as
 	"%04x" or something similar have been changed to use the NPFMT
 	macro defined in include/wintypes.h.  Some times, explicit casts
 	were also necessary.
     	Parameter, field, and variable declarations have been made more
 	exact, such as converting 'WORD wParam' to 'WPARAM wParam' or
 	'WORD hFont' to 'HFONT hFont'.
     	Any call of the form GetWindowWord(hwnd,GWW_HINSTANCE) has been
 	replaced with a call to WIN_GetWindowInstance(hwnd).

	* [controls/combo.c]
	Added WINELIB32 support in CLBoxGetCombo().

	* [include/dialog.h]
	Commented out the '#ifndef WINELIB' around the '#pragma pack(1)'.
	winelib needs the packing as well (e.g. when accessing resources
	like sysres_DIALOG_SHELL_ABOUT_MSGBOX).

	* [include/windows.h]
	Got rid of the F[a-k] macros, which were cluttering up the global
	namespace.

	* [include/windows.h] [windows/defwnd.c]
	Added Win32 messages WM_CTLCOLOR*.

	* [include/wintypes.h]
	Put in preprocessor '#define WINELIB32' if appropriate and changed
	the types of some typedefs (WPARAM, HANDLE) based on this.
	
	* [loader/module.c] [toolkit/miscstubs.c]
	Added #ifdef'd portion in LoadModule to handle loading a WINElib
	module (already loaded, just init values).  '#ifdef'ed out the
	definition for GetWndProcEntry16 and added a new version to
	toolkit/miscstubs.c.

	* [misc/shell.c]
	Adjusted the lengths of AppName and AppMisc from 512,512 to 128,906.
	Same amount of total storage, but much more reasonable.  Also, changed
	calls to strcpy() in ShellAbout() to calls to strncpy() instead.
	This was a difficult bug to track down, but the AppMisc field was
	being initialized with the contributers text, which was much larger
	than 512 characters.

	* [toolkit/atom.c]
	New file for atom-handling functions.  Copied from memory/atom.c and
	then heavily modified.  Right now, it's just a linked list of atoms.
	Consider it as a hash table with just one entry.  It's easily changed
	later.

	* [toolkit/heap.c]
	Commented out the heap functions with a "#ifdef WINELIB16" and put in
	a Win32 version (which is basically a modified copy).

	* [toolkit/sup.c] [toolkit/miscstubs.c]
	Moved the stuff I put in toolkit/sup.c into toolkit/miscstubs.c and
	added quite a few more stubs.

	* [toolkit/winmain.c]
	Rearranged startup code in _WinMain.  I think this will work.

	* [toolkit/Makefile.in]
	Added targets for 'hello' and 'hello2' in case anyone cares to try
	out the sample programs.

Wed Sep 27 23:13:43 1995  Anand Kumria <akumria@ozemail.com.au>
	
	* [miscemu/int2f.c] [miscemu/vxd.c] [if1632/winprocs.spec]
	First attempt at support for some VxDs. Comm, Shell and Pagefile.

Tue Sep 26 21:34:45 1995  Hans de Graaff  <graaff@twi72.twi.tudelft.nl>

	* [misc/dos_fs.c]
	DOS_SimplifyPath: Also remove "/./" from path. (Happens when
 	starting applications like 'wine ./excel.exe')

Sat Sep 23 23:32:40 1995  Morten Welinder  <terra@diku.dk>

	* [configure.in]
	Avoid relative path for wine.ini.

	* [rc/sysres_Da.rc]
	Support for Danish [Da] language.

	* [misc/main.c] [miscemu/cpu.c]
	Return the processor we're running on correctly.

	* [miscemu/int2f.c]
	Minor stuff in int 0x2f, function 0x16.

Sat Sep 23 1995 17:58:04  Marcus Meissner  <msmeissn@faui01.informatik.uni-erlangen.de>

	* [misc/shell.c] [misc/main.c]
	Implement saving and loading of the registry database (needed for
	OLE). Very experimental. Fixed ShellExecute().
	
	* [miscemu/int21.c]
	EEXIST is not a critical error condition for mkdir().

Fri Sep 22 01:33:34 1995  Alex Korobka  <alex@phm6.pharm.sunysb.edu>

	* [include/shell.h] [misc/shell.c]
	Implemented 4 drag/drop functions with documented functionality.

        * [multimedia/time.c]
        "Fixed" MMSysTimeCallback kludge so Excel5 loads up without crashing.

	* [*/*] 
        Added new files, more message definitions, structures, debug info,
 	etc.  Rewrote message logging functions to produce output similar
 	to WinSight.  Check out -debugmsg +message option.

	* [misc/file.c]
        Fixed GetDriveType return value.  

        * [windows/message.c] 
        Hooks are invoked in normal order.

        * [miscemu/*]
        Added some functions and interrupts.

        * [misc/shell.c]
        Implemented Drag... functions.

Thu Sep 21 23:50:12 1995  Jukka Iivonen <iivonen@cc.helsinki.fi>

	* [rc/sysres_Fi.rc] [rc/sysres.rc]
	First attempt at Finnish [Fi] language support.
1995-10-03 17:06:08 +00:00

1152 lines
31 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 "dos_fs.h"
#include "debugger.h"
#include "global.h"
#include "instance.h"
#include "miscemu.h"
#include "module.h"
#include "neexe.h"
#include "options.h"
#include "selectors.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
static HTASK hFirstTask = 0;
static HTASK hCurrentTask = 0;
static HTASK hTaskToKill = 0;
static HTASK hLockedTask = 0;
static WORD nTaskCount = 0;
static HANDLE hDOSEnvironment = 0;
/* TASK_Reschedule() 16-bit entry point */
static FARPROC TASK_RescheduleProc;
#ifdef WINELIB
#define TASK_SCHEDULE() TASK_Reschedule();
#else
#define TASK_SCHEDULE() CallTo16_word_(TASK_RescheduleProc,0)
#endif
static HANDLE TASK_CreateDOSEnvironment(void);
/***********************************************************************
* TASK_Init
*/
BOOL TASK_Init(void)
{
TASK_RescheduleProc = (FARPROC)GetWndProcEntry16( "TASK_Reschedule" );
if (!(hDOSEnvironment = TASK_CreateDOSEnvironment()))
fprintf( stderr, "Not enough memory for DOS Environment\n" );
return (hDOSEnvironment != 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;
HANDLE handle;
extern char **environ;
extern char WindowsDirectory[], SystemDirectory[];
/* DOS environment format:
* ASCIIZ string 1
* ASCIIZ string 2
* ...
* ASCIIZ string n
* ASCIIZ PATH=xxx
* ASCIIZ windir=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 */
initial_size = 5 + /* PATH= */
strlen(WindowsPath) + 1 + /* path value */
7 + /* windir= */
strlen(WindowsDirectory) + 1 + /* windir value */
1 + /* BYTE 0 at end */
sizeof(WORD) + /* WORD 1 */
strlen(SystemDirectory) + 1 + /* 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 (strncasecmp(*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 = GlobalAlloc( GMEM_FIXED, size ))) return 0;
p = (char *)GlobalLock( handle );
/* And fill it with the Unix environment */
for (e = environ, size = initial_size; *e; e++)
{
if (strncasecmp(*e, "path=", 5))
{
int len = strlen(*e) + 1;
if (size + len >= 32767) break;
strcpy( p, *e );
size += len;
p += len;
}
}
/* Now add the path and Windows directory */
strcpy( p, "PATH=" );
strcat( p, WindowsPath );
p += strlen(p) + 1;
strcpy( p, "windir=" );
strcat( p, WindowsDirectory );
p += strlen(p) + 1;
/* Now add the program name */
*p++ = '\0';
*(WORD *)p = 1;
p += sizeof(WORD);
strcpy( p, SystemDirectory );
strcat( p, "\\" );
strcat( p, program_name );
/* Display it */
p = (char *) GlobalLock( 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( HTASK hTask )
{
HTASK *prevTask;
TDB *pTask;
if (!(pTask = (TDB *)GlobalLock( hTask ))) return;
prevTask = &hFirstTask;
while (*prevTask)
{
TDB *prevTaskPtr = (TDB *)GlobalLock( *prevTask );
if (prevTaskPtr->priority >= pTask->priority) break;
prevTask = &prevTaskPtr->hNext;
}
pTask->hNext = *prevTask;
*prevTask = hTask;
nTaskCount++;
}
/***********************************************************************
* TASK_UnlinkTask
*/
static void TASK_UnlinkTask( HTASK hTask )
{
HTASK *prevTask;
TDB *pTask;
prevTask = &hFirstTask;
while (*prevTask && (*prevTask != hTask))
{
pTask = (TDB *)GlobalLock( *prevTask );
prevTask = &pTask->hNext;
}
if (*prevTask)
{
pTask = (TDB *)GlobalLock( *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 *)GlobalLock( 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( HTASK hTask )
{
TDB *pTask;
THUNKS *pThunk;
WORD sel, base;
if (!(pTask = (TDB *)GlobalLock( 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 *)GlobalLock( sel );
base = 0;
}
base += pThunk->free;
pThunk->free = *(WORD *)((BYTE *)pThunk + pThunk->free);
return MAKELONG( base, sel );
}
/***********************************************************************
* TASK_FreeThunk
*
* Free a MakeProcInstance() thunk.
*/
static BOOL TASK_FreeThunk( HTASK hTask, SEGPTR thunk )
{
TDB *pTask;
THUNKS *pThunk;
WORD sel, base;
if (!(pTask = (TDB *)GlobalLock( hTask ))) return 0;
sel = pTask->hCSAlias;
pThunk = &pTask->thunks;
base = (int)pThunk - (int)pTask;
while (sel && (sel != HIWORD(thunk)))
{
sel = pThunk->next;
pThunk = (THUNKS *)GlobalLock( 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.
*/
static void TASK_CallToStart(void)
{
int cs_reg, ds_reg, ip_reg;
TDB *pTask = (TDB *)GlobalLock( hCurrentTask );
NE_MODULE *pModule = (NE_MODULE *)GlobalLock( 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;
#ifndef WINELIB
/* JBP: I doubt a CallTo16_regs_ is possible in libwine.a, and IF1632 is not
* allowed.
*/
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_( (FARPROC)(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*/ );
#else
fprintf(stderr, "JBP: Ignoring main program: cs:ip=%04x:%04x ds=%04x ss:sp=%04x:%04x\n",
cs_reg, ip_reg, ds_reg,
pTask->ss, pTask->sp);
#endif
/* This should never return */
fprintf( stderr, "TASK_CallToStart: Main program returned!\n" );
TASK_KillCurrentTask( 1 );
}
/***********************************************************************
* TASK_CreateTask
*/
HTASK TASK_CreateTask( HMODULE hModule, HANDLE hInstance, HANDLE hPrevInstance,
HANDLE hEnvironment, char *cmdLine, WORD cmdShow )
{
HTASK hTask;
TDB *pTask;
HANDLE hParentEnv;
NE_MODULE *pModule;
SEGTABLEENTRY *pSegTable;
LPSTR name;
char filename[256];
#ifndef WINELIB32
char *stack16Top, *stack32Top;
STACK16FRAME *frame16;
STACK32FRAME *frame32;
extern DWORD CALL16_RetAddr_word;
#endif
if (!(pModule = (NE_MODULE *)GlobalLock( 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 *)GlobalLock( hTask );
/* Allocate the new environment block */
if (!(hParentEnv = hEnvironment))
{
TDB *pParent = (TDB *)GlobalLock( 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 = GlobalAlloc( GMEM_FIXED, GlobalSize( hParentEnv ) )))
{
GlobalFree( hTask );
return 0;
}
memcpy( GlobalLock( hEnvironment ), GlobalLock( hParentEnv ),
GlobalSize( 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;
pTask->magic = TDB_MAGIC;
pTask->nCmdShow = cmdShow;
strcpy( pTask->curdir, filename+2 );
/* 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) );
/* Fill the PDB */
pTask->pdb.int20 = 0x20cd;
pTask->pdb.dispatcher[0] = 0x9a; /* ljmp */
*(DWORD *)&pTask->pdb.dispatcher[1] = MODULE_GetEntryPoint( GetModuleHandle("KERNEL"), 102 ); /* KERNEL.102 is DOS3Call() */
#ifndef WINELIB
pTask->pdb.savedint22 = INT_GetHandler( 0x22 );
pTask->pdb.savedint23 = INT_GetHandler( 0x23 );
pTask->pdb.savedint24 = INT_GetHandler( 0x24 );
#endif
pTask->pdb.environment = hEnvironment;
strncpy( pTask->pdb.cmdLine + 1, cmdLine, 126 );
pTask->pdb.cmdLine[127] = '\0';
pTask->pdb.cmdLine[0] = strlen( pTask->pdb.cmdLine + 1 );
/* Get the compatibility flags */
pTask->compat_flags = GetProfileInt( name, "Compatibility", 0 );
/* Allocate a selector for the PDB */
pTask->hPDB = GLOBAL_CreateBlock( GMEM_FIXED, &pTask->pdb, sizeof(PDB),
hModule, FALSE, FALSE, FALSE, NULL );
/* 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 = MAKELONG( (int)&pTask->pdb.cmdLine - (int)&pTask->pdb,
pTask->hPDB );
/* Allocate the 32-bit stack */
#ifndef WINELIB
pTask->hStack32 = GLOBAL_Alloc( GMEM_FIXED, STACK32_SIZE, pTask->hPDB,
FALSE, FALSE, FALSE );
/* Create the 32-bit stack frame */
*(DWORD *)GlobalLock(pTask->hStack32) = 0xDEADBEEF;
stack32Top = (char*)GlobalLock(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;
frame32->retaddr = (DWORD)TASK_CallToStart;
frame32->codeselector = WINE_CODE_SELECTOR;
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; /*pTask->ss;*/
frame16->saved_sp = 0; /*pTask->sp;*/
frame16->ds = frame16->es = pTask->hInstance;
frame16->entry_point = 0;
frame16->ordinal_number = 24; /* WINPROCS.24 is TASK_Reschedule */
frame16->dll_id = 24; /* WINPROCS */
frame16->bp = 0;
frame16->ip = LOWORD( CALL16_RetAddr_word );
frame16->cs = HIWORD( CALL16_RetAddr_word );
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 */
if (Options.debug)
{
DBG_ADDR addr = { pSegTable[pModule->cs-1].selector, pModule->ip };
fprintf( stderr, "Task '%s': ", name );
DEBUG_AddBreakpoint( &addr );
}
#endif
/* Add the task to the linked list */
TASK_LinkTask( hTask );
dprintf_task( stddeb, "CreateTask: module='%s' cmdline='%s' task="NPFMT"\n",
name, cmdLine, hTask );
return hTask;
}
/***********************************************************************
* TASK_DeleteTask
*/
static void TASK_DeleteTask( HTASK hTask )
{
TDB *pTask;
if (!(pTask = (TDB *)GlobalLock( hTask ))) return;
/* Free the task module */
FreeModule( pTask->hModule );
/* Free all memory used by this task (including the 32-bit stack, */
/* the environment block and the thunk segments). */
GlobalFreeAll( pTask->hPDB );
/* Free the selector aliases */
GLOBAL_FreeBlock( pTask->hCSAlias );
GLOBAL_FreeBlock( pTask->hPDB );
/* Free the task structure itself */
GlobalFree( hTask );
}
/***********************************************************************
* 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( int exitCode )
{
if (hTaskToKill && (hTaskToKill != hCurrentTask))
{
/* If another task is already marked for destruction, */
/* we call kill it now, as we are in another context. */
TASK_DeleteTask( hTaskToKill );
}
if (nTaskCount <= 1)
{
dprintf_task( stddeb, "Killing the last task, exiting\n" );
exit(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 never return from Yield() */
}
/***********************************************************************
* 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;
}
/* If current task is locked, simply return */
if (hLockedTask) return;
/* Find a task to yield to */
pOldTask = (TDB *)GlobalLock( 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 *)GlobalLock( hTask )) || !pNewTask->nEvents)
hTask = 0;
}
if (!hTask)
{
hTask = hFirstTask;
while (hTask)
{
pNewTask = (TDB *)GlobalLock( hTask );
if (pNewTask->nEvents && (hTask != hCurrentTask)) break;
hTask = pNewTask->hNext;
}
}
/* If there's a task to kill, switch to any other task, */
/* even if it doesn't have events pending. */
if (!hTask && hTaskToKill) hTask = hFirstTask;
if (!hTask) return; /* Do nothing */
pNewTask = (TDB *)GlobalLock( hTask );
dprintf_task( stddeb, "Switching to task "NPFMT" (%.8s)\n",
hTask, pNewTask->module_name );
/* Save the stacks of the previous task (if any) */
#ifndef WINELIB /* FIXME: JBP: IF1632 not allowed in libwine.a */
if (pOldTask)
{
pOldTask->ss = IF1632_Saved16_ss;
pOldTask->sp = IF1632_Saved16_sp;
pOldTask->esp = IF1632_Saved32_esp;
}
else IF1632_Original32_esp = IF1632_Saved32_esp;
#endif
/* 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;
#ifndef WINELIB /* FIXME: JBP: IF1632 not allowed in libwine.a */
IF1632_Saved16_ss = pNewTask->ss;
IF1632_Saved16_sp = pNewTask->sp;
IF1632_Saved32_esp = pNewTask->esp;
IF1632_Stack32_base = WIN16_GlobalLock( pNewTask->hStack32 );
#endif
}
/***********************************************************************
* InitTask (KERNEL.91)
*/
void InitTask( struct sigcontext_struct context )
{
static int firstTask = 1;
TDB *pTask;
NE_MODULE *pModule;
SEGTABLEENTRY *pSegTable;
INSTANCEDATA *pinstance;
LONG stacklow, stackhi;
context.sc_eax = 0;
if (!(pTask = (TDB *)GlobalLock( hCurrentTask ))) return;
if (!(pModule = (NE_MODULE *)GlobalLock( pTask->hModule ))) return;
if (firstTask)
{
extern BOOL WIDGETS_Init(void);
extern BOOL WIN_CreateDesktopWindow(void);
/* Perform global initialisations that need a task context */
/* Initialize built-in window classes */
if (!WIDGETS_Init()) return;
/* Create desktop window */
if (!WIN_CreateDesktopWindow()) return;
firstTask = 0;
}
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
*/
context.sc_eax = 1;
context.sc_ebx = 0x81;
context.sc_ecx = pModule->stack_size;
context.sc_edx = pTask->nCmdShow;
context.sc_esi = (DWORD)pTask->hPrevInstance;
context.sc_edi = (DWORD)pTask->hInstance;
context.sc_es = (WORD)pTask->hPDB;
/* Initialize the local heap */
if ( pModule->heap_size )
{
LocalInit( pTask->hInstance, 0, pModule->heap_size );
}
/* 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 /* FIXME: JBP: IF1632 not allowed in libwine.a */
pinstance->stackmin = IF1632_Saved16_sp;
#endif
}
/***********************************************************************
* WaitEvent (KERNEL.30)
*/
BOOL WaitEvent( HTASK hTask )
{
TDB *pTask;
if (!hTask) hTask = hCurrentTask;
pTask = (TDB *)GlobalLock( hTask );
if (pTask->nEvents > 0)
{
pTask->nEvents--;
return FALSE;
}
TASK_SCHEDULE();
/* When we get back here, we have an event (or the task is the only one) */
if (pTask->nEvents > 0) pTask->nEvents--;
return TRUE;
}
/***********************************************************************
* PostEvent (KERNEL.31)
*/
void PostEvent( HTASK hTask )
{
TDB *pTask;
if (!hTask) hTask = hCurrentTask;
if (!(pTask = (TDB *)GlobalLock( hTask ))) return;
pTask->nEvents++;
}
/***********************************************************************
* SetPriority (KERNEL.32)
*/
void SetPriority( HTASK hTask, int delta )
{
TDB *pTask;
int newpriority;
if (!hTask) hTask = hCurrentTask;
if (!(pTask = (TDB *)GlobalLock( 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)
*/
WORD IsTaskLocked(void)
{
return hLockedTask;
}
/***********************************************************************
* OldYield (KERNEL.117)
*/
void OldYield(void)
{
TDB *pCurTask;
pCurTask = (TDB *)GlobalLock( 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;
if ((pCurTask = (TDB *)GlobalLock( hCurrentTask )) != NULL)
pCurTask->hYieldTo = hTask;
OldYield();
}
/***********************************************************************
* Yield (KERNEL.29)
*/
void Yield(void)
{
DirectedYield( 0 );
}
/***********************************************************************
* MakeProcInstance (KERNEL.51)
*/
FARPROC MakeProcInstance( FARPROC func, HANDLE hInstance )
{
BYTE *thunk;
SEGPTR thunkaddr;
thunkaddr = TASK_AllocThunk( hCurrentTask );
if (!thunkaddr) return (FARPROC)0;
thunk = PTR_SEG_TO_LIN( thunkaddr );
dprintf_task( stddeb, "MakeProcInstance(%08lx,"NPFMT"): got thunk %08lx\n",
(SEGPTR)func, hInstance, (SEGPTR)thunkaddr );
*thunk++ = 0xb8; /* movw instance, %ax */
#ifndef WINELIB
*thunk++ = (BYTE)(hInstance & 0xff);
*thunk++ = (BYTE)(hInstance >> 8);
#endif
*thunk++ = 0xea; /* ljmp func */
*(DWORD *)thunk = (DWORD)func;
return (FARPROC)thunkaddr;
}
/***********************************************************************
* FreeProcInstance (KERNEL.52)
*/
void FreeProcInstance( FARPROC func )
{
dprintf_task( stddeb, "FreeProcInstance(%08lx)\n", (SEGPTR)func );
TASK_FreeThunk( hCurrentTask, (SEGPTR)func );
}
/**********************************************************************
* GetCodeHandle (KERNEL.93)
*/
HANDLE GetCodeHandle( FARPROC proc )
{
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 = GlobalHandle( thunk[6] + (thunk[7] << 8) );
else
handle = GlobalHandle( HIWORD(proc) );
printf( "STUB: GetCodeHandle(%08lx) returning "NPFMT"\n",
(DWORD)proc, handle );
return handle;
}
/***********************************************************************
* SetTaskQueue (KERNEL.34)
*/
HGLOBAL SetTaskQueue( HANDLE hTask, HGLOBAL hQueue )
{
HGLOBAL hPrev;
TDB *pTask;
if (!hTask) hTask = hCurrentTask;
if (!(pTask = (TDB *)GlobalLock( hTask ))) return 0;
hPrev = pTask->hQueue;
pTask->hQueue = hQueue;
return hPrev;
}
/***********************************************************************
* GetTaskQueue (KERNEL.35)
*/
HGLOBAL GetTaskQueue( HANDLE hTask )
{
TDB *pTask;
if (!hTask) hTask = hCurrentTask;
if (!(pTask = (TDB *)GlobalLock( hTask ))) return 0;
return pTask->hQueue;
}
/***********************************************************************
* GetCurrentTask (KERNEL.36)
*/
HTASK GetCurrentTask(void)
{
/* Undocumented: first task is returned in high word */
#ifdef WINELIB32
return hCurrentTask;
#else
return MAKELONG( hCurrentTask, hFirstTask );
#endif
}
/***********************************************************************
* GetCurrentPDB (KERNEL.37)
*/
HANDLE GetCurrentPDB(void)
{
TDB *pTask;
if (!(pTask = (TDB *)GlobalLock( hCurrentTask ))) return 0;
return pTask->hPDB;
}
/***********************************************************************
* GetInstanceData (KERNEL.54)
*/
int GetInstanceData( HANDLE instance, WORD buffer, int len )
{
char *ptr = (char *)GlobalLock( instance );
if (!ptr || !len) return 0;
if ((int)buffer + len >= 0x10000) len = 0x10000 - buffer;
memcpy( ptr + buffer, (char *)GlobalLock( CURRENT_DS ) + buffer, len );
return len;
}
/***********************************************************************
* GetDOSEnvironment (KERNEL.131)
*/
SEGPTR GetDOSEnvironment(void)
{
TDB *pTask;
if (!(pTask = (TDB *)GlobalLock( hCurrentTask ))) return 0;
return (SEGPTR)WIN16_GlobalLock( pTask->pdb.environment );
}
/***********************************************************************
* GetNumTasks (KERNEL.152)
*/
WORD GetNumTasks(void)
{
return nTaskCount;
}
/***********************************************************************
* GetTaskDS (KERNEL.155)
*/
HINSTANCE GetTaskDS(void)
{
TDB *pTask;
if (!(pTask = (TDB *)GlobalLock( hCurrentTask ))) return 0;
return pTask->hInstance;
}
/***********************************************************************
* IsTask (KERNEL.320)
*/
BOOL IsTask( HTASK hTask )
{
TDB *pTask;
if (!(pTask = (TDB *)GlobalLock( hTask ))) return FALSE;
if (GlobalSize( hTask ) < sizeof(TDB)) return FALSE;
return (pTask->magic == TDB_MAGIC);
}
/***********************************************************************
* GetExePtr (KERNEL.133)
*/
HMODULE GetExePtr( HANDLE handle )
{
char *ptr;
HTASK hTask;
HANDLE owner;
/* Check for module handle */
if (!(ptr = GlobalLock( handle ))) return 0;
if (((NE_MODULE *)ptr)->magic == NE_SIGNATURE) return handle;
/* Check the owner for module handle */
#ifndef WINELIB
owner = FarGetOwner( handle );
#else
owner = NULL;
fprintf(stderr,"JBP: FarGetOwner() ignored.\n");
#endif
if (!(ptr = GlobalLock( 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 *)GlobalLock( 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)
*/
BOOL TaskFirst( TASKENTRY *lpte )
{
lpte->hNext = hFirstTask;
return TaskNext( lpte );
}
/***********************************************************************
* TaskNext (TOOLHELP.64)
*/
BOOL TaskNext( TASKENTRY *lpte )
{
TDB *pTask;
INSTANCEDATA *pInstData;
dprintf_toolhelp( stddeb, "TaskNext(%p): task="NPFMT"\n", lpte, lpte->hNext );
if (!lpte->hNext) return FALSE;
pTask = (TDB *)GlobalLock( 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)
*/
BOOL TaskFindHandle( TASKENTRY *lpte, HTASK hTask )
{
lpte->hNext = hTask;
return TaskNext( lpte );
}