d3drm: Just use RGBA_MAKE.
[wine] / dlls / toolhelp.dll16 / toolhelp.c
1 /*
2  * Toolhelp functions
3  *
4  * Copyright 1996 Marcus Meissner
5  * Copyright 2009 Alexandre Julliard
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include "config.h"
23
24 #define NONAMELESSUNION
25 #define NONAMELESSSTRUCT
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #ifdef HAVE_UNISTD_H
30 # include <unistd.h>
31 #endif
32 #include <ctype.h>
33 #include <assert.h>
34 #include "windef.h"
35 #include "winbase.h"
36 #include "winternl.h"
37 #include "wownt32.h"
38
39 #include "wine/winbase16.h"
40 #include "toolhelp.h"
41 #include "wine/debug.h"
42
43 WINE_DEFAULT_DEBUG_CHANNEL(toolhelp);
44
45 #include "pshpack1.h"
46
47 typedef struct
48 {
49     void     *base;          /* Base address (0 if discarded) */
50     DWORD     size;          /* Size in bytes (0 indicates a free block) */
51     HGLOBAL16 handle;        /* Handle for this block */
52     HGLOBAL16 hOwner;        /* Owner of this block */
53     BYTE      lockCount;     /* Count of GlobalFix() calls */
54     BYTE      pageLockCount; /* Count of GlobalPageLock() calls */
55     BYTE      flags;         /* Allocation flags */
56     BYTE      selCount;      /* Number of selectors allocated for this block */
57 } GLOBALARENA;
58
59 #define GLOBAL_MAX_COUNT  8192        /* Max number of allocated blocks */
60
61 typedef struct
62 {
63     WORD check;                 /* 00 Heap checking flag */
64     WORD freeze;                /* 02 Heap frozen flag */
65     WORD items;                 /* 04 Count of items on the heap */
66     WORD first;                 /* 06 First item of the heap */
67     WORD pad1;                  /* 08 Always 0 */
68     WORD last;                  /* 0a Last item of the heap */
69     WORD pad2;                  /* 0c Always 0 */
70     BYTE ncompact;              /* 0e Compactions counter */
71     BYTE dislevel;              /* 0f Discard level */
72     DWORD distotal;             /* 10 Total bytes discarded */
73     WORD htable;                /* 14 Pointer to handle table */
74     WORD hfree;                 /* 16 Pointer to free handle table */
75     WORD hdelta;                /* 18 Delta to expand the handle table */
76     WORD expand;                /* 1a Pointer to expand function (unused) */
77     WORD pstat;                 /* 1c Pointer to status structure (unused) */
78     FARPROC16 notify;           /* 1e Pointer to LocalNotify() function */
79     WORD lock;                  /* 22 Lock count for the heap */
80     WORD extra;                 /* 24 Extra bytes to allocate when expanding */
81     WORD minsize;               /* 26 Minimum size of the heap */
82     WORD magic;                 /* 28 Magic number */
83 } LOCALHEAPINFO;
84
85 typedef struct
86 {
87 /* Arena header */
88     WORD prev;          /* Previous arena | arena type */
89     WORD next;          /* Next arena */
90 /* Start of the memory block or free-list info */
91     WORD size;          /* Size of the free block */
92     WORD free_prev;     /* Previous free block */
93     WORD free_next;     /* Next free block */
94 } LOCALARENA;
95
96 #define LOCAL_ARENA_HEADER_SIZE      4
97 #define LOCAL_ARENA_HEADER( handle) ((handle) - LOCAL_ARENA_HEADER_SIZE)
98 #define LOCAL_ARENA_PTR(ptr,arena)  ((LOCALARENA *)((char *)(ptr)+(arena)))
99
100 typedef struct
101 {
102     WORD null;        /* Always 0 */
103     DWORD old_ss_sp;  /* Stack pointer; used by SwitchTaskTo() */
104     WORD heap;        /* Pointer to the local heap information (if any) */
105     WORD atomtable;   /* Pointer to the local atom table (if any) */
106     WORD stacktop;    /* Top of the stack */
107     WORD stackmin;    /* Lowest stack address used so far */
108     WORD stackbottom; /* Bottom of the stack */
109 } INSTANCEDATA;
110
111 typedef struct _THHOOK
112 {
113     HANDLE16   hGlobalHeap;         /* 00 (handle BURGERMASTER) */
114     WORD       pGlobalHeap;         /* 02 (selector BURGERMASTER) */
115     HMODULE16  hExeHead;            /* 04 hFirstModule */
116     HMODULE16  hExeSweep;           /* 06 (unused) */
117     HANDLE16   TopPDB;              /* 08 (handle of KERNEL PDB) */
118     HANDLE16   HeadPDB;             /* 0A (first PDB in list) */
119     HANDLE16   TopSizePDB;          /* 0C (unused) */
120     HTASK16    HeadTDB;             /* 0E hFirstTask */
121     HTASK16    CurTDB;              /* 10 hCurrentTask */
122     HTASK16    LoadTDB;             /* 12 (unused) */
123     HTASK16    LockTDB;             /* 14 hLockedTask */
124 } THHOOK;
125
126 typedef struct _NE_MODULE
127 {
128     WORD      ne_magic;         /* 00 'NE' signature */
129     WORD      count;            /* 02 Usage count (ne_ver/ne_rev on disk) */
130     WORD      ne_enttab;        /* 04 Near ptr to entry table */
131     HMODULE16 next;             /* 06 Selector to next module (ne_cbenttab on disk) */
132     WORD      dgroup_entry;     /* 08 Near ptr to segment entry for DGROUP (ne_crc on disk) */
133     WORD      fileinfo;         /* 0a Near ptr to file info (OFSTRUCT) (ne_crc on disk) */
134     WORD      ne_flags;         /* 0c Module flags */
135     WORD      ne_autodata;      /* 0e Logical segment for DGROUP */
136     WORD      ne_heap;          /* 10 Initial heap size */
137     WORD      ne_stack;         /* 12 Initial stack size */
138     DWORD     ne_csip;          /* 14 Initial cs:ip */
139     DWORD     ne_sssp;          /* 18 Initial ss:sp */
140     WORD      ne_cseg;          /* 1c Number of segments in segment table */
141     WORD      ne_cmod;          /* 1e Number of module references */
142     WORD      ne_cbnrestab;     /* 20 Size of non-resident names table */
143     WORD      ne_segtab;        /* 22 Near ptr to segment table */
144     WORD      ne_rsrctab;       /* 24 Near ptr to resource table */
145     WORD      ne_restab;        /* 26 Near ptr to resident names table */
146     WORD      ne_modtab;        /* 28 Near ptr to module reference table */
147     WORD      ne_imptab;        /* 2a Near ptr to imported names table */
148     DWORD     ne_nrestab;       /* 2c File offset of non-resident names table */
149     WORD      ne_cmovent;       /* 30 Number of moveable entries in entry table*/
150     WORD      ne_align;         /* 32 Alignment shift count */
151     WORD      ne_cres;          /* 34 # of resource segments */
152     BYTE      ne_exetyp;        /* 36 Operating system flags */
153     BYTE      ne_flagsothers;   /* 37 Misc. flags */
154     HANDLE16  dlls_to_init;     /* 38 List of DLLs to initialize (ne_pretthunks on disk) */
155     HANDLE16  nrname_handle;    /* 3a Handle to non-resident name table (ne_psegrefbytes on disk) */
156     WORD      ne_swaparea;      /* 3c Min. swap area size */
157     WORD      ne_expver;        /* 3e Expected Windows version */
158     /* From here, these are extra fields not present in normal Windows */
159     HMODULE   module32;         /* PE module handle for Win32 modules */
160     HMODULE   owner32;          /* PE module containing this one for 16-bit builtins */
161     HMODULE16 self;             /* Handle for this module */
162     WORD      self_loading_sel; /* Selector used for self-loading apps. */
163     LPVOID    rsrc32_map;       /* HRSRC 16->32 map (for 32-bit modules) */
164     LPCVOID   mapping;          /* mapping of the binary file */
165     SIZE_T    mapping_size;     /* size of the file mapping */
166 } NE_MODULE;
167
168 #include "poppack.h"
169
170 #define TDB_MAGIC    ('T' | ('D' << 8))
171
172 /* FIXME: to make this work, we have to call back all these registered
173  * functions from all over the WINE code. Someone with more knowledge than
174  * me please do that. -Marcus
175  */
176
177 static struct notify
178 {
179     HTASK16   htask;
180     FARPROC16 lpfnCallback;
181     WORD     wFlags;
182 } *notifys = NULL;
183
184 static int nrofnotifys = 0;
185
186 static THHOOK *get_thhook(void)
187 {
188     static THHOOK *thhook;
189
190     if (!thhook) thhook = MapSL( (SEGPTR)GetProcAddress16( GetModuleHandle16("KERNEL"), (LPCSTR)332 ));
191     return thhook;
192 }
193
194 static GLOBALARENA *get_global_arena(void)
195 {
196     return *(GLOBALARENA **)get_thhook();
197 }
198
199 static LOCALHEAPINFO *get_local_heap( HANDLE16 ds )
200 {
201     INSTANCEDATA *ptr = MapSL( MAKESEGPTR( ds, 0 ));
202
203     if (!ptr || !ptr->heap) return NULL;
204     return (LOCALHEAPINFO*)((char*)ptr + ptr->heap);
205 }
206
207
208 /***********************************************************************
209  *           GlobalHandleToSel   (TOOLHELP.50)
210  */
211 WORD WINAPI GlobalHandleToSel16( HGLOBAL16 handle )
212 {
213     if (!handle) return 0;
214     if (!(handle & 7)) return handle - 1;
215     return handle | 7;
216 }
217
218
219 /***********************************************************************
220  *           GlobalFirst   (TOOLHELP.51)
221  */
222 BOOL16 WINAPI GlobalFirst16( GLOBALENTRY *pGlobal, WORD wFlags )
223 {
224     if (wFlags == GLOBAL_LRU) return FALSE;
225     pGlobal->dwNext = 0;
226     return GlobalNext16( pGlobal, wFlags );
227 }
228
229
230 /***********************************************************************
231  *           GlobalNext   (TOOLHELP.52)
232  */
233 BOOL16 WINAPI GlobalNext16( GLOBALENTRY *pGlobal, WORD wFlags)
234 {
235     GLOBALARENA *pGlobalArena = get_global_arena();
236     GLOBALARENA *pArena;
237
238     if (pGlobal->dwNext >= GLOBAL_MAX_COUNT) return FALSE;
239     pArena = pGlobalArena + pGlobal->dwNext;
240     if (wFlags == GLOBAL_FREE)  /* only free blocks */
241     {
242         int i;
243         for (i = pGlobal->dwNext; i < GLOBAL_MAX_COUNT; i++, pArena++)
244             if (pArena->size == 0) break;  /* block is free */
245         if (i >= GLOBAL_MAX_COUNT) return FALSE;
246         pGlobal->dwNext = i;
247     }
248
249     pGlobal->dwAddress    = (DWORD_PTR)pArena->base;
250     pGlobal->dwBlockSize  = pArena->size;
251     pGlobal->hBlock       = pArena->handle;
252     pGlobal->wcLock       = pArena->lockCount;
253     pGlobal->wcPageLock   = pArena->pageLockCount;
254     pGlobal->wFlags       = (GetCurrentPDB16() == pArena->hOwner);
255     pGlobal->wHeapPresent = FALSE;
256     pGlobal->hOwner       = pArena->hOwner;
257     pGlobal->wType        = GT_UNKNOWN;
258     pGlobal->wData        = 0;
259     pGlobal->dwNext++;
260     return TRUE;
261 }
262
263
264 /***********************************************************************
265  *           GlobalInfo   (TOOLHELP.53)
266  */
267 BOOL16 WINAPI GlobalInfo16( GLOBALINFO *pInfo )
268 {
269     GLOBALARENA *pGlobalArena = get_global_arena();
270     GLOBALARENA *pArena;
271     int i;
272
273     pInfo->wcItems = GLOBAL_MAX_COUNT;
274     pInfo->wcItemsFree = 0;
275     pInfo->wcItemsLRU = 0;
276     for (i = 0, pArena = pGlobalArena; i < GLOBAL_MAX_COUNT; i++, pArena++)
277         if (pArena->size == 0) pInfo->wcItemsFree++;
278     return TRUE;
279 }
280
281
282 /***********************************************************************
283  *           GlobalEntryHandle   (TOOLHELP.54)
284  */
285 BOOL16 WINAPI GlobalEntryHandle16( GLOBALENTRY *pGlobal, HGLOBAL16 hItem )
286 {
287     GLOBALARENA *pGlobalArena = get_global_arena();
288     GLOBALARENA *pArena = pGlobalArena + (hItem >> __AHSHIFT);
289
290     pGlobal->dwAddress    = (DWORD_PTR)pArena->base;
291     pGlobal->dwBlockSize  = pArena->size;
292     pGlobal->hBlock       = pArena->handle;
293     pGlobal->wcLock       = pArena->lockCount;
294     pGlobal->wcPageLock   = pArena->pageLockCount;
295     pGlobal->wFlags       = (GetCurrentPDB16() == pArena->hOwner);
296     pGlobal->wHeapPresent = FALSE;
297     pGlobal->hOwner       = pArena->hOwner;
298     pGlobal->wType        = GT_UNKNOWN;
299     pGlobal->wData        = 0;
300     pGlobal->dwNext++;
301     return TRUE;
302 }
303
304
305 /***********************************************************************
306  *           GlobalEntryModule   (TOOLHELP.55)
307  */
308 BOOL16 WINAPI GlobalEntryModule16( GLOBALENTRY *pGlobal, HMODULE16 hModule,
309                                  WORD wSeg )
310 {
311     FIXME("(%p, 0x%04x, 0x%04x), stub.\n", pGlobal, hModule, wSeg);
312     return FALSE;
313 }
314
315
316 /***********************************************************************
317  *           LocalInfo   (TOOLHELP.56)
318  */
319 BOOL16 WINAPI LocalInfo16( LOCALINFO *pLocalInfo, HGLOBAL16 handle )
320 {
321     LOCALHEAPINFO *pInfo = get_local_heap( SELECTOROF(WOWGlobalLock16(handle)) );
322     if (!pInfo) return FALSE;
323     pLocalInfo->wcItems = pInfo->items;
324     return TRUE;
325 }
326
327
328 /***********************************************************************
329  *           LocalFirst   (TOOLHELP.57)
330  */
331 BOOL16 WINAPI LocalFirst16( LOCALENTRY *pLocalEntry, HGLOBAL16 handle )
332 {
333     WORD ds = GlobalHandleToSel16( handle );
334     char *ptr = MapSL( MAKESEGPTR( ds, 0 ) );
335     LOCALHEAPINFO *pInfo = get_local_heap( ds );
336     if (!pInfo) return FALSE;
337
338     pLocalEntry->hHandle   = pInfo->first + LOCAL_ARENA_HEADER_SIZE;
339     pLocalEntry->wAddress  = pLocalEntry->hHandle;
340     pLocalEntry->wFlags    = LF_FIXED;
341     pLocalEntry->wcLock    = 0;
342     pLocalEntry->wType     = LT_NORMAL;
343     pLocalEntry->hHeap     = handle;
344     pLocalEntry->wHeapType = NORMAL_HEAP;
345     pLocalEntry->wNext     = LOCAL_ARENA_PTR(ptr,pInfo->first)->next;
346     pLocalEntry->wSize     = pLocalEntry->wNext - pLocalEntry->hHandle;
347     return TRUE;
348 }
349
350
351 /***********************************************************************
352  *           LocalNext   (TOOLHELP.58)
353  */
354 BOOL16 WINAPI LocalNext16( LOCALENTRY *pLocalEntry )
355 {
356     WORD ds = GlobalHandleToSel16( pLocalEntry->hHeap );
357     char *ptr = MapSL( MAKESEGPTR( ds, 0 ) );
358     LOCALARENA *pArena;
359
360     if (!get_local_heap( ds )) return FALSE;
361     if (!pLocalEntry->wNext) return FALSE;
362     pArena = LOCAL_ARENA_PTR( ptr, pLocalEntry->wNext );
363
364     pLocalEntry->hHandle   = pLocalEntry->wNext + LOCAL_ARENA_HEADER_SIZE;
365     pLocalEntry->wAddress  = pLocalEntry->hHandle;
366     pLocalEntry->wFlags    = (pArena->prev & 3) + 1;
367     pLocalEntry->wcLock    = 0;
368     pLocalEntry->wType     = LT_NORMAL;
369     if (pArena->next != pLocalEntry->wNext)  /* last one? */
370         pLocalEntry->wNext = pArena->next;
371     else
372         pLocalEntry->wNext = 0;
373     pLocalEntry->wSize     = pLocalEntry->wNext - pLocalEntry->hHandle;
374     return TRUE;
375 }
376
377
378 /**********************************************************************
379  *          ModuleFirst    (TOOLHELP.59)
380  */
381 BOOL16 WINAPI ModuleFirst16( MODULEENTRY *lpme )
382 {
383     lpme->wNext = get_thhook()->hExeHead;
384     return ModuleNext16( lpme );
385 }
386
387
388 /**********************************************************************
389  *          ModuleNext    (TOOLHELP.60)
390  */
391 BOOL16 WINAPI ModuleNext16( MODULEENTRY *lpme )
392 {
393     NE_MODULE *pModule;
394     char *name;
395
396     if (!lpme->wNext) return FALSE;
397     if (!(pModule = GlobalLock16( GetExePtr(lpme->wNext) ))) return FALSE;
398     name = (char *)pModule + pModule->ne_restab;
399     memcpy( lpme->szModule, name + 1, min(*name, MAX_MODULE_NAME) );
400     lpme->szModule[min(*name, MAX_MODULE_NAME)] = '\0';
401     lpme->hModule = lpme->wNext;
402     lpme->wcUsage = pModule->count;
403     name = ((OFSTRUCT *)((char*)pModule + pModule->fileinfo))->szPathName;
404     lstrcpynA( lpme->szExePath, name, sizeof(lpme->szExePath) );
405     lpme->wNext = pModule->next;
406     return TRUE;
407 }
408
409
410 /**********************************************************************
411  *          ModuleFindName    (TOOLHELP.61)
412  */
413 BOOL16 WINAPI ModuleFindName16( MODULEENTRY *lpme, LPCSTR name )
414 {
415     lpme->wNext = GetModuleHandle16( name );
416     return ModuleNext16( lpme );
417 }
418
419
420 /**********************************************************************
421  *          ModuleFindHandle    (TOOLHELP.62)
422  */
423 BOOL16 WINAPI ModuleFindHandle16( MODULEENTRY *lpme, HMODULE16 hModule )
424 {
425     hModule = GetExePtr( hModule );
426     lpme->wNext = hModule;
427     return ModuleNext16( lpme );
428 }
429
430
431 /***********************************************************************
432  *           TaskFirst   (TOOLHELP.63)
433  */
434 BOOL16 WINAPI TaskFirst16( TASKENTRY *lpte )
435 {
436     lpte->hNext = get_thhook()->HeadTDB;
437     return TaskNext16( lpte );
438 }
439
440
441 /***********************************************************************
442  *           TaskNext   (TOOLHELP.64)
443  */
444 BOOL16 WINAPI TaskNext16( TASKENTRY *lpte )
445 {
446     TDB *pTask;
447     INSTANCEDATA *pInstData;
448
449     TRACE_(toolhelp)("(%p): task=%04x\n", lpte, lpte->hNext );
450     if (!lpte->hNext) return FALSE;
451
452     /* make sure that task and hInstance are valid (skip initial Wine task !) */
453     while (1) {
454         pTask = GlobalLock16( lpte->hNext );
455         if (!pTask || pTask->magic != TDB_MAGIC) return FALSE;
456         if (pTask->hInstance)
457             break;
458         lpte->hNext = pTask->hNext;
459     }
460     pInstData = MapSL( MAKESEGPTR( GlobalHandleToSel16(pTask->hInstance), 0 ) );
461     lpte->hTask         = lpte->hNext;
462     lpte->hTaskParent   = pTask->hParent;
463     lpte->hInst         = pTask->hInstance;
464     lpte->hModule       = pTask->hModule;
465     lpte->wSS           = SELECTOROF( pTask->teb->WOW32Reserved );
466     lpte->wSP           = OFFSETOF( pTask->teb->WOW32Reserved );
467     lpte->wStackTop     = pInstData->stacktop;
468     lpte->wStackMinimum = pInstData->stackmin;
469     lpte->wStackBottom  = pInstData->stackbottom;
470     lpte->wcEvents      = pTask->nEvents;
471     lpte->hQueue        = pTask->hQueue;
472     lstrcpynA( lpte->szModule, pTask->module_name, sizeof(lpte->szModule) );
473     lpte->wPSPOffset    = 0x100;  /*??*/
474     lpte->hNext         = pTask->hNext;
475     return TRUE;
476 }
477
478
479 /***********************************************************************
480  *           TaskFindHandle   (TOOLHELP.65)
481  */
482 BOOL16 WINAPI TaskFindHandle16( TASKENTRY *lpte, HTASK16 hTask )
483 {
484     lpte->hNext = hTask;
485     return TaskNext16( lpte );
486 }
487
488
489 /***********************************************************************
490  *           MemManInfo   (TOOLHELP.72)
491  */
492 BOOL16 WINAPI MemManInfo16( MEMMANINFO *info )
493 {
494     SYSTEM_BASIC_INFORMATION sbi;
495     MEMORYSTATUS status;
496
497     /*
498      * Not unsurprisingly although the documentation says you
499      * _must_ provide the size in the dwSize field, this function
500      * (under Windows) always fills the structure and returns true.
501      */
502     NtQuerySystemInformation( SystemBasicInformation, &sbi, sizeof(sbi), NULL );
503     GlobalMemoryStatus( &status );
504     info->wPageSize            = sbi.PageSize;
505     info->dwLargestFreeBlock   = status.dwAvailVirtual;
506     info->dwMaxPagesAvailable  = info->dwLargestFreeBlock / info->wPageSize;
507     info->dwMaxPagesLockable   = info->dwMaxPagesAvailable;
508     info->dwTotalLinearSpace   = status.dwTotalVirtual / info->wPageSize;
509     info->dwTotalUnlockedPages = info->dwTotalLinearSpace;
510     info->dwFreePages          = info->dwMaxPagesAvailable;
511     info->dwTotalPages         = info->dwTotalLinearSpace;
512     info->dwFreeLinearSpace    = info->dwMaxPagesAvailable;
513     info->dwSwapFilePages      = status.dwTotalPageFile / info->wPageSize;
514     return TRUE;
515 }
516
517
518 /***********************************************************************
519  *              NotifyRegister (TOOLHELP.73)
520  */
521 BOOL16 WINAPI NotifyRegister16( HTASK16 htask, FARPROC16 lpfnCallback,
522                               WORD wFlags )
523 {
524     int i;
525
526     FIXME("(%x,%x,%x), semi-stub.\n",
527                       htask, (DWORD)lpfnCallback, wFlags );
528     if (!htask) htask = GetCurrentTask();
529     for (i=0;i<nrofnotifys;i++)
530         if (notifys[i].htask==htask)
531             break;
532     if (i==nrofnotifys) {
533         if (notifys==NULL)
534             notifys=HeapAlloc( GetProcessHeap(), 0,
535                                                sizeof(struct notify) );
536         else
537             notifys=HeapReAlloc( GetProcessHeap(), 0, notifys,
538                                         sizeof(struct notify)*(nrofnotifys+1));
539         if (!notifys) return FALSE;
540         nrofnotifys++;
541     }
542     notifys[i].htask=htask;
543     notifys[i].lpfnCallback=lpfnCallback;
544     notifys[i].wFlags=wFlags;
545     return TRUE;
546 }
547
548 /***********************************************************************
549  *              NotifyUnregister (TOOLHELP.74)
550  */
551 BOOL16 WINAPI NotifyUnregister16( HTASK16 htask )
552 {
553     int i;
554
555     FIXME("(%x), semi-stub.\n", htask );
556     if (!htask) htask = GetCurrentTask();
557     for (i=nrofnotifys;i--;)
558         if (notifys[i].htask==htask)
559             break;
560     if (i==-1)
561         return FALSE;
562     memcpy(notifys+i,notifys+(i+1),sizeof(struct notify)*(nrofnotifys-i-1));
563     notifys=HeapReAlloc( GetProcessHeap(), 0, notifys,
564                                         (nrofnotifys-1)*sizeof(struct notify));
565     nrofnotifys--;
566     return TRUE;
567 }
568
569 /***********************************************************************
570  *              StackTraceCSIPFirst (TOOLHELP.67)
571  */
572 BOOL16 WINAPI StackTraceCSIPFirst16(STACKTRACEENTRY *ste, WORD wSS, WORD wCS, WORD wIP, WORD wBP)
573 {
574     FIXME("(%p, ss %04x, cs %04x, ip %04x, bp %04x): stub.\n", ste, wSS, wCS, wIP, wBP);
575     return TRUE;
576 }
577
578 /***********************************************************************
579  *              StackTraceFirst (TOOLHELP.66)
580  */
581 BOOL16 WINAPI StackTraceFirst16(STACKTRACEENTRY *ste, HTASK16 Task)
582 {
583     FIXME("(%p, %04x), stub.\n", ste, Task);
584     return TRUE;
585 }
586
587 /***********************************************************************
588  *              StackTraceNext (TOOLHELP.68)
589  */
590 BOOL16 WINAPI StackTraceNext16(STACKTRACEENTRY *ste)
591 {
592     FIXME("(%p), stub.\n", ste);
593     return TRUE;
594 }
595
596 /***********************************************************************
597  *              InterruptRegister (TOOLHELP.75)
598  */
599 BOOL16 WINAPI InterruptRegister16( HTASK16 task, FARPROC callback )
600 {
601     FIXME("(%04x, %p), stub.\n", task, callback);
602     return TRUE;
603 }
604
605 /***********************************************************************
606  *              InterruptUnRegister (TOOLHELP.76)
607  */
608 BOOL16 WINAPI InterruptUnRegister16( HTASK16 task )
609 {
610     FIXME("(%04x), stub.\n", task);
611     return TRUE;
612 }
613
614 /***********************************************************************
615  *           TerminateApp   (TOOLHELP.77)
616  *
617  * See "Undocumented Windows".
618  */
619 void WINAPI TerminateApp16(HTASK16 hTask, WORD wFlags)
620 {
621     if (hTask && hTask != GetCurrentTask())
622     {
623         FIXME("cannot terminate task %x\n", hTask);
624         return;
625     }
626
627 #if 0  /* FIXME */
628     /* check undocumented flag */
629     if (!(wFlags & 0x8000))
630         TASK_CallTaskSignalProc( USIG16_TERMINATION, hTask );
631 #endif
632
633     /* UndocWin says to call int 0x21/0x4c exit=0xff here,
634        but let's just call ExitThread */
635     ExitThread(0xff);
636 }
637
638 /***********************************************************************
639  *           MemoryRead   (TOOLHELP.78)
640  */
641 DWORD WINAPI MemoryRead16( WORD sel, DWORD offset, void *buffer, DWORD count )
642 {
643     LDT_ENTRY entry;
644     DWORD limit;
645
646     wine_ldt_get_entry( sel, &entry );
647     if (wine_ldt_is_empty( &entry )) return 0;
648     limit = wine_ldt_get_limit( &entry );
649     if (offset > limit) return 0;
650     if (offset + count > limit + 1) count = limit + 1 - offset;
651     memcpy( buffer, (char *)wine_ldt_get_base(&entry) + offset, count );
652     return count;
653 }
654
655
656 /***********************************************************************
657  *           MemoryWrite   (TOOLHELP.79)
658  */
659 DWORD WINAPI MemoryWrite16( WORD sel, DWORD offset, void *buffer, DWORD count )
660 {
661     LDT_ENTRY entry;
662     DWORD limit;
663
664     wine_ldt_get_entry( sel, &entry );
665     if (wine_ldt_is_empty( &entry )) return 0;
666     limit = wine_ldt_get_limit( &entry );
667     if (offset > limit) return 0;
668     if (offset + count > limit) count = limit + 1 - offset;
669     memcpy( (char *)wine_ldt_get_base(&entry) + offset, buffer, count );
670     return count;
671 }
672
673 /***********************************************************************
674  *           TimerCount   (TOOLHELP.80)
675  */
676 BOOL16 WINAPI TimerCount16( TIMERINFO *pTimerInfo )
677 {
678     /* FIXME
679      * In standard mode, dwmsSinceStart = dwmsThisVM
680      *
681      * I tested this, under Windows in enhanced mode, and
682      * if you never switch VM (ie start/stop DOS) these
683      * values should be the same as well.
684      *
685      * Also, Wine should adjust for the hardware timer
686      * to reduce the amount of error to ~1ms.
687      * I can't be bothered, can you?
688      */
689     pTimerInfo->dwmsSinceStart = pTimerInfo->dwmsThisVM = GetTickCount();
690     return TRUE;
691 }
692
693 /***********************************************************************
694  *           SystemHeapInfo   (TOOLHELP.71)
695  */
696 BOOL16 WINAPI SystemHeapInfo16( SYSHEAPINFO *pHeapInfo )
697 {
698     STACK16FRAME* stack16 = MapSL((SEGPTR)NtCurrentTeb()->WOW32Reserved);
699     HANDLE16 oldDS = stack16->ds;
700     WORD user = LoadLibrary16( "USER.EXE" );
701     WORD gdi = LoadLibrary16( "GDI.EXE" );
702     stack16->ds = user;
703     pHeapInfo->wUserFreePercent = (int)LocalCountFree16() * 100 / LocalHeapSize16();
704     stack16->ds = gdi;
705     pHeapInfo->wGDIFreePercent  = (int)LocalCountFree16() * 100 / LocalHeapSize16();
706     stack16->ds = oldDS;
707     pHeapInfo->hUserSegment = user;
708     pHeapInfo->hGDISegment  = gdi;
709     FreeLibrary16( user );
710     FreeLibrary16( gdi );
711     return TRUE;
712 }
713
714 /***********************************************************************
715  *           Local32Info   (TOOLHELP.84)
716  */
717 BOOL16 WINAPI Local32Info16( LOCAL32INFO *pLocal32Info, HGLOBAL16 handle )
718 {
719     FIXME( "Call Local32Info16 in kernel\n" );
720     return FALSE;
721 }
722
723 /***********************************************************************
724  *           Local32First   (TOOLHELP.85)
725  */
726 BOOL16 WINAPI Local32First16( LOCAL32ENTRY *pLocal32Entry, HGLOBAL16 handle )
727 {
728     FIXME( "Call Local32First16 in kernel\n" );
729     return FALSE;
730 }
731
732 /***********************************************************************
733  *           Local32Next   (TOOLHELP.86)
734  */
735 BOOL16 WINAPI Local32Next16( LOCAL32ENTRY *pLocal32Entry )
736 {
737     FIXME( "Call Local32Next16 in kernel\n" );
738     return FALSE;
739 }