Reimplement GetStringTypeA and GetStringTypeExA.
[wine] / memory / heap.c
1 /*
2  * Win32 heap functions
3  *
4  * Copyright 1996 Alexandre Julliard
5  * Copyright 1998 Ulrich Weigand
6  */
7
8 #include "config.h"
9
10 #include <assert.h>
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <string.h>
14
15 #include "winbase.h"
16 #include "wine/winbase16.h"
17 #include "winerror.h"
18 #include "winnt.h"
19 #include "ntddk.h"
20 #include "wine/unicode.h"
21 #include "selectors.h"
22 #include "global.h"
23 #include "thread.h"
24 #include "toolhelp.h"
25 #include "debugtools.h"
26
27 DEFAULT_DEBUG_CHANNEL(heap);
28
29 /* address where we try to map the system heap */
30 #define SYSTEM_HEAP_BASE  ((void*)0x65430000)
31 #define SYSTEM_HEAP_SIZE  0x100000   /* Default heap size = 1Mb */
32
33
34 #define HTABLE_SIZE      0x10000
35 #define HTABLE_PAGESIZE  0x1000
36 #define HTABLE_NPAGES    (HTABLE_SIZE / HTABLE_PAGESIZE)
37
38 #include "pshpack1.h"
39 typedef struct _LOCAL32HEADER
40 {
41     WORD     freeListFirst[HTABLE_NPAGES];
42     WORD     freeListSize[HTABLE_NPAGES];
43     WORD     freeListLast[HTABLE_NPAGES];
44
45     DWORD    selectorTableOffset;
46     WORD     selectorTableSize;
47     WORD     selectorDelta;
48
49     DWORD    segment;
50     LPBYTE   base;
51
52     DWORD    limit;
53     DWORD    flags;
54
55     DWORD    magic;
56     HANDLE heap;
57
58 } LOCAL32HEADER;
59 #include "poppack.h"
60
61 #define LOCAL32_MAGIC    ((DWORD)('L' | ('H'<<8) | ('3'<<16) | ('2'<<24)))
62
63 static HANDLE systemHeap;   /* globally shared heap */
64
65 /***********************************************************************
66  *           HEAP_CreateSystemHeap
67  *
68  * Create the system heap.
69  */
70 inline static HANDLE HEAP_CreateSystemHeap(void)
71 {
72     int created;
73     void *base;
74     HANDLE map, event;
75     UNICODE_STRING event_name;
76     OBJECT_ATTRIBUTES event_attr;
77
78     if (!(map = CreateFileMappingA( INVALID_HANDLE_VALUE, NULL, SEC_COMMIT | PAGE_READWRITE,
79                                     0, SYSTEM_HEAP_SIZE, "__SystemHeap" ))) return 0;
80     created = (GetLastError() != ERROR_ALREADY_EXISTS);
81
82     if (!(base = MapViewOfFileEx( map, FILE_MAP_ALL_ACCESS, 0, 0, 0, SYSTEM_HEAP_BASE )))
83     {
84         /* pre-defined address not available */
85         ERR( "system heap base address %p not available\n", SYSTEM_HEAP_BASE );
86         return 0;
87     }
88
89     /* create the system heap event */
90     RtlCreateUnicodeStringFromAsciiz( &event_name, "__SystemHeapEvent" );
91     event_attr.Length = sizeof(event_attr);
92     event_attr.RootDirectory = 0;
93     event_attr.ObjectName = &event_name;
94     event_attr.Attributes = 0;
95     event_attr.SecurityDescriptor = NULL;
96     event_attr.SecurityQualityOfService = NULL;
97     NtCreateEvent( &event, EVENT_ALL_ACCESS, &event_attr, TRUE, FALSE );
98
99     if (created)  /* newly created heap */
100     {
101         systemHeap = RtlCreateHeap( HEAP_SHARED, base, SYSTEM_HEAP_SIZE,
102                                     SYSTEM_HEAP_SIZE, NULL, NULL );
103         NtSetEvent( event, NULL );
104     }
105     else
106     {
107         /* wait for the heap to be initialized */
108         WaitForSingleObject( event, INFINITE );
109         systemHeap = (HANDLE)base;
110     }
111     CloseHandle( map );
112     return systemHeap;
113 }
114
115
116 /***********************************************************************
117  *           HeapCreate   (KERNEL32.@)
118  * RETURNS
119  *      Handle of heap: Success
120  *      NULL: Failure
121  */
122 HANDLE WINAPI HeapCreate(
123                 DWORD flags,       /* [in] Heap allocation flag */
124                 DWORD initialSize, /* [in] Initial heap size */
125                 DWORD maxSize      /* [in] Maximum heap size */
126 ) {
127     HANDLE ret;
128
129     if ( flags & HEAP_SHARED )
130     {
131         if (!systemHeap) HEAP_CreateSystemHeap();
132         else WARN( "Shared Heap requested, returning system heap.\n" );
133         ret = systemHeap;
134     }
135     else
136     {
137         ret = RtlCreateHeap( flags, NULL, maxSize, initialSize, NULL, NULL );
138         if (!ret) SetLastError( ERROR_NOT_ENOUGH_MEMORY );
139     }
140     return ret;
141 }
142
143 /***********************************************************************
144  *           HeapDestroy   (KERNEL32.@)
145  * RETURNS
146  *      TRUE: Success
147  *      FALSE: Failure
148  */
149 BOOL WINAPI HeapDestroy( HANDLE heap /* [in] Handle of heap */ )
150 {
151     if (heap == systemHeap)
152     {
153         WARN( "attempt to destroy system heap, returning TRUE!\n" );
154         return TRUE;
155     }
156     if (!RtlDestroyHeap( heap )) return TRUE;
157     SetLastError( ERROR_INVALID_HANDLE );
158     return FALSE;
159 }
160
161
162 /***********************************************************************
163  *           HeapCompact   (KERNEL32.@)
164  */
165 DWORD WINAPI HeapCompact( HANDLE heap, DWORD flags )
166 {
167     return RtlCompactHeap( heap, flags );
168 }
169
170
171 /***********************************************************************
172  *           HeapLock   (KERNEL32.@)
173  * Attempts to acquire the critical section object for a specified heap.
174  *
175  * RETURNS
176  *      TRUE: Success
177  *      FALSE: Failure
178  */
179 BOOL WINAPI HeapLock(
180               HANDLE heap /* [in] Handle of heap to lock for exclusive access */
181 ) {
182     return RtlLockHeap( heap );
183 }
184
185
186 /***********************************************************************
187  *           HeapUnlock   (KERNEL32.@)
188  * Releases ownership of the critical section object.
189  *
190  * RETURNS
191  *      TRUE: Success
192  *      FALSE: Failure
193  */
194 BOOL WINAPI HeapUnlock(
195               HANDLE heap /* [in] Handle to the heap to unlock */
196 ) {
197     return RtlUnlockHeap( heap );
198 }
199
200
201 /***********************************************************************
202  *           HeapValidate   (KERNEL32.@)
203  * Validates a specified heap.
204  *
205  * NOTES
206  *      Flags is ignored.
207  *
208  * RETURNS
209  *      TRUE: Success
210  *      FALSE: Failure
211  */
212 BOOL WINAPI HeapValidate(
213               HANDLE heap, /* [in] Handle to the heap */
214               DWORD flags,   /* [in] Bit flags that control access during operation */
215               LPCVOID block  /* [in] Optional pointer to memory block to validate */
216 ) {
217     return RtlValidateHeap( heap, flags, block );
218 }
219
220
221 /***********************************************************************
222  *           HeapWalk   (KERNEL32.@)
223  * Enumerates the memory blocks in a specified heap.
224  *
225  * TODO
226  *   - handling of PROCESS_HEAP_ENTRY_MOVEABLE and
227  *     PROCESS_HEAP_ENTRY_DDESHARE (needs heap.c support)
228  *
229  * RETURNS
230  *      TRUE: Success
231  *      FALSE: Failure
232  */
233 BOOL WINAPI HeapWalk(
234               HANDLE heap,               /* [in]  Handle to heap to enumerate */
235               LPPROCESS_HEAP_ENTRY entry /* [out] Pointer to structure of enumeration info */
236 ) {
237     NTSTATUS ret = RtlWalkHeap( heap, entry );
238     if (ret) SetLastError( RtlNtStatusToDosError(ret) );
239     return !ret;
240 }
241
242
243 /***********************************************************************
244  *           GetProcessHeap    (KERNEL32.@)
245  */
246 HANDLE WINAPI GetProcessHeap(void)
247 {
248     HANDLE *pdb = (HANDLE *)NtCurrentTeb()->process;
249     return pdb[0x18 / sizeof(HANDLE)];  /* get dword at offset 0x18 in pdb */
250 }
251
252
253 /***********************************************************************
254  *           GetProcessHeaps    (KERNEL32.@)
255  */
256 DWORD WINAPI GetProcessHeaps( DWORD count, HANDLE *heaps )
257 {
258     return RtlGetProcessHeaps( count, heaps );
259 }
260
261
262 /***********************************************************************
263  * 32-bit local heap functions (Win95; undocumented)
264  */
265
266 /***********************************************************************
267  *           K208   (KERNEL.208)
268  */
269 HANDLE WINAPI Local32Init16( WORD segment, DWORD tableSize,
270                              DWORD heapSize, DWORD flags )
271 {
272     DWORD totSize, segSize = 0;
273     LPBYTE base;
274     LOCAL32HEADER *header;
275     HANDLE heap;
276     WORD *selectorTable;
277     WORD selectorEven, selectorOdd;
278     int i, nrBlocks;
279
280     /* Determine new heap size */
281
282     if ( segment )
283     {
284         if ( (segSize = GetSelectorLimit16( segment )) == 0 )
285             return 0;
286         else
287             segSize++;
288     }
289
290     if ( heapSize == -1L )
291         heapSize = 1024L*1024L;   /* FIXME */
292
293     heapSize = (heapSize + 0xffff) & 0xffff0000;
294     segSize  = (segSize  + 0x0fff) & 0xfffff000;
295     totSize  = segSize + HTABLE_SIZE + heapSize;
296
297
298     /* Allocate memory and initialize heap */
299
300     if ( !(base = VirtualAlloc( NULL, totSize, MEM_RESERVE, PAGE_READWRITE )) )
301         return 0;
302
303     if ( !VirtualAlloc( base, segSize + HTABLE_PAGESIZE, 
304                         MEM_COMMIT, PAGE_READWRITE ) )
305     {
306         VirtualFree( base, 0, MEM_RELEASE );
307         return 0;
308     }
309
310     if (!(heap = RtlCreateHeap( 0, base + segSize + HTABLE_SIZE, heapSize, 0x10000, NULL, NULL )))
311     {
312         VirtualFree( base, 0, MEM_RELEASE );
313         return 0;
314     }
315
316
317     /* Set up header and handle table */
318     
319     header = (LOCAL32HEADER *)(base + segSize);
320     header->base    = base;
321     header->limit   = HTABLE_PAGESIZE-1;
322     header->flags   = 0;
323     header->magic   = LOCAL32_MAGIC;
324     header->heap    = heap;
325
326     header->freeListFirst[0] = sizeof(LOCAL32HEADER);
327     header->freeListLast[0]  = HTABLE_PAGESIZE - 4;
328     header->freeListSize[0]  = (HTABLE_PAGESIZE - sizeof(LOCAL32HEADER)) / 4;
329
330     for (i = header->freeListFirst[0]; i < header->freeListLast[0]; i += 4)
331         *(DWORD *)((LPBYTE)header + i) = i+4;
332
333     header->freeListFirst[1] = 0xffff;
334
335
336     /* Set up selector table */
337   
338     nrBlocks      = (totSize + 0x7fff) >> 15; 
339     selectorTable = (LPWORD) HeapAlloc( header->heap,  0, nrBlocks * 2 );
340     selectorEven  = SELECTOR_AllocBlock( base, totSize, WINE_LDT_FLAGS_DATA );
341     selectorOdd   = SELECTOR_AllocBlock( base + 0x8000, totSize - 0x8000, WINE_LDT_FLAGS_DATA );
342     if ( !selectorTable || !selectorEven || !selectorOdd )
343     {
344         if ( selectorTable ) HeapFree( header->heap, 0, selectorTable );
345         if ( selectorEven  ) SELECTOR_FreeBlock( selectorEven );
346         if ( selectorOdd   ) SELECTOR_FreeBlock( selectorOdd );
347         HeapDestroy( header->heap );
348         VirtualFree( base, 0, MEM_RELEASE );
349         return 0;
350     }
351
352     header->selectorTableOffset = (LPBYTE)selectorTable - header->base;
353     header->selectorTableSize   = nrBlocks * 4;  /* ??? Win95 does it this way! */
354     header->selectorDelta       = selectorEven - selectorOdd;
355     header->segment             = segment? segment : selectorEven;
356
357     for (i = 0; i < nrBlocks; i++)
358         selectorTable[i] = (i & 1)? selectorOdd  + ((i >> 1) << __AHSHIFT)
359                                   : selectorEven + ((i >> 1) << __AHSHIFT);
360
361     /* Move old segment */
362
363     if ( segment )
364     {
365         /* FIXME: This is somewhat ugly and relies on implementation
366                   details about 16-bit global memory handles ... */
367
368         LPBYTE oldBase = (LPBYTE)GetSelectorBase( segment );
369         memcpy( base, oldBase, segSize );
370         GLOBAL_MoveBlock( segment, base, totSize );
371         HeapFree( GetProcessHeap(), 0, oldBase );
372     }
373     
374     return (HANDLE)header;
375 }
376
377 /***********************************************************************
378  *           Local32_SearchHandle
379  */
380 static LPDWORD Local32_SearchHandle( LOCAL32HEADER *header, DWORD addr )
381 {
382     LPDWORD handle;
383
384     for ( handle = (LPDWORD)((LPBYTE)header + sizeof(LOCAL32HEADER));
385           handle < (LPDWORD)((LPBYTE)header + header->limit);
386           handle++)
387     {
388         if (*handle == addr)
389             return handle;
390     }
391
392     return NULL;
393 }
394
395 /***********************************************************************
396  *           Local32_ToHandle
397  */
398 static VOID Local32_ToHandle( LOCAL32HEADER *header, INT16 type, 
399                               DWORD addr, LPDWORD *handle, LPBYTE *ptr )
400 {
401     *handle = NULL;
402     *ptr    = NULL;
403
404     switch (type)
405     {
406         case -2:    /* 16:16 pointer, no handles */
407             *ptr    = MapSL( addr );
408             *handle = (LPDWORD)*ptr;
409             break;
410
411         case -1:    /* 32-bit offset, no handles */
412             *ptr    = header->base + addr;
413             *handle = (LPDWORD)*ptr;
414             break;
415
416         case 0:     /* handle */
417             if (    addr >= sizeof(LOCAL32HEADER) 
418                  && addr <  header->limit && !(addr & 3) 
419                  && *(LPDWORD)((LPBYTE)header + addr) >= HTABLE_SIZE )
420             {
421                 *handle = (LPDWORD)((LPBYTE)header + addr);
422                 *ptr    = header->base + **handle;
423             }
424             break;
425
426         case 1:     /* 16:16 pointer */
427             *ptr    = MapSL( addr );
428             *handle = Local32_SearchHandle( header, *ptr - header->base );
429             break;
430
431         case 2:     /* 32-bit offset */
432             *ptr    = header->base + addr;
433             *handle = Local32_SearchHandle( header, *ptr - header->base );
434             break;
435     }
436 }
437
438 /***********************************************************************
439  *           Local32_FromHandle
440  */
441 static VOID Local32_FromHandle( LOCAL32HEADER *header, INT16 type, 
442                                 DWORD *addr, LPDWORD handle, LPBYTE ptr )
443 {
444     switch (type)
445     {
446         case -2:    /* 16:16 pointer */
447         case  1:
448         {
449             WORD *selTable = (LPWORD)(header->base + header->selectorTableOffset);
450             DWORD offset   = (LPBYTE)ptr - header->base;
451             *addr = MAKELONG( offset & 0x7fff, selTable[offset >> 15] ); 
452         }
453         break;
454
455         case -1:    /* 32-bit offset */
456         case  2:
457             *addr = ptr - header->base;
458             break;
459
460         case  0:    /* handle */
461             *addr = (LPBYTE)handle - (LPBYTE)header;
462             break;
463     }
464 }
465
466 /***********************************************************************
467  *           K209   (KERNEL.209)
468  */
469 DWORD WINAPI Local32Alloc16( HANDLE heap, DWORD size, INT16 type, DWORD flags )
470 {
471     LOCAL32HEADER *header = (LOCAL32HEADER *)heap;
472     LPDWORD handle;
473     LPBYTE ptr;
474     DWORD addr;
475
476     /* Allocate memory */
477     ptr = HeapAlloc( header->heap, 
478                      (flags & LMEM_MOVEABLE)? HEAP_ZERO_MEMORY : 0, size );
479     if (!ptr) return 0;
480
481
482     /* Allocate handle if requested */
483     if (type >= 0)
484     {
485         int page, i;
486
487         /* Find first page of handle table with free slots */
488         for (page = 0; page < HTABLE_NPAGES; page++)
489             if (header->freeListFirst[page] != 0)
490                 break;
491         if (page == HTABLE_NPAGES)
492         {
493             WARN("Out of handles!\n" );
494             HeapFree( header->heap, 0, ptr );
495             return 0;
496         }
497
498         /* If virgin page, initialize it */
499         if (header->freeListFirst[page] == 0xffff)
500         {
501             if ( !VirtualAlloc( (LPBYTE)header + (page << 12), 
502                                 0x1000, MEM_COMMIT, PAGE_READWRITE ) )
503             {
504                 WARN("Cannot grow handle table!\n" );
505                 HeapFree( header->heap, 0, ptr );
506                 return 0;
507             }
508             
509             header->limit += HTABLE_PAGESIZE;
510
511             header->freeListFirst[page] = 0;
512             header->freeListLast[page]  = HTABLE_PAGESIZE - 4;
513             header->freeListSize[page]  = HTABLE_PAGESIZE / 4;
514            
515             for (i = 0; i < HTABLE_PAGESIZE; i += 4)
516                 *(DWORD *)((LPBYTE)header + i) = i+4;
517
518             if (page < HTABLE_NPAGES-1) 
519                 header->freeListFirst[page+1] = 0xffff;
520         }
521
522         /* Allocate handle slot from page */
523         handle = (LPDWORD)((LPBYTE)header + header->freeListFirst[page]);
524         if (--header->freeListSize[page] == 0)
525             header->freeListFirst[page] = header->freeListLast[page] = 0;
526         else
527             header->freeListFirst[page] = *handle;
528
529         /* Store 32-bit offset in handle slot */
530         *handle = ptr - header->base;
531     }
532     else
533     {
534         handle = (LPDWORD)ptr;
535         header->flags |= 1;
536     }
537
538
539     /* Convert handle to requested output type */
540     Local32_FromHandle( header, type, &addr, handle, ptr );
541     return addr;
542 }
543
544 /***********************************************************************
545  *           K210   (KERNEL.210)
546  */
547 DWORD WINAPI Local32ReAlloc16( HANDLE heap, DWORD addr, INT16 type,
548                              DWORD size, DWORD flags )
549 {
550     LOCAL32HEADER *header = (LOCAL32HEADER *)heap;
551     LPDWORD handle;
552     LPBYTE ptr;
553
554     if (!addr)
555         return Local32Alloc16( heap, size, type, flags );
556
557     /* Retrieve handle and pointer */
558     Local32_ToHandle( header, type, addr, &handle, &ptr );
559     if (!handle) return FALSE;
560
561     /* Reallocate memory block */
562     ptr = HeapReAlloc( header->heap, 
563                        (flags & LMEM_MOVEABLE)? HEAP_ZERO_MEMORY : 0, 
564                        ptr, size );
565     if (!ptr) return 0;
566
567     /* Modify handle */
568     if (type >= 0)
569         *handle = ptr - header->base;
570     else
571         handle = (LPDWORD)ptr;
572
573     /* Convert handle to requested output type */
574     Local32_FromHandle( header, type, &addr, handle, ptr );
575     return addr;
576 }
577
578 /***********************************************************************
579  *           K211   (KERNEL.211)
580  */
581 BOOL WINAPI Local32Free16( HANDLE heap, DWORD addr, INT16 type )
582 {
583     LOCAL32HEADER *header = (LOCAL32HEADER *)heap;
584     LPDWORD handle;
585     LPBYTE ptr;
586
587     /* Retrieve handle and pointer */
588     Local32_ToHandle( header, type, addr, &handle, &ptr );
589     if (!handle) return FALSE;
590
591     /* Free handle if necessary */
592     if (type >= 0)
593     {
594         int offset = (LPBYTE)handle - (LPBYTE)header;
595         int page   = offset >> 12;
596
597         /* Return handle slot to page free list */
598         if (header->freeListSize[page]++ == 0)
599             header->freeListFirst[page] = header->freeListLast[page]  = offset;
600         else
601             *(LPDWORD)((LPBYTE)header + header->freeListLast[page]) = offset,
602             header->freeListLast[page] = offset;
603
604         *handle = 0;
605
606         /* Shrink handle table when possible */
607         while (page > 0 && header->freeListSize[page] == HTABLE_PAGESIZE / 4)
608         {
609             if ( VirtualFree( (LPBYTE)header + 
610                               (header->limit & ~(HTABLE_PAGESIZE-1)),
611                               HTABLE_PAGESIZE, MEM_DECOMMIT ) )
612                 break;
613
614             header->limit -= HTABLE_PAGESIZE;
615             header->freeListFirst[page] = 0xffff;
616             page--;
617         }
618     }
619
620     /* Free memory */
621     return HeapFree( header->heap, 0, ptr );
622 }
623
624 /***********************************************************************
625  *           K213   (KERNEL.213)
626  */
627 DWORD WINAPI Local32Translate16( HANDLE heap, DWORD addr, INT16 type1, INT16 type2 )
628 {
629     LOCAL32HEADER *header = (LOCAL32HEADER *)heap;
630     LPDWORD handle;
631     LPBYTE ptr;
632
633     Local32_ToHandle( header, type1, addr, &handle, &ptr );
634     if (!handle) return 0;
635
636     Local32_FromHandle( header, type2, &addr, handle, ptr );
637     return addr;
638 }
639
640 /***********************************************************************
641  *           K214   (KERNEL.214)
642  */
643 DWORD WINAPI Local32Size16( HANDLE heap, DWORD addr, INT16 type )
644 {
645     LOCAL32HEADER *header = (LOCAL32HEADER *)heap;
646     LPDWORD handle;
647     LPBYTE ptr;
648
649     Local32_ToHandle( header, type, addr, &handle, &ptr );
650     if (!handle) return 0;
651
652     return HeapSize( header->heap, 0, ptr );
653 }
654
655 /***********************************************************************
656  *           K215   (KERNEL.215)
657  */
658 BOOL WINAPI Local32ValidHandle16( HANDLE heap, WORD addr )
659 {
660     LOCAL32HEADER *header = (LOCAL32HEADER *)heap;
661     LPDWORD handle;
662     LPBYTE ptr;
663
664     Local32_ToHandle( header, 0, addr, &handle, &ptr );
665     return handle != NULL;
666 }
667
668 /***********************************************************************
669  *           K229   (KERNEL.229)
670  */
671 WORD WINAPI Local32GetSegment16( HANDLE heap )
672 {
673     LOCAL32HEADER *header = (LOCAL32HEADER *)heap;
674     return header->segment;
675 }
676
677 /***********************************************************************
678  *           Local32_GetHeap
679  */
680 static LOCAL32HEADER *Local32_GetHeap( HGLOBAL16 handle )
681 {
682     WORD selector = GlobalHandleToSel16( handle );
683     DWORD base  = GetSelectorBase( selector ); 
684     DWORD limit = GetSelectorLimit16( selector ); 
685
686     /* Hmmm. This is a somewhat stupid heuristic, but Windows 95 does
687        it this way ... */
688
689     if ( limit > 0x10000 && ((LOCAL32HEADER *)base)->magic == LOCAL32_MAGIC )
690         return (LOCAL32HEADER *)base;
691
692     base  += 0x10000;
693     limit -= 0x10000;
694
695     if ( limit > 0x10000 && ((LOCAL32HEADER *)base)->magic == LOCAL32_MAGIC )
696         return (LOCAL32HEADER *)base;
697
698     return NULL;
699 }
700
701 /***********************************************************************
702  *           Local32Info   (KERNEL.444)
703  *           Local32Info   (TOOLHELP.84)
704  */
705 BOOL16 WINAPI Local32Info16( LOCAL32INFO *pLocal32Info, HGLOBAL16 handle )
706 {
707     PROCESS_HEAP_ENTRY entry;
708     int i;
709
710     LOCAL32HEADER *header = Local32_GetHeap( handle );
711     if ( !header ) return FALSE;
712
713     if ( !pLocal32Info || pLocal32Info->dwSize < sizeof(LOCAL32INFO) )
714         return FALSE;
715
716     pLocal32Info->dwMemReserved = 0;
717     pLocal32Info->dwMemCommitted = 0;
718     pLocal32Info->dwTotalFree = 0;
719     pLocal32Info->dwLargestFreeBlock = 0;
720
721     while (HeapWalk( header->heap, &entry ))
722     {
723         if (entry.wFlags & PROCESS_HEAP_REGION)
724         {
725             pLocal32Info->dwMemReserved += entry.u.Region.dwCommittedSize
726                                            + entry.u.Region.dwUnCommittedSize;
727             pLocal32Info->dwMemCommitted = entry.u.Region.dwCommittedSize;
728         }
729         else if (!(entry.wFlags & PROCESS_HEAP_ENTRY_BUSY))
730         {
731             DWORD size = entry.cbData + entry.cbOverhead;
732             pLocal32Info->dwTotalFree += size;
733             if (size > pLocal32Info->dwLargestFreeBlock) pLocal32Info->dwLargestFreeBlock = size;
734         }
735     }
736
737     pLocal32Info->dwcFreeHandles = 0;
738     for ( i = 0; i < HTABLE_NPAGES; i++ )
739     {
740         if ( header->freeListFirst[i] == 0xffff ) break;
741         pLocal32Info->dwcFreeHandles += header->freeListSize[i];
742     }
743     pLocal32Info->dwcFreeHandles += (HTABLE_NPAGES - i) * HTABLE_PAGESIZE/4;
744
745     return TRUE;
746 }
747
748 /***********************************************************************
749  *           Local32First   (KERNEL.445)
750  *           Local32First   (TOOLHELP.85)
751  */
752 BOOL16 WINAPI Local32First16( LOCAL32ENTRY *pLocal32Entry, HGLOBAL16 handle )
753 {
754     FIXME("(%p, %04X): stub!\n", pLocal32Entry, handle );
755     return FALSE;
756 }
757
758 /***********************************************************************
759  *           Local32Next   (KERNEL.446)
760  *           Local32Next   (TOOLHELP.86)
761  */
762 BOOL16 WINAPI Local32Next16( LOCAL32ENTRY *pLocal32Entry )
763 {
764     FIXME("(%p): stub!\n", pLocal32Entry );
765     return FALSE;
766 }
767