Add Traditional Chinese Big5 and Simplified Chinese GBK mappings.
[wine] / dlls / kernel / task.c
1 /*
2  * Task functions
3  *
4  * Copyright 1995 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <stdarg.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <assert.h>
28 #ifdef HAVE_UNISTD_H
29 # include <unistd.h>
30 #endif
31
32 #include "windef.h"
33 #include "winbase.h"
34 #include "wingdi.h"
35 #include "winnt.h"
36 #include "wownt32.h"
37 #include "winuser.h"
38
39 #include "wine/winbase16.h"
40 #include "file.h"
41 #include "module.h"
42 #include "winternl.h"
43 #include "wine/server.h"
44 #include "stackframe.h"
45 #include "thread.h"
46 #include "toolhelp.h"
47 #include "kernel_private.h"
48
49 #include "wine/debug.h"
50
51 WINE_DEFAULT_DEBUG_CHANNEL(task);
52 WINE_DECLARE_DEBUG_CHANNEL(relay);
53 WINE_DECLARE_DEBUG_CHANNEL(toolhelp);
54
55 #include "pshpack1.h"
56
57 /* Segment containing MakeProcInstance() thunks */
58 typedef struct
59 {
60     WORD  next;       /* Selector of next segment */
61     WORD  magic;      /* Thunks signature */
62     WORD  unused;
63     WORD  free;       /* Head of the free list */
64     WORD  thunks[4];  /* Each thunk is 4 words long */
65 } THUNKS;
66
67 #include "poppack.h"
68
69 #define THUNK_MAGIC  ('P' | ('T' << 8))
70
71   /* Min. number of thunks allocated when creating a new segment */
72 #define MIN_THUNKS  32
73
74 #define TDB_MAGIC    ('T' | ('D' << 8))
75
76 static THHOOK DefaultThhook;
77 THHOOK *pThhook = &DefaultThhook;
78
79 #define hFirstTask   (pThhook->HeadTDB)
80 #define hLockedTask  (pThhook->LockTDB)
81
82 static UINT16 nTaskCount = 0;
83
84 static HTASK16 initial_task;
85
86 /***********************************************************************
87  *           TASK_InstallTHHook
88  */
89 void TASK_InstallTHHook( THHOOK *pNewThhook )
90 {
91      THHOOK *pOldThhook = pThhook;
92
93      pThhook = pNewThhook? pNewThhook : &DefaultThhook;
94
95      *pThhook = *pOldThhook;
96 }
97
98 /***********************************************************************
99  *           TASK_GetPtr
100  */
101 static TDB *TASK_GetPtr( HTASK16 hTask )
102 {
103     return GlobalLock16( hTask );
104 }
105
106
107 /***********************************************************************
108  *           TASK_GetCurrent
109  */
110 TDB *TASK_GetCurrent(void)
111 {
112     return TASK_GetPtr( GetCurrentTask() );
113 }
114
115
116 /***********************************************************************
117  *           TASK_LinkTask
118  */
119 static void TASK_LinkTask( HTASK16 hTask )
120 {
121     HTASK16 *prevTask;
122     TDB *pTask;
123
124     if (!(pTask = TASK_GetPtr( hTask ))) return;
125     prevTask = &hFirstTask;
126     while (*prevTask)
127     {
128         TDB *prevTaskPtr = TASK_GetPtr( *prevTask );
129         if (prevTaskPtr->priority >= pTask->priority) break;
130         prevTask = &prevTaskPtr->hNext;
131     }
132     pTask->hNext = *prevTask;
133     *prevTask = hTask;
134     nTaskCount++;
135 }
136
137
138 /***********************************************************************
139  *           TASK_UnlinkTask
140  */
141 static void TASK_UnlinkTask( HTASK16 hTask )
142 {
143     HTASK16 *prevTask;
144     TDB *pTask;
145
146     prevTask = &hFirstTask;
147     while (*prevTask && (*prevTask != hTask))
148     {
149         pTask = TASK_GetPtr( *prevTask );
150         prevTask = &pTask->hNext;
151     }
152     if (*prevTask)
153     {
154         pTask = TASK_GetPtr( *prevTask );
155         *prevTask = pTask->hNext;
156         pTask->hNext = 0;
157         nTaskCount--;
158     }
159 }
160
161
162 /***********************************************************************
163  *           TASK_CreateThunks
164  *
165  * Create a thunk free-list in segment 'handle', starting from offset 'offset'
166  * and containing 'count' entries.
167  */
168 static void TASK_CreateThunks( HGLOBAL16 handle, WORD offset, WORD count )
169 {
170     int i;
171     WORD free;
172     THUNKS *pThunk;
173
174     pThunk = (THUNKS *)((BYTE *)GlobalLock16( handle ) + offset);
175     pThunk->next = 0;
176     pThunk->magic = THUNK_MAGIC;
177     pThunk->free = (int)&pThunk->thunks - (int)pThunk;
178     free = pThunk->free;
179     for (i = 0; i < count-1; i++)
180     {
181         free += 8;  /* Offset of next thunk */
182         pThunk->thunks[4*i] = free;
183     }
184     pThunk->thunks[4*i] = 0;  /* Last thunk */
185 }
186
187
188 /***********************************************************************
189  *           TASK_AllocThunk
190  *
191  * Allocate a thunk for MakeProcInstance().
192  */
193 static SEGPTR TASK_AllocThunk(void)
194 {
195     TDB *pTask;
196     THUNKS *pThunk;
197     WORD sel, base;
198
199     if (!(pTask = TASK_GetCurrent())) return 0;
200     sel = pTask->hCSAlias;
201     pThunk = (THUNKS *)&pTask->thunks;
202     base = (char *)pThunk - (char *)pTask;
203     while (!pThunk->free)
204     {
205         sel = pThunk->next;
206         if (!sel)  /* Allocate a new segment */
207         {
208             sel = GLOBAL_Alloc( GMEM_FIXED, sizeof(THUNKS) + (MIN_THUNKS-1)*8,
209                                 pTask->hPDB, WINE_LDT_FLAGS_CODE );
210             if (!sel) return (SEGPTR)0;
211             TASK_CreateThunks( sel, 0, MIN_THUNKS );
212             pThunk->next = sel;
213         }
214         pThunk = (THUNKS *)GlobalLock16( sel );
215         base = 0;
216     }
217     base += pThunk->free;
218     pThunk->free = *(WORD *)((BYTE *)pThunk + pThunk->free);
219     return MAKESEGPTR( sel, base );
220 }
221
222
223 /***********************************************************************
224  *           TASK_FreeThunk
225  *
226  * Free a MakeProcInstance() thunk.
227  */
228 static BOOL TASK_FreeThunk( SEGPTR thunk )
229 {
230     TDB *pTask;
231     THUNKS *pThunk;
232     WORD sel, base;
233
234     if (!(pTask = TASK_GetCurrent())) return 0;
235     sel = pTask->hCSAlias;
236     pThunk = (THUNKS *)&pTask->thunks;
237     base = (char *)pThunk - (char *)pTask;
238     while (sel && (sel != HIWORD(thunk)))
239     {
240         sel = pThunk->next;
241         pThunk = (THUNKS *)GlobalLock16( sel );
242         base = 0;
243     }
244     if (!sel) return FALSE;
245     *(WORD *)((BYTE *)pThunk + LOWORD(thunk) - base) = pThunk->free;
246     pThunk->free = LOWORD(thunk) - base;
247     return TRUE;
248 }
249
250
251 /***********************************************************************
252  *           TASK_Create
253  *
254  * NOTE: This routine might be called by a Win32 thread. Thus, we need
255  *       to be careful to protect global data structures. We do this
256  *       by entering the Win16Lock while linking the task into the
257  *       global task list.
258  */
259 static TDB *TASK_Create( NE_MODULE *pModule, UINT16 cmdShow, TEB *teb, LPCSTR cmdline, BYTE len )
260 {
261     HTASK16 hTask;
262     TDB *pTask;
263     FARPROC16 proc;
264     HMODULE16 hModule = pModule ? pModule->self : 0;
265
266       /* Allocate the task structure */
267
268     hTask = GlobalAlloc16( GMEM_FIXED | GMEM_ZEROINIT, sizeof(TDB) );
269     if (!hTask) return NULL;
270     pTask = TASK_GetPtr( hTask );
271     FarSetOwner16( hTask, hModule );
272
273     /* Fill the task structure */
274
275     pTask->hSelf = hTask;
276
277     if (teb && teb->tibflags & TEBF_WIN32)
278     {
279         pTask->flags        |= TDBF_WIN32;
280         pTask->hInstance     = hModule;
281         pTask->hPrevInstance = 0;
282         /* NOTE: for 16-bit tasks, the instance handles are updated later on
283            in NE_InitProcess */
284     }
285
286     pTask->version       = pModule ? pModule->expected_version : 0x0400;
287     pTask->hModule       = hModule;
288     pTask->hParent       = GetCurrentTask();
289     pTask->magic         = TDB_MAGIC;
290     pTask->nCmdShow      = cmdShow;
291     pTask->teb           = teb;
292     pTask->curdrive      = DRIVE_GetCurrentDrive() | 0x80;
293     strcpy( pTask->curdir, "\\" );
294     WideCharToMultiByte(CP_ACP, 0, DRIVE_GetDosCwd(DRIVE_GetCurrentDrive()), -1,
295                         pTask->curdir + 1, sizeof(pTask->curdir) - 1, NULL, NULL);
296     pTask->curdir[sizeof(pTask->curdir) - 1] = 0; /* ensure 0 termination */
297
298       /* Create the thunks block */
299
300     TASK_CreateThunks( hTask, (char *)&pTask->thunks - (char *)pTask, 7 );
301
302       /* Copy the module name */
303
304     if (hModule)
305     {
306         char name[10];
307         GetModuleName16( hModule, name, sizeof(name) );
308         strncpy( pTask->module_name, name, sizeof(pTask->module_name) );
309         pTask->compat_flags = GetProfileIntA( "Compatibility", name, 0 );
310     }
311
312       /* Allocate a selector for the PDB */
313
314     pTask->hPDB = GLOBAL_CreateBlock( GMEM_FIXED, &pTask->pdb, sizeof(PDB16),
315                                       hModule, WINE_LDT_FLAGS_DATA );
316
317       /* Fill the PDB */
318
319     pTask->pdb.int20 = 0x20cd;
320     pTask->pdb.dispatcher[0] = 0x9a;  /* ljmp */
321     proc = GetProcAddress16( GetModuleHandle16("KERNEL"), "DOS3Call" );
322     memcpy( &pTask->pdb.dispatcher[1], &proc, sizeof(proc) );
323     pTask->pdb.savedint22 = 0;
324     pTask->pdb.savedint23 = 0;
325     pTask->pdb.savedint24 = 0;
326     pTask->pdb.fileHandlesPtr =
327         MAKESEGPTR( GlobalHandleToSel16(pTask->hPDB), (int)&((PDB16 *)0)->fileHandles );
328     pTask->pdb.hFileHandles = 0;
329     memset( pTask->pdb.fileHandles, 0xff, sizeof(pTask->pdb.fileHandles) );
330     /* FIXME: should we make a copy of the environment? */
331     pTask->pdb.environment    = SELECTOROF(GetDOSEnvironment16());
332     pTask->pdb.nbFiles        = 20;
333
334     /* Fill the command line */
335
336     if (!cmdline)
337     {
338         cmdline = GetCommandLineA();
339         /* remove the first word (program name) */
340         if (*cmdline == '"')
341             if (!(cmdline = strchr( cmdline+1, '"' ))) cmdline = GetCommandLineA();
342         while (*cmdline && (*cmdline != ' ') && (*cmdline != '\t')) cmdline++;
343         while ((*cmdline == ' ') || (*cmdline == '\t')) cmdline++;
344         len = strlen(cmdline);
345     }
346     if (len >= sizeof(pTask->pdb.cmdLine)) len = sizeof(pTask->pdb.cmdLine)-1;
347     pTask->pdb.cmdLine[0] = len;
348     memcpy( pTask->pdb.cmdLine + 1, cmdline, len );
349     /* pTask->pdb.cmdLine[len+1] = 0; */
350
351     TRACE("cmdline='%.*s' task=%04x\n", len, cmdline, hTask );
352
353       /* Allocate a code segment alias for the TDB */
354
355     pTask->hCSAlias = GLOBAL_CreateBlock( GMEM_FIXED, (void *)pTask,
356                                           sizeof(TDB), pTask->hPDB, WINE_LDT_FLAGS_CODE );
357
358       /* Default DTA overwrites command line */
359
360     pTask->dta = MAKESEGPTR( pTask->hPDB, (int)&pTask->pdb.cmdLine - (int)&pTask->pdb );
361
362     /* Create scheduler event for 16-bit tasks */
363
364     if ( !(pTask->flags & TDBF_WIN32) )
365         NtCreateEvent( &pTask->hEvent, EVENT_ALL_ACCESS, NULL, TRUE, FALSE );
366
367     /* Enter task handle into thread */
368
369     if (teb) teb->htask16 = hTask;
370     if (!initial_task) initial_task = hTask;
371
372     return pTask;
373 }
374
375
376 /***********************************************************************
377  *           TASK_DeleteTask
378  */
379 static void TASK_DeleteTask( HTASK16 hTask )
380 {
381     TDB *pTask;
382     HGLOBAL16 hPDB;
383
384     if (!(pTask = TASK_GetPtr( hTask ))) return;
385     hPDB = pTask->hPDB;
386
387     pTask->magic = 0xdead; /* invalidate signature */
388
389     /* Free the selector aliases */
390
391     GLOBAL_FreeBlock( pTask->hCSAlias );
392     GLOBAL_FreeBlock( pTask->hPDB );
393
394     /* Free the task module */
395
396     FreeModule16( pTask->hModule );
397
398     /* Free the task structure itself */
399
400     GlobalFree16( hTask );
401
402     /* Free all memory used by this task (including the 32-bit stack, */
403     /* the environment block and the thunk segments). */
404
405     GlobalFreeAll16( hPDB );
406 }
407
408
409 /***********************************************************************
410  *           TASK_CreateMainTask
411  *
412  * Create a task for the main (32-bit) process.
413  */
414 void TASK_CreateMainTask(void)
415 {
416     TDB *pTask;
417     STARTUPINFOA startup_info;
418     UINT cmdShow = 1; /* SW_SHOWNORMAL but we don't want to include winuser.h here */
419
420     GetStartupInfoA( &startup_info );
421     if (startup_info.dwFlags & STARTF_USESHOWWINDOW) cmdShow = startup_info.wShowWindow;
422     pTask = TASK_Create( NULL, cmdShow, NtCurrentTeb(), NULL, 0 );
423     if (!pTask)
424     {
425         ERR("could not create task for main process\n");
426         ExitProcess(1);
427     }
428
429     /* Add the task to the linked list */
430     /* (no need to get the win16 lock, we are the only thread at this point) */
431     TASK_LinkTask( pTask->hSelf );
432 }
433
434
435 /* startup routine for a new 16-bit thread */
436 static DWORD CALLBACK task_start( TDB *pTask )
437 {
438     DWORD ret;
439
440     NtCurrentTeb()->tibflags &= ~TEBF_WIN32;
441     NtCurrentTeb()->htask16 = pTask->hSelf;
442
443     _EnterWin16Lock();
444     TASK_LinkTask( pTask->hSelf );
445     pTask->teb = NtCurrentTeb();
446     ret = NE_StartTask();
447     _LeaveWin16Lock();
448     return ret;
449 }
450
451
452 /***********************************************************************
453  *           TASK_SpawnTask
454  *
455  * Spawn a new 16-bit task.
456  */
457 HTASK16 TASK_SpawnTask( NE_MODULE *pModule, WORD cmdShow,
458                         LPCSTR cmdline, BYTE len, HANDLE *hThread )
459 {
460     TDB *pTask;
461
462     if (!(pTask = TASK_Create( pModule, cmdShow, NULL, cmdline, len ))) return 0;
463     if (!(*hThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)task_start, pTask, 0, NULL )))
464     {
465         TASK_DeleteTask( pTask->hSelf );
466         return 0;
467     }
468     return pTask->hSelf;
469 }
470
471
472 /***********************************************************************
473  *           TASK_GetTaskFromThread
474  */
475 HTASK16 TASK_GetTaskFromThread( DWORD thread )
476 {
477     TDB *p = TASK_GetPtr( hFirstTask );
478     while (p)
479     {
480         if (p->teb->ClientId.UniqueThread == (HANDLE)thread) return p->hSelf;
481         p = TASK_GetPtr( p->hNext );
482     }
483     return 0;
484 }
485
486
487 /***********************************************************************
488  *           TASK_CallTaskSignalProc
489  */
490 static void TASK_CallTaskSignalProc( UINT16 uCode, HANDLE16 hTaskOrModule )
491 {
492     WORD args[5];
493     TDB *pTask = TASK_GetCurrent();
494
495     if ( !pTask || !pTask->userhandler ) return;
496
497     args[4] = hTaskOrModule;
498     args[3] = uCode;
499     args[2] = 0;
500     args[1] = pTask->hInstance;
501     args[0] = pTask->hQueue;
502     WOWCallback16Ex( (DWORD)pTask->userhandler, WCB16_PASCAL, sizeof(args), args, NULL );
503 }
504
505
506 /***********************************************************************
507  *           TASK_ExitTask
508  */
509 void TASK_ExitTask(void)
510 {
511     TDB *pTask;
512     DWORD lockCount;
513
514     /* Enter the Win16Lock to protect global data structures */
515     _EnterWin16Lock();
516
517     pTask = TASK_GetCurrent();
518     if ( !pTask )
519     {
520         _LeaveWin16Lock();
521         return;
522     }
523
524     TRACE("Killing task %04x\n", pTask->hSelf );
525
526     /* Perform USER cleanup */
527
528     TASK_CallTaskSignalProc( USIG16_TERMINATION, pTask->hSelf );
529
530     /* Remove the task from the list to be sure we never switch back to it */
531     TASK_UnlinkTask( pTask->hSelf );
532
533     if (!nTaskCount || (nTaskCount == 1 && hFirstTask == initial_task))
534     {
535         TRACE("this is the last task, exiting\n" );
536         ExitKernel16();
537     }
538
539     pTask->nEvents = 0;
540
541     if ( hLockedTask == pTask->hSelf )
542         hLockedTask = 0;
543
544     TASK_DeleteTask( pTask->hSelf );
545
546     /* ... and completely release the Win16Lock, just in case. */
547     ReleaseThunkLock( &lockCount );
548 }
549
550
551 /***********************************************************************
552  *           ExitKernel (KERNEL.2)
553  *
554  * Clean-up everything and exit the Wine process.
555  */
556 void WINAPI ExitKernel16(void)
557 {
558     WriteOutProfiles16();
559     TerminateProcess( GetCurrentProcess(), 0 );
560 }
561
562
563 /***********************************************************************
564  *           InitTask  (KERNEL.91)
565  *
566  * Called by the application startup code.
567  */
568 void WINAPI InitTask16( CONTEXT86 *context )
569 {
570     TDB *pTask;
571     INSTANCEDATA *pinstance;
572     SEGPTR ptr;
573
574     context->Eax = 0;
575     if (!(pTask = TASK_GetCurrent())) return;
576
577     /* Note: we need to trust that BX/CX contain the stack/heap sizes,
578        as some apps, notably Visual Basic apps, *modify* the heap/stack
579        size of the instance data segment before calling InitTask() */
580
581     /* Initialize the INSTANCEDATA structure */
582     pinstance = MapSL( MAKESEGPTR(CURRENT_DS, 0) );
583     pinstance->stackmin    = OFFSETOF( NtCurrentTeb()->cur_stack ) + sizeof( STACK16FRAME );
584     pinstance->stackbottom = pinstance->stackmin; /* yup, that's right. Confused me too. */
585     pinstance->stacktop    = ( pinstance->stackmin > LOWORD(context->Ebx) ?
586                                pinstance->stackmin - LOWORD(context->Ebx) : 0 ) + 150;
587
588     /* Initialize the local heap */
589     if (LOWORD(context->Ecx))
590         LocalInit16( GlobalHandleToSel16(pTask->hInstance), 0, LOWORD(context->Ecx) );
591
592     /* Initialize implicitly loaded DLLs */
593     NE_InitializeDLLs( pTask->hModule );
594     NE_DllProcessAttach( pTask->hModule );
595
596     /* Registers on return are:
597      * ax     1 if OK, 0 on error
598      * cx     stack limit in bytes
599      * dx     cmdShow parameter
600      * si     instance handle of the previous instance
601      * di     instance handle of the new task
602      * es:bx  pointer to command line inside PSP
603      *
604      * 0 (=%bp) is pushed on the stack
605      */
606     ptr = stack16_push( sizeof(WORD) );
607     *(WORD *)MapSL(ptr) = 0;
608     context->Esp -= 2;
609
610     context->Eax = 1;
611
612     if (!pTask->pdb.cmdLine[0]) context->Ebx = 0x80;
613     else
614     {
615         LPBYTE p = &pTask->pdb.cmdLine[1];
616         while ((*p == ' ') || (*p == '\t')) p++;
617         context->Ebx = 0x80 + (p - pTask->pdb.cmdLine);
618     }
619     context->Ecx   = pinstance->stacktop;
620     context->Edx   = pTask->nCmdShow;
621     context->Esi   = (DWORD)pTask->hPrevInstance;
622     context->Edi   = (DWORD)pTask->hInstance;
623     context->SegEs = (WORD)pTask->hPDB;
624 }
625
626
627 /***********************************************************************
628  *           WaitEvent  (KERNEL.30)
629  */
630 BOOL16 WINAPI WaitEvent16( HTASK16 hTask )
631 {
632     TDB *pTask;
633
634     if (!hTask) hTask = GetCurrentTask();
635     pTask = TASK_GetPtr( hTask );
636
637     if (pTask->flags & TDBF_WIN32)
638     {
639         FIXME("called for Win32 thread (%04lx)!\n", GetCurrentThreadId());
640         return TRUE;
641     }
642
643     if (pTask->nEvents > 0)
644     {
645         pTask->nEvents--;
646         return FALSE;
647     }
648
649     if (pTask->teb == NtCurrentTeb())
650     {
651         DWORD lockCount;
652
653         NtResetEvent( pTask->hEvent, NULL );
654         ReleaseThunkLock( &lockCount );
655         SYSLEVEL_CheckNotLevel( 1 );
656         WaitForSingleObject( pTask->hEvent, INFINITE );
657         RestoreThunkLock( lockCount );
658         if (pTask->nEvents > 0) pTask->nEvents--;
659     }
660     else FIXME("for other task %04x cur=%04x\n",pTask->hSelf,GetCurrentTask());
661
662     return TRUE;
663 }
664
665
666 /***********************************************************************
667  *           PostEvent  (KERNEL.31)
668  */
669 void WINAPI PostEvent16( HTASK16 hTask )
670 {
671     TDB *pTask;
672
673     if (!hTask) hTask = GetCurrentTask();
674     if (!(pTask = TASK_GetPtr( hTask ))) return;
675
676     if (pTask->flags & TDBF_WIN32)
677     {
678         FIXME("called for Win32 thread (%04lx)!\n", (DWORD)pTask->teb->ClientId.UniqueThread );
679         return;
680     }
681
682     pTask->nEvents++;
683
684     if (pTask->nEvents == 1) NtSetEvent( pTask->hEvent, NULL );
685 }
686
687
688 /***********************************************************************
689  *           SetPriority  (KERNEL.32)
690  */
691 void WINAPI SetPriority16( HTASK16 hTask, INT16 delta )
692 {
693     TDB *pTask;
694     INT16 newpriority;
695
696     if (!hTask) hTask = GetCurrentTask();
697     if (!(pTask = TASK_GetPtr( hTask ))) return;
698     newpriority = pTask->priority + delta;
699     if (newpriority < -32) newpriority = -32;
700     else if (newpriority > 15) newpriority = 15;
701
702     pTask->priority = newpriority + 1;
703     TASK_UnlinkTask( pTask->hSelf );
704     TASK_LinkTask( pTask->hSelf );
705     pTask->priority--;
706 }
707
708
709 /***********************************************************************
710  *           LockCurrentTask  (KERNEL.33)
711  */
712 HTASK16 WINAPI LockCurrentTask16( BOOL16 bLock )
713 {
714     if (bLock) hLockedTask = GetCurrentTask();
715     else hLockedTask = 0;
716     return hLockedTask;
717 }
718
719
720 /***********************************************************************
721  *           IsTaskLocked  (KERNEL.122)
722  */
723 HTASK16 WINAPI IsTaskLocked16(void)
724 {
725     return hLockedTask;
726 }
727
728
729 /***********************************************************************
730  *           OldYield  (KERNEL.117)
731  */
732 void WINAPI OldYield16(void)
733 {
734    DWORD count;
735
736    ReleaseThunkLock(&count);
737    RestoreThunkLock(count);
738 }
739
740 /***********************************************************************
741  *           WIN32_OldYield  (KERNEL.447)
742  */
743 void WINAPI WIN32_OldYield16(void)
744 {
745    DWORD count;
746
747    ReleaseThunkLock(&count);
748    RestoreThunkLock(count);
749 }
750
751 /***********************************************************************
752  *           DirectedYield  (KERNEL.150)
753  */
754 void WINAPI DirectedYield16( HTASK16 hTask )
755 {
756     OldYield16();
757 }
758
759 /***********************************************************************
760  *           Yield  (KERNEL.29)
761  */
762 void WINAPI Yield16(void)
763 {
764     TDB *pCurTask = TASK_GetCurrent();
765
766     if (pCurTask && pCurTask->hQueue)
767     {
768         HMODULE mod = GetModuleHandleA( "user32.dll" );
769         if (mod)
770         {
771             FARPROC proc = GetProcAddress( mod, "UserYield16" );
772             if (proc)
773             {
774                 proc();
775                 return;
776             }
777         }
778     }
779     OldYield16();
780 }
781
782 /***********************************************************************
783  *           KERNEL_490  (KERNEL.490)
784  */
785 HTASK16 WINAPI KERNEL_490( HTASK16 someTask )
786 {
787     if ( !someTask ) return 0;
788
789     FIXME("(%04x): stub\n", someTask );
790     return 0;
791 }
792
793 /***********************************************************************
794  *           MakeProcInstance  (KERNEL.51)
795  */
796 FARPROC16 WINAPI MakeProcInstance16( FARPROC16 func, HANDLE16 hInstance )
797 {
798     BYTE *thunk,*lfunc;
799     SEGPTR thunkaddr;
800     WORD hInstanceSelector;
801
802     hInstanceSelector = GlobalHandleToSel16(hInstance);
803
804     TRACE("(%08lx, %04x);\n", (DWORD)func, hInstance);
805
806     if (!HIWORD(func)) {
807       /* Win95 actually protects via SEH, but this is better for debugging */
808       WARN("Ouch ! Called with invalid func 0x%08lx !\n", (DWORD)func);
809       return (FARPROC16)0;
810     }
811
812     if ( (GlobalHandleToSel16(CURRENT_DS) != hInstanceSelector)
813       && (hInstance != 0)
814       && (hInstance != 0xffff) )
815     {
816         /* calling MPI with a foreign DSEG is invalid ! */
817         WARN("Problem with hInstance? Got %04x, using %04x instead\n",
818                    hInstance,CURRENT_DS);
819     }
820
821     /* Always use the DSEG that MPI was entered with.
822      * We used to set hInstance to GetTaskDS16(), but this should be wrong
823      * as CURRENT_DS provides the DSEG value we need.
824      * ("calling" DS, *not* "task" DS !) */
825     hInstanceSelector = CURRENT_DS;
826     hInstance = GlobalHandle16(hInstanceSelector);
827
828     /* no thunking for DLLs */
829     if (NE_GetPtr(FarGetOwner16(hInstance))->flags & NE_FFLAGS_LIBMODULE)
830         return func;
831
832     thunkaddr = TASK_AllocThunk();
833     if (!thunkaddr) return (FARPROC16)0;
834     thunk = MapSL( thunkaddr );
835     lfunc = MapSL( (SEGPTR)func );
836
837     TRACE("(%08lx,%04x): got thunk %08lx\n",
838           (DWORD)func, hInstance, (DWORD)thunkaddr );
839     if (((lfunc[0]==0x8c) && (lfunc[1]==0xd8)) || /* movw %ds, %ax */
840         ((lfunc[0]==0x1e) && (lfunc[1]==0x58))    /* pushw %ds, popw %ax */
841     ) {
842         WARN("This was the (in)famous \"thunk useless\" warning. We thought we have to overwrite with nop;nop;, but this isn't true.\n");
843     }
844
845     *thunk++ = 0xb8;    /* movw instance, %ax */
846     *thunk++ = (BYTE)(hInstanceSelector & 0xff);
847     *thunk++ = (BYTE)(hInstanceSelector >> 8);
848     *thunk++ = 0xea;    /* ljmp func */
849     *(DWORD *)thunk = (DWORD)func;
850     return (FARPROC16)thunkaddr;
851     /* CX reg indicates if thunkaddr != NULL, implement if needed */
852 }
853
854
855 /***********************************************************************
856  *           FreeProcInstance  (KERNEL.52)
857  */
858 void WINAPI FreeProcInstance16( FARPROC16 func )
859 {
860     TRACE("(%08lx)\n", (DWORD)func );
861     TASK_FreeThunk( (SEGPTR)func );
862 }
863
864 /**********************************************************************
865  *          TASK_GetCodeSegment
866  *
867  * Helper function for GetCodeHandle/GetCodeInfo: Retrieve the module
868  * and logical segment number of a given code segment.
869  *
870  * 'proc' either *is* already a pair of module handle and segment number,
871  * in which case there's nothing to do.  Otherwise, it is a pointer to
872  * a function, and we need to retrieve the code segment.  If the pointer
873  * happens to point to a thunk, we'll retrieve info about the code segment
874  * where the function pointed to by the thunk resides, not the thunk itself.
875  *
876  * FIXME: if 'proc' is a SNOOP16 return stub, we should retrieve info about
877  *        the function the snoop code will return to ...
878  *
879  */
880 static BOOL TASK_GetCodeSegment( FARPROC16 proc, NE_MODULE **ppModule,
881                                  SEGTABLEENTRY **ppSeg, int *pSegNr )
882 {
883     NE_MODULE *pModule = NULL;
884     SEGTABLEENTRY *pSeg = NULL;
885     int segNr=0;
886
887     /* Try pair of module handle / segment number */
888     pModule = (NE_MODULE *) GlobalLock16( HIWORD( proc ) );
889     if ( pModule && pModule->magic == IMAGE_OS2_SIGNATURE )
890     {
891         segNr = LOWORD( proc );
892         if ( segNr && segNr <= pModule->seg_count )
893             pSeg = NE_SEG_TABLE( pModule ) + segNr-1;
894     }
895
896     /* Try thunk or function */
897     else
898     {
899         BYTE *thunk = MapSL( (SEGPTR)proc );
900         WORD selector;
901
902         if ((thunk[0] == 0xb8) && (thunk[3] == 0xea))
903             selector = thunk[6] + (thunk[7] << 8);
904         else
905             selector = HIWORD( proc );
906
907         pModule = NE_GetPtr( GlobalHandle16( selector ) );
908         pSeg = pModule? NE_SEG_TABLE( pModule ) : NULL;
909
910         if ( pModule )
911             for ( segNr = 1; segNr <= pModule->seg_count; segNr++, pSeg++ )
912                 if ( GlobalHandleToSel16(pSeg->hSeg) == selector )
913                     break;
914
915         if ( pModule && segNr > pModule->seg_count )
916             pSeg = NULL;
917     }
918
919     /* Abort if segment not found */
920
921     if ( !pModule || !pSeg )
922         return FALSE;
923
924     /* Return segment data */
925
926     if ( ppModule ) *ppModule = pModule;
927     if ( ppSeg    ) *ppSeg    = pSeg;
928     if ( pSegNr   ) *pSegNr   = segNr;
929
930     return TRUE;
931 }
932
933 /**********************************************************************
934  *          GetCodeHandle    (KERNEL.93)
935  */
936 HANDLE16 WINAPI GetCodeHandle16( FARPROC16 proc )
937 {
938     SEGTABLEENTRY *pSeg;
939
940     if ( !TASK_GetCodeSegment( proc, NULL, &pSeg, NULL ) )
941         return (HANDLE16)0;
942
943     return pSeg->hSeg;
944 }
945
946 /**********************************************************************
947  *          GetCodeInfo    (KERNEL.104)
948  */
949 BOOL16 WINAPI GetCodeInfo16( FARPROC16 proc, SEGINFO *segInfo )
950 {
951     NE_MODULE *pModule;
952     SEGTABLEENTRY *pSeg;
953     int segNr;
954
955     if ( !TASK_GetCodeSegment( proc, &pModule, &pSeg, &segNr ) )
956         return FALSE;
957
958     /* Fill in segment information */
959
960     segInfo->offSegment = pSeg->filepos;
961     segInfo->cbSegment  = pSeg->size;
962     segInfo->flags      = pSeg->flags;
963     segInfo->cbAlloc    = pSeg->minsize;
964     segInfo->h          = pSeg->hSeg;
965     segInfo->alignShift = pModule->alignment;
966
967     if ( segNr == pModule->dgroup )
968         segInfo->cbAlloc += pModule->heap_size + pModule->stack_size;
969
970     /* Return module handle in %es */
971
972     CURRENT_STACK16->es = GlobalHandleToSel16( pModule->self );
973
974     return TRUE;
975 }
976
977
978 /**********************************************************************
979  *          DefineHandleTable    (KERNEL.94)
980  */
981 BOOL16 WINAPI DefineHandleTable16( WORD wOffset )
982 {
983     FIXME("(%04x): stub ?\n", wOffset);
984     return TRUE;
985 }
986
987
988 /***********************************************************************
989  *           SetTaskQueue  (KERNEL.34)
990  */
991 HQUEUE16 WINAPI SetTaskQueue16( HTASK16 hTask, HQUEUE16 hQueue )
992 {
993     HQUEUE16 hPrev;
994     TDB *pTask;
995
996     if (!hTask) hTask = GetCurrentTask();
997     if (!(pTask = TASK_GetPtr( hTask ))) return 0;
998
999     hPrev = pTask->hQueue;
1000     pTask->hQueue = hQueue;
1001
1002     return hPrev;
1003 }
1004
1005
1006 /***********************************************************************
1007  *           GetTaskQueue  (KERNEL.35)
1008  */
1009 HQUEUE16 WINAPI GetTaskQueue16( HTASK16 hTask )
1010 {
1011     TDB *pTask;
1012
1013     if (!hTask) hTask = GetCurrentTask();
1014     if (!(pTask = TASK_GetPtr( hTask ))) return 0;
1015     return pTask->hQueue;
1016 }
1017
1018 /***********************************************************************
1019  *           SetThreadQueue  (KERNEL.463)
1020  */
1021 HQUEUE16 WINAPI SetThreadQueue16( DWORD thread, HQUEUE16 hQueue )
1022 {
1023     HQUEUE16 oldQueue = NtCurrentTeb()->queue;
1024
1025     if (thread && thread != GetCurrentThreadId())
1026     {
1027         FIXME( "not supported on other thread %04lx\n", thread );
1028         return 0;
1029     }
1030     NtCurrentTeb()->queue = hQueue;
1031     if ( GetTaskQueue16( NtCurrentTeb()->htask16 ) == oldQueue )
1032         SetTaskQueue16( NtCurrentTeb()->htask16, hQueue );
1033     return oldQueue;
1034 }
1035
1036 /***********************************************************************
1037  *           GetThreadQueue  (KERNEL.464)
1038  */
1039 HQUEUE16 WINAPI GetThreadQueue16( DWORD thread )
1040 {
1041     if (thread && thread != GetCurrentThreadId())
1042     {
1043         FIXME( "not supported on other thread %04lx\n", thread );
1044         return 0;
1045     }
1046     return NtCurrentTeb()->queue;
1047 }
1048
1049 /***********************************************************************
1050  *           SetFastQueue  (KERNEL.624)
1051  */
1052 VOID WINAPI SetFastQueue16( DWORD thread, HQUEUE16 hQueue )
1053 {
1054     if (!thread || thread == GetCurrentThreadId())
1055         NtCurrentTeb()->queue = hQueue;
1056     else
1057         FIXME( "not supported on other thread %04lx\n", thread );
1058 }
1059
1060 /***********************************************************************
1061  *           GetFastQueue  (KERNEL.625)
1062  */
1063 HQUEUE16 WINAPI GetFastQueue16( void )
1064 {
1065     HQUEUE16 ret = NtCurrentTeb()->queue;
1066
1067     if (!ret) FIXME("(): should initialize thread-local queue, expect failure!\n" );
1068     return ret;
1069 }
1070
1071 /***********************************************************************
1072  *           SwitchStackTo   (KERNEL.108)
1073  */
1074 void WINAPI SwitchStackTo16( WORD seg, WORD ptr, WORD top )
1075 {
1076     STACK16FRAME *oldFrame, *newFrame;
1077     INSTANCEDATA *pData;
1078     UINT16 copySize;
1079
1080     if (!(pData = (INSTANCEDATA *)GlobalLock16( seg ))) return;
1081     TRACE("old=%04x:%04x new=%04x:%04x\n",
1082           SELECTOROF( NtCurrentTeb()->cur_stack ),
1083           OFFSETOF( NtCurrentTeb()->cur_stack ), seg, ptr );
1084
1085     /* Save the old stack */
1086
1087     oldFrame = CURRENT_STACK16;
1088     /* pop frame + args and push bp */
1089     pData->old_ss_sp   = NtCurrentTeb()->cur_stack + sizeof(STACK16FRAME)
1090                            + 2 * sizeof(WORD);
1091     *(WORD *)MapSL(pData->old_ss_sp) = oldFrame->bp;
1092     pData->stacktop    = top;
1093     pData->stackmin    = ptr;
1094     pData->stackbottom = ptr;
1095
1096     /* Switch to the new stack */
1097
1098     /* Note: we need to take the 3 arguments into account; otherwise,
1099      * the stack will underflow upon return from this function.
1100      */
1101     copySize = oldFrame->bp - OFFSETOF(pData->old_ss_sp);
1102     copySize += 3 * sizeof(WORD) + sizeof(STACK16FRAME);
1103     NtCurrentTeb()->cur_stack = MAKESEGPTR( seg, ptr - copySize );
1104     newFrame = CURRENT_STACK16;
1105
1106     /* Copy the stack frame and the local variables to the new stack */
1107
1108     memmove( newFrame, oldFrame, copySize );
1109     newFrame->bp = ptr;
1110     *(WORD *)MapSL( MAKESEGPTR( seg, ptr ) ) = 0;  /* clear previous bp */
1111 }
1112
1113
1114 /***********************************************************************
1115  *           SwitchStackBack   (KERNEL.109)
1116  */
1117 void WINAPI SwitchStackBack16( CONTEXT86 *context )
1118 {
1119     STACK16FRAME *oldFrame, *newFrame;
1120     INSTANCEDATA *pData;
1121
1122     if (!(pData = (INSTANCEDATA *)GlobalLock16(SELECTOROF(NtCurrentTeb()->cur_stack))))
1123         return;
1124     if (!pData->old_ss_sp)
1125     {
1126         WARN("No previous SwitchStackTo\n" );
1127         return;
1128     }
1129     TRACE("restoring stack %04x:%04x\n",
1130           SELECTOROF(pData->old_ss_sp), OFFSETOF(pData->old_ss_sp) );
1131
1132     oldFrame = CURRENT_STACK16;
1133
1134     /* Pop bp from the previous stack */
1135
1136     context->Ebp = (context->Ebp & ~0xffff) | *(WORD *)MapSL(pData->old_ss_sp);
1137     pData->old_ss_sp += sizeof(WORD);
1138
1139     /* Switch back to the old stack */
1140
1141     NtCurrentTeb()->cur_stack = pData->old_ss_sp - sizeof(STACK16FRAME);
1142     context->SegSs = SELECTOROF(pData->old_ss_sp);
1143     context->Esp   = OFFSETOF(pData->old_ss_sp) - sizeof(DWORD); /*ret addr*/
1144     pData->old_ss_sp = 0;
1145
1146     /* Build a stack frame for the return */
1147
1148     newFrame = CURRENT_STACK16;
1149     newFrame->frame32     = oldFrame->frame32;
1150     newFrame->module_cs   = oldFrame->module_cs;
1151     newFrame->callfrom_ip = oldFrame->callfrom_ip;
1152     newFrame->entry_ip    = oldFrame->entry_ip;
1153 }
1154
1155
1156 /***********************************************************************
1157  *           GetTaskQueueDS  (KERNEL.118)
1158  */
1159 void WINAPI GetTaskQueueDS16(void)
1160 {
1161     CURRENT_STACK16->ds = GlobalHandleToSel16( GetTaskQueue16(0) );
1162 }
1163
1164
1165 /***********************************************************************
1166  *           GetTaskQueueES  (KERNEL.119)
1167  */
1168 void WINAPI GetTaskQueueES16(void)
1169 {
1170     CURRENT_STACK16->es = GlobalHandleToSel16( GetTaskQueue16(0) );
1171 }
1172
1173
1174 /***********************************************************************
1175  *           GetCurrentTask   (KERNEL32.@)
1176  */
1177 HTASK16 WINAPI GetCurrentTask(void)
1178 {
1179     return NtCurrentTeb()->htask16;
1180 }
1181
1182 /***********************************************************************
1183  *           GetCurrentTask   (KERNEL.36)
1184  */
1185 DWORD WINAPI WIN16_GetCurrentTask(void)
1186 {
1187     /* This is the version used by relay code; the first task is */
1188     /* returned in the high word of the result */
1189     return MAKELONG( GetCurrentTask(), hFirstTask );
1190 }
1191
1192
1193 /***********************************************************************
1194  *           GetCurrentPDB   (KERNEL.37)
1195  *
1196  * UNDOC: returns PSP of KERNEL in high word
1197  */
1198 DWORD WINAPI GetCurrentPDB16(void)
1199 {
1200     TDB *pTask;
1201
1202     if (!(pTask = TASK_GetCurrent())) return 0;
1203     return MAKELONG(pTask->hPDB, 0); /* FIXME */
1204 }
1205
1206
1207 /***********************************************************************
1208  *           GetCurPID   (KERNEL.157)
1209  */
1210 DWORD WINAPI GetCurPID16( DWORD unused )
1211 {
1212     return 0;
1213 }
1214
1215
1216 /***********************************************************************
1217  *           GetInstanceData   (KERNEL.54)
1218  */
1219 INT16 WINAPI GetInstanceData16( HINSTANCE16 instance, WORD buffer, INT16 len )
1220 {
1221     char *ptr = (char *)GlobalLock16( instance );
1222     if (!ptr || !len) return 0;
1223     if ((int)buffer + len >= 0x10000) len = 0x10000 - buffer;
1224     memcpy( (char *)GlobalLock16(CURRENT_DS) + buffer, ptr + buffer, len );
1225     return len;
1226 }
1227
1228
1229 /***********************************************************************
1230  *           GetExeVersion   (KERNEL.105)
1231  */
1232 WORD WINAPI GetExeVersion16(void)
1233 {
1234     TDB *pTask;
1235
1236     if (!(pTask = TASK_GetCurrent())) return 0;
1237     return pTask->version;
1238 }
1239
1240
1241 /***********************************************************************
1242  *           SetErrorMode   (KERNEL.107)
1243  */
1244 UINT16 WINAPI SetErrorMode16( UINT16 mode )
1245 {
1246     TDB *pTask;
1247     if (!(pTask = TASK_GetCurrent())) return 0;
1248     pTask->error_mode = mode;
1249     return SetErrorMode( mode );
1250 }
1251
1252
1253 /***********************************************************************
1254  *           GetNumTasks   (KERNEL.152)
1255  */
1256 UINT16 WINAPI GetNumTasks16(void)
1257 {
1258     return nTaskCount;
1259 }
1260
1261
1262 /***********************************************************************
1263  *           GetTaskDS   (KERNEL.155)
1264  *
1265  * Note: this function apparently returns a DWORD with LOWORD == HIWORD.
1266  * I don't think we need to bother with this.
1267  */
1268 HINSTANCE16 WINAPI GetTaskDS16(void)
1269 {
1270     TDB *pTask;
1271
1272     if (!(pTask = TASK_GetCurrent())) return 0;
1273     return GlobalHandleToSel16(pTask->hInstance);
1274 }
1275
1276 /***********************************************************************
1277  *           GetDummyModuleHandleDS   (KERNEL.602)
1278  */
1279 WORD WINAPI GetDummyModuleHandleDS16(void)
1280 {
1281     TDB *pTask;
1282     WORD selector;
1283
1284     if (!(pTask = TASK_GetCurrent())) return 0;
1285     if (!(pTask->flags & TDBF_WIN32)) return 0;
1286     selector = GlobalHandleToSel16( pTask->hModule );
1287     CURRENT_DS = selector;
1288     return selector;
1289 }
1290
1291 /***********************************************************************
1292  *           IsTask   (KERNEL.320)
1293  */
1294 BOOL16 WINAPI IsTask16( HTASK16 hTask )
1295 {
1296     TDB *pTask;
1297
1298     if (!(pTask = TASK_GetPtr( hTask ))) return FALSE;
1299     if (GlobalSize16( hTask ) < sizeof(TDB)) return FALSE;
1300     return (pTask->magic == TDB_MAGIC);
1301 }
1302
1303
1304 /***********************************************************************
1305  *           IsWinOldApTask   (KERNEL.158)
1306  */
1307 BOOL16 WINAPI IsWinOldApTask16( HTASK16 hTask )
1308 {
1309     /* should return bit 0 of byte 0x48 in PSP */
1310     return FALSE;
1311 }
1312
1313 /***********************************************************************
1314  *           SetTaskSignalProc   (KERNEL.38)
1315  */
1316 FARPROC16 WINAPI SetTaskSignalProc( HTASK16 hTask, FARPROC16 proc )
1317 {
1318     TDB *pTask;
1319     FARPROC16 oldProc;
1320
1321     if (!hTask) hTask = GetCurrentTask();
1322     if (!(pTask = TASK_GetPtr( hTask ))) return NULL;
1323     oldProc = pTask->userhandler;
1324     pTask->userhandler = proc;
1325     return oldProc;
1326 }
1327
1328 /***********************************************************************
1329  *           SetSigHandler   (KERNEL.140)
1330  */
1331 WORD WINAPI SetSigHandler16( FARPROC16 newhandler, FARPROC16* oldhandler,
1332                            UINT16 *oldmode, UINT16 newmode, UINT16 flag )
1333 {
1334     FIXME("(%p,%p,%p,%d,%d), unimplemented.\n",
1335           newhandler,oldhandler,oldmode,newmode,flag );
1336
1337     if (flag != 1) return 0;
1338     if (!newmode) newhandler = NULL;  /* Default handler */
1339     if (newmode != 4)
1340     {
1341         TDB *pTask;
1342
1343         if (!(pTask = TASK_GetCurrent())) return 0;
1344         if (oldmode) *oldmode = pTask->signal_flags;
1345         pTask->signal_flags = newmode;
1346         if (oldhandler) *oldhandler = pTask->sighandler;
1347         pTask->sighandler = newhandler;
1348     }
1349     return 0;
1350 }
1351
1352
1353 /***********************************************************************
1354  *           GlobalNotify   (KERNEL.154)
1355  *
1356  * Note that GlobalNotify does _not_ return the old NotifyProc
1357  * -- contrary to LocalNotify !!
1358  */
1359 VOID WINAPI GlobalNotify16( FARPROC16 proc )
1360 {
1361     TDB *pTask;
1362
1363     if (!(pTask = TASK_GetCurrent())) return;
1364     pTask->discardhandler = proc;
1365 }
1366
1367
1368 /***********************************************************************
1369  *           GetExePtrHelper
1370  */
1371 static inline HMODULE16 GetExePtrHelper( HANDLE16 handle, HTASK16 *hTask )
1372 {
1373     char *ptr;
1374     HANDLE16 owner;
1375
1376       /* Check for module handle */
1377
1378     if (!(ptr = GlobalLock16( handle ))) return 0;
1379     if (((NE_MODULE *)ptr)->magic == IMAGE_OS2_SIGNATURE) return handle;
1380
1381       /* Search for this handle inside all tasks */
1382
1383     *hTask = hFirstTask;
1384     while (*hTask)
1385     {
1386         TDB *pTask = TASK_GetPtr( *hTask );
1387         if ((*hTask == handle) ||
1388             (pTask->hInstance == handle) ||
1389             (pTask->hQueue == handle) ||
1390             (pTask->hPDB == handle)) return pTask->hModule;
1391         *hTask = pTask->hNext;
1392     }
1393
1394       /* Check the owner for module handle */
1395
1396     owner = FarGetOwner16( handle );
1397     if (!(ptr = GlobalLock16( owner ))) return 0;
1398     if (((NE_MODULE *)ptr)->magic == IMAGE_OS2_SIGNATURE) return owner;
1399
1400       /* Search for the owner inside all tasks */
1401
1402     *hTask = hFirstTask;
1403     while (*hTask)
1404     {
1405         TDB *pTask = TASK_GetPtr( *hTask );
1406         if ((*hTask == owner) ||
1407             (pTask->hInstance == owner) ||
1408             (pTask->hQueue == owner) ||
1409             (pTask->hPDB == owner)) return pTask->hModule;
1410         *hTask = pTask->hNext;
1411     }
1412
1413     return 0;
1414 }
1415
1416 /***********************************************************************
1417  *           GetExePtr   (KERNEL.133)
1418  */
1419 HMODULE16 WINAPI WIN16_GetExePtr( HANDLE16 handle )
1420 {
1421     HTASK16 hTask = 0;
1422     HMODULE16 hModule = GetExePtrHelper( handle, &hTask );
1423     STACK16FRAME *frame = CURRENT_STACK16;
1424     frame->ecx = hModule;
1425     if (hTask) frame->es = hTask;
1426     return hModule;
1427 }
1428
1429
1430 /***********************************************************************
1431  *           K228   (KERNEL.228)
1432  */
1433 HMODULE16 WINAPI GetExePtr( HANDLE16 handle )
1434 {
1435     HTASK16 hTask = 0;
1436     return GetExePtrHelper( handle, &hTask );
1437 }
1438
1439
1440 /***********************************************************************
1441  *           TaskFirst   (TOOLHELP.63)
1442  */
1443 BOOL16 WINAPI TaskFirst16( TASKENTRY *lpte )
1444 {
1445     lpte->hNext = hFirstTask;
1446     return TaskNext16( lpte );
1447 }
1448
1449
1450 /***********************************************************************
1451  *           TaskNext   (TOOLHELP.64)
1452  */
1453 BOOL16 WINAPI TaskNext16( TASKENTRY *lpte )
1454 {
1455     TDB *pTask;
1456     INSTANCEDATA *pInstData;
1457
1458     TRACE_(toolhelp)("(%p): task=%04x\n", lpte, lpte->hNext );
1459     if (!lpte->hNext) return FALSE;
1460
1461     /* make sure that task and hInstance are valid (skip initial Wine task !) */
1462     while (1) {
1463         pTask = TASK_GetPtr( lpte->hNext );
1464         if (!pTask || pTask->magic != TDB_MAGIC) return FALSE;
1465         if (pTask->hInstance)
1466             break;
1467         lpte->hNext = pTask->hNext;
1468     }
1469     pInstData = MapSL( MAKESEGPTR( GlobalHandleToSel16(pTask->hInstance), 0 ) );
1470     lpte->hTask         = lpte->hNext;
1471     lpte->hTaskParent   = pTask->hParent;
1472     lpte->hInst         = pTask->hInstance;
1473     lpte->hModule       = pTask->hModule;
1474     lpte->wSS           = SELECTOROF( pTask->teb->cur_stack );
1475     lpte->wSP           = OFFSETOF( pTask->teb->cur_stack );
1476     lpte->wStackTop     = pInstData->stacktop;
1477     lpte->wStackMinimum = pInstData->stackmin;
1478     lpte->wStackBottom  = pInstData->stackbottom;
1479     lpte->wcEvents      = pTask->nEvents;
1480     lpte->hQueue        = pTask->hQueue;
1481     lstrcpynA( lpte->szModule, pTask->module_name, sizeof(lpte->szModule) );
1482     lpte->wPSPOffset    = 0x100;  /*??*/
1483     lpte->hNext         = pTask->hNext;
1484     return TRUE;
1485 }
1486
1487
1488 /***********************************************************************
1489  *           TaskFindHandle   (TOOLHELP.65)
1490  */
1491 BOOL16 WINAPI TaskFindHandle16( TASKENTRY *lpte, HTASK16 hTask )
1492 {
1493     lpte->hNext = hTask;
1494     return TaskNext16( lpte );
1495 }
1496
1497
1498 typedef INT (WINAPI *MessageBoxA_funcptr)(HWND hWnd, LPCSTR text, LPCSTR title, UINT type);
1499
1500 /**************************************************************************
1501  *           FatalAppExit   (KERNEL.137)
1502  */
1503 void WINAPI FatalAppExit16( UINT16 action, LPCSTR str )
1504 {
1505     TDB *pTask = TASK_GetCurrent();
1506
1507     if (!pTask || !(pTask->error_mode & SEM_NOGPFAULTERRORBOX))
1508     {
1509         HMODULE mod = GetModuleHandleA( "user32.dll" );
1510         if (mod)
1511         {
1512             MessageBoxA_funcptr pMessageBoxA = (MessageBoxA_funcptr)GetProcAddress( mod, "MessageBoxA" );
1513             if (pMessageBoxA)
1514             {
1515                 pMessageBoxA( 0, str, NULL, MB_SYSTEMMODAL | MB_OK );
1516                 goto done;
1517             }
1518         }
1519         ERR( "%s\n", debugstr_a(str) );
1520     }
1521  done:
1522     ExitThread(0xff);
1523 }
1524
1525
1526 /***********************************************************************
1527  *           TerminateApp   (TOOLHELP.77)
1528  *
1529  * See "Undocumented Windows".
1530  */
1531 void WINAPI TerminateApp16(HTASK16 hTask, WORD wFlags)
1532 {
1533     if (hTask && hTask != GetCurrentTask())
1534     {
1535         FIXME("cannot terminate task %x\n", hTask);
1536         return;
1537     }
1538
1539     if (wFlags & NO_UAE_BOX)
1540     {
1541         UINT16 old_mode;
1542         old_mode = SetErrorMode16(0);
1543         SetErrorMode16(old_mode|SEM_NOGPFAULTERRORBOX);
1544     }
1545     FatalAppExit16( 0, NULL );
1546
1547     /* hmm, we're still alive ?? */
1548
1549     /* check undocumented flag */
1550     if (!(wFlags & 0x8000))
1551         TASK_CallTaskSignalProc( USIG16_TERMINATION, hTask );
1552
1553     /* UndocWin says to call int 0x21/0x4c exit=0xff here,
1554        but let's just call ExitThread */
1555     ExitThread(0xff);
1556 }
1557
1558
1559 /***********************************************************************
1560  *           GetAppCompatFlags   (KERNEL.354)
1561  */
1562 DWORD WINAPI GetAppCompatFlags16( HTASK16 hTask )
1563 {
1564     TDB *pTask;
1565
1566     if (!hTask) hTask = GetCurrentTask();
1567     if (!(pTask=TASK_GetPtr( hTask ))) return 0;
1568     if (GlobalSize16(hTask) < sizeof(TDB)) return 0;
1569     return pTask->compat_flags;
1570 }
1571
1572
1573 /***********************************************************************
1574  *           GetDOSEnvironment     (KERNEL.131)
1575  *
1576  * Note: the environment is allocated once, it doesn't track changes
1577  * made using the Win32 API. This shouldn't matter.
1578  *
1579  * Format of a 16-bit environment block:
1580  * ASCIIZ   string 1 (xx=yy format)
1581  * ...
1582  * ASCIIZ   string n
1583  * BYTE     0
1584  * WORD     1
1585  * ASCIIZ   program name (e.g. C:\WINDOWS\SYSTEM\KRNL386.EXE)
1586  */
1587 SEGPTR WINAPI GetDOSEnvironment16(void)
1588 {
1589     static const char ENV_program_name[] = "C:\\WINDOWS\\SYSTEM\\KRNL386.EXE";
1590     static HGLOBAL16 handle;  /* handle to the 16 bit environment */
1591
1592     if (!handle)
1593     {
1594         DWORD size;
1595         LPSTR p, env;
1596
1597         p = env = GetEnvironmentStringsA();
1598         while (*p) p += strlen(p) + 1;
1599         p++;  /* skip last null */
1600         size = (p - env) + sizeof(WORD) + sizeof(ENV_program_name);
1601         handle = GlobalAlloc16( GMEM_FIXED, size );
1602         if (handle)
1603         {
1604             WORD one = 1;
1605             LPSTR env16 = GlobalLock16( handle );
1606             memcpy( env16, env, p - env );
1607             memcpy( env16 + (p - env), &one, sizeof(one));
1608             memcpy( env16 + (p - env) + sizeof(WORD), ENV_program_name, sizeof(ENV_program_name));
1609             GlobalUnlock16( handle );
1610         }
1611         FreeEnvironmentStringsA( env );
1612     }
1613     return K32WOWGlobalLock16( handle );
1614 }