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