user32: Fix uninitialized variable so first call to DdeGetLastError isn't undefined.
[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     MEMORYSTATUS status;
495
496     /*
497      * Not unsurprisingly although the documentation says you
498      * _must_ provide the size in the dwSize field, this function
499      * (under Windows) always fills the structure and returns true.
500      */
501     GlobalMemoryStatus( &status );
502     info->wPageSize            = getpagesize();
503     info->dwLargestFreeBlock   = status.dwAvailVirtual;
504     info->dwMaxPagesAvailable  = info->dwLargestFreeBlock / info->wPageSize;
505     info->dwMaxPagesLockable   = info->dwMaxPagesAvailable;
506     info->dwTotalLinearSpace   = status.dwTotalVirtual / info->wPageSize;
507     info->dwTotalUnlockedPages = info->dwTotalLinearSpace;
508     info->dwFreePages          = info->dwMaxPagesAvailable;
509     info->dwTotalPages         = info->dwTotalLinearSpace;
510     info->dwFreeLinearSpace    = info->dwMaxPagesAvailable;
511     info->dwSwapFilePages      = status.dwTotalPageFile / info->wPageSize;
512     return TRUE;
513 }
514
515
516 /***********************************************************************
517  *              NotifyRegister (TOOLHELP.73)
518  */
519 BOOL16 WINAPI NotifyRegister16( HTASK16 htask, FARPROC16 lpfnCallback,
520                               WORD wFlags )
521 {
522     int i;
523
524     FIXME("(%x,%x,%x), semi-stub.\n",
525                       htask, (DWORD)lpfnCallback, wFlags );
526     if (!htask) htask = GetCurrentTask();
527     for (i=0;i<nrofnotifys;i++)
528         if (notifys[i].htask==htask)
529             break;
530     if (i==nrofnotifys) {
531         if (notifys==NULL)
532             notifys=HeapAlloc( GetProcessHeap(), 0,
533                                                sizeof(struct notify) );
534         else
535             notifys=HeapReAlloc( GetProcessHeap(), 0, notifys,
536                                         sizeof(struct notify)*(nrofnotifys+1));
537         if (!notifys) return FALSE;
538         nrofnotifys++;
539     }
540     notifys[i].htask=htask;
541     notifys[i].lpfnCallback=lpfnCallback;
542     notifys[i].wFlags=wFlags;
543     return TRUE;
544 }
545
546 /***********************************************************************
547  *              NotifyUnregister (TOOLHELP.74)
548  */
549 BOOL16 WINAPI NotifyUnregister16( HTASK16 htask )
550 {
551     int i;
552
553     FIXME("(%x), semi-stub.\n", htask );
554     if (!htask) htask = GetCurrentTask();
555     for (i=nrofnotifys;i--;)
556         if (notifys[i].htask==htask)
557             break;
558     if (i==-1)
559         return FALSE;
560     memcpy(notifys+i,notifys+(i+1),sizeof(struct notify)*(nrofnotifys-i-1));
561     notifys=HeapReAlloc( GetProcessHeap(), 0, notifys,
562                                         (nrofnotifys-1)*sizeof(struct notify));
563     nrofnotifys--;
564     return TRUE;
565 }
566
567 /***********************************************************************
568  *              StackTraceCSIPFirst (TOOLHELP.67)
569  */
570 BOOL16 WINAPI StackTraceCSIPFirst16(STACKTRACEENTRY *ste, WORD wSS, WORD wCS, WORD wIP, WORD wBP)
571 {
572     FIXME("(%p, ss %04x, cs %04x, ip %04x, bp %04x): stub.\n", ste, wSS, wCS, wIP, wBP);
573     return TRUE;
574 }
575
576 /***********************************************************************
577  *              StackTraceFirst (TOOLHELP.66)
578  */
579 BOOL16 WINAPI StackTraceFirst16(STACKTRACEENTRY *ste, HTASK16 Task)
580 {
581     FIXME("(%p, %04x), stub.\n", ste, Task);
582     return TRUE;
583 }
584
585 /***********************************************************************
586  *              StackTraceNext (TOOLHELP.68)
587  */
588 BOOL16 WINAPI StackTraceNext16(STACKTRACEENTRY *ste)
589 {
590     FIXME("(%p), stub.\n", ste);
591     return TRUE;
592 }
593
594 /***********************************************************************
595  *              InterruptRegister (TOOLHELP.75)
596  */
597 BOOL16 WINAPI InterruptRegister16( HTASK16 task, FARPROC callback )
598 {
599     FIXME("(%04x, %p), stub.\n", task, callback);
600     return TRUE;
601 }
602
603 /***********************************************************************
604  *              InterruptUnRegister (TOOLHELP.76)
605  */
606 BOOL16 WINAPI InterruptUnRegister16( HTASK16 task )
607 {
608     FIXME("(%04x), stub.\n", task);
609     return TRUE;
610 }
611
612 /***********************************************************************
613  *           TerminateApp   (TOOLHELP.77)
614  *
615  * See "Undocumented Windows".
616  */
617 void WINAPI TerminateApp16(HTASK16 hTask, WORD wFlags)
618 {
619     if (hTask && hTask != GetCurrentTask())
620     {
621         FIXME("cannot terminate task %x\n", hTask);
622         return;
623     }
624
625 #if 0  /* FIXME */
626     /* check undocumented flag */
627     if (!(wFlags & 0x8000))
628         TASK_CallTaskSignalProc( USIG16_TERMINATION, hTask );
629 #endif
630
631     /* UndocWin says to call int 0x21/0x4c exit=0xff here,
632        but let's just call ExitThread */
633     ExitThread(0xff);
634 }
635
636 /***********************************************************************
637  *           MemoryRead   (TOOLHELP.78)
638  */
639 DWORD WINAPI MemoryRead16( WORD sel, DWORD offset, void *buffer, DWORD count )
640 {
641     LDT_ENTRY entry;
642     DWORD limit;
643
644     wine_ldt_get_entry( sel, &entry );
645     if (wine_ldt_is_empty( &entry )) return 0;
646     limit = wine_ldt_get_limit( &entry );
647     if (offset > limit) return 0;
648     if (offset + count > limit + 1) count = limit + 1 - offset;
649     memcpy( buffer, (char *)wine_ldt_get_base(&entry) + offset, count );
650     return count;
651 }
652
653
654 /***********************************************************************
655  *           MemoryWrite   (TOOLHELP.79)
656  */
657 DWORD WINAPI MemoryWrite16( WORD sel, DWORD offset, void *buffer, DWORD count )
658 {
659     LDT_ENTRY entry;
660     DWORD limit;
661
662     wine_ldt_get_entry( sel, &entry );
663     if (wine_ldt_is_empty( &entry )) return 0;
664     limit = wine_ldt_get_limit( &entry );
665     if (offset > limit) return 0;
666     if (offset + count > limit) count = limit + 1 - offset;
667     memcpy( (char *)wine_ldt_get_base(&entry) + offset, buffer, count );
668     return count;
669 }
670
671 /***********************************************************************
672  *           TimerCount   (TOOLHELP.80)
673  */
674 BOOL16 WINAPI TimerCount16( TIMERINFO *pTimerInfo )
675 {
676     /* FIXME
677      * In standard mode, dwmsSinceStart = dwmsThisVM
678      *
679      * I tested this, under Windows in enhanced mode, and
680      * if you never switch VM (ie start/stop DOS) these
681      * values should be the same as well.
682      *
683      * Also, Wine should adjust for the hardware timer
684      * to reduce the amount of error to ~1ms.
685      * I can't be bothered, can you?
686      */
687     pTimerInfo->dwmsSinceStart = pTimerInfo->dwmsThisVM = GetTickCount();
688     return TRUE;
689 }
690
691 /***********************************************************************
692  *           SystemHeapInfo   (TOOLHELP.71)
693  */
694 BOOL16 WINAPI SystemHeapInfo16( SYSHEAPINFO *pHeapInfo )
695 {
696     STACK16FRAME* stack16 = MapSL((SEGPTR)NtCurrentTeb()->WOW32Reserved);
697     HANDLE16 oldDS = stack16->ds;
698     WORD user = LoadLibrary16( "USER.EXE" );
699     WORD gdi = LoadLibrary16( "GDI.EXE" );
700     stack16->ds = user;
701     pHeapInfo->wUserFreePercent = (int)LocalCountFree16() * 100 / LocalHeapSize16();
702     stack16->ds = gdi;
703     pHeapInfo->wGDIFreePercent  = (int)LocalCountFree16() * 100 / LocalHeapSize16();
704     stack16->ds = oldDS;
705     pHeapInfo->hUserSegment = user;
706     pHeapInfo->hGDISegment  = gdi;
707     FreeLibrary16( user );
708     FreeLibrary16( gdi );
709     return TRUE;
710 }
711
712 /***********************************************************************
713  *           Local32Info   (TOOLHELP.84)
714  */
715 BOOL16 WINAPI Local32Info16( LOCAL32INFO *pLocal32Info, HGLOBAL16 handle )
716 {
717     FIXME( "Call Local32Info16 in kernel\n" );
718     return FALSE;
719 }
720
721 /***********************************************************************
722  *           Local32First   (TOOLHELP.85)
723  */
724 BOOL16 WINAPI Local32First16( LOCAL32ENTRY *pLocal32Entry, HGLOBAL16 handle )
725 {
726     FIXME( "Call Local32First16 in kernel\n" );
727     return FALSE;
728 }
729
730 /***********************************************************************
731  *           Local32Next   (TOOLHELP.86)
732  */
733 BOOL16 WINAPI Local32Next16( LOCAL32ENTRY *pLocal32Entry )
734 {
735     FIXME( "Call Local32Next16 in kernel\n" );
736     return FALSE;
737 }