msxml3: Implement comment node.
[wine] / dlls / kernel / heap.c
1 /*
2  * Win32 heap functions
3  *
4  * Copyright 1995, 1996 Alexandre Julliard
5  * Copyright 1996 Huw Davies
6  * Copyright 1998 Ulrich Weigand
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include "config.h"
24 #include "wine/port.h"
25
26 #include <assert.h>
27 #include <stdlib.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <sys/types.h>
32 #include <time.h>
33 #ifdef HAVE_SYS_PARAM_H
34 #include <sys/param.h>
35 #endif
36 #ifdef HAVE_SYS_SYSCTL_H
37 #include <sys/sysctl.h>
38 #endif
39 #ifdef HAVE_UNISTD_H
40 # include <unistd.h>
41 #endif
42
43 #ifdef sun
44 /* FIXME:  Unfortunately swapctl can't be used with largefile.... */
45 # undef _FILE_OFFSET_BITS
46 # define _FILE_OFFSET_BITS 32
47 # include <sys/resource.h>
48 # ifdef HAVE_SYS_STAT_H
49 #  include <sys/stat.h>
50 # endif
51 # include <sys/swap.h>
52 #endif
53
54
55 #include "windef.h"
56 #include "winbase.h"
57 #include "winerror.h"
58 #include "winnt.h"
59 #include "winternl.h"
60 #include "excpt.h"
61 #include "wine/exception.h"
62 #include "wine/debug.h"
63
64 WINE_DEFAULT_DEBUG_CHANNEL(heap);
65
66 /* address where we try to map the system heap */
67 #define SYSTEM_HEAP_BASE  ((void*)0x80000000)
68 #define SYSTEM_HEAP_SIZE  0x1000000   /* Default heap size = 16Mb */
69
70 static HANDLE systemHeap;   /* globally shared heap */
71
72
73 /***********************************************************************
74  *           HEAP_CreateSystemHeap
75  *
76  * Create the system heap.
77  */
78 inline static HANDLE HEAP_CreateSystemHeap(void)
79 {
80     int created;
81     void *base;
82     HANDLE map, event;
83
84     /* create the system heap event first */
85     event = CreateEventA( NULL, TRUE, FALSE, "__wine_system_heap_event" );
86
87     if (!(map = CreateFileMappingA( INVALID_HANDLE_VALUE, NULL, SEC_COMMIT | PAGE_READWRITE,
88                                     0, SYSTEM_HEAP_SIZE, "__wine_system_heap" ))) return 0;
89     created = (GetLastError() != ERROR_ALREADY_EXISTS);
90
91     if (!(base = MapViewOfFileEx( map, FILE_MAP_ALL_ACCESS, 0, 0, 0, SYSTEM_HEAP_BASE )))
92     {
93         /* pre-defined address not available */
94         ERR( "system heap base address %p not available\n", SYSTEM_HEAP_BASE );
95         return 0;
96     }
97
98     if (created)  /* newly created heap */
99     {
100         systemHeap = RtlCreateHeap( HEAP_SHARED, base, SYSTEM_HEAP_SIZE,
101                                     SYSTEM_HEAP_SIZE, NULL, NULL );
102         SetEvent( event );
103     }
104     else
105     {
106         /* wait for the heap to be initialized */
107         WaitForSingleObject( event, INFINITE );
108         systemHeap = (HANDLE)base;
109     }
110     CloseHandle( map );
111     return systemHeap;
112 }
113
114
115 /***********************************************************************
116  *           HeapCreate   (KERNEL32.@)
117  *
118  * Create a heap object.
119  *
120  * RETURNS
121  *      Handle of heap: Success
122  *      NULL: Failure
123  */
124 HANDLE WINAPI HeapCreate(
125                 DWORD flags,       /* [in] Heap allocation flag */
126                 SIZE_T initialSize, /* [in] Initial heap size */
127                 SIZE_T maxSize      /* [in] Maximum heap size */
128 ) {
129     HANDLE ret;
130
131     if ( flags & HEAP_SHARED )
132     {
133         if (!systemHeap) HEAP_CreateSystemHeap();
134         else WARN( "Shared Heap requested, returning system heap.\n" );
135         ret = systemHeap;
136     }
137     else
138     {
139         ret = RtlCreateHeap( flags, NULL, maxSize, initialSize, NULL, NULL );
140         if (!ret) SetLastError( ERROR_NOT_ENOUGH_MEMORY );
141     }
142     return ret;
143 }
144
145
146 /***********************************************************************
147  *           HeapDestroy   (KERNEL32.@)
148  *
149  * Destroy a heap object.
150  *
151  * RETURNS
152  *      TRUE: Success
153  *      FALSE: Failure
154  */
155 BOOL WINAPI HeapDestroy( HANDLE heap /* [in] Handle of heap */ )
156 {
157     if (heap == systemHeap)
158     {
159         WARN( "attempt to destroy system heap, returning TRUE!\n" );
160         return TRUE;
161     }
162     if (!RtlDestroyHeap( heap )) return TRUE;
163     SetLastError( ERROR_INVALID_HANDLE );
164     return FALSE;
165 }
166
167
168 /***********************************************************************
169  *           HeapCompact   (KERNEL32.@)
170  */
171 SIZE_T WINAPI HeapCompact( HANDLE heap, DWORD flags )
172 {
173     return RtlCompactHeap( heap, flags );
174 }
175
176
177 /***********************************************************************
178  *           HeapValidate   (KERNEL32.@)
179  * Validates a specified heap.
180  *
181  * NOTES
182  *      Flags is ignored.
183  *
184  * RETURNS
185  *      TRUE: Success
186  *      FALSE: Failure
187  */
188 BOOL WINAPI HeapValidate(
189               HANDLE heap, /* [in] Handle to the heap */
190               DWORD flags,   /* [in] Bit flags that control access during operation */
191               LPCVOID block  /* [in] Optional pointer to memory block to validate */
192 ) {
193     return RtlValidateHeap( heap, flags, block );
194 }
195
196
197 /***********************************************************************
198  *           HeapWalk   (KERNEL32.@)
199  * Enumerates the memory blocks in a specified heap.
200  *
201  * TODO
202  *   - handling of PROCESS_HEAP_ENTRY_MOVEABLE and
203  *     PROCESS_HEAP_ENTRY_DDESHARE (needs heap.c support)
204  *
205  * RETURNS
206  *      TRUE: Success
207  *      FALSE: Failure
208  */
209 BOOL WINAPI HeapWalk(
210               HANDLE heap,               /* [in]  Handle to heap to enumerate */
211               LPPROCESS_HEAP_ENTRY entry /* [out] Pointer to structure of enumeration info */
212 ) {
213     NTSTATUS ret = RtlWalkHeap( heap, entry );
214     if (ret) SetLastError( RtlNtStatusToDosError(ret) );
215     return !ret;
216 }
217
218
219 /***********************************************************************
220  *           HeapLock   (KERNEL32.@)
221  * Attempts to acquire the critical section object for a specified heap.
222  *
223  * RETURNS
224  *      TRUE: Success
225  *      FALSE: Failure
226  */
227 BOOL WINAPI HeapLock(
228               HANDLE heap /* [in] Handle of heap to lock for exclusive access */
229 ) {
230     return RtlLockHeap( heap );
231 }
232
233
234 /***********************************************************************
235  *           HeapUnlock   (KERNEL32.@)
236  * Releases ownership of the critical section object.
237  *
238  * RETURNS
239  *      TRUE: Success
240  *      FALSE: Failure
241  */
242 BOOL WINAPI HeapUnlock(
243               HANDLE heap /* [in] Handle to the heap to unlock */
244 ) {
245     return RtlUnlockHeap( heap );
246 }
247
248
249 /***********************************************************************
250  *           GetProcessHeap    (KERNEL32.@)
251  */
252 HANDLE WINAPI GetProcessHeap(void)
253 {
254     return NtCurrentTeb()->Peb->ProcessHeap;
255 }
256
257
258 /***********************************************************************
259  *           GetProcessHeaps    (KERNEL32.@)
260  */
261 DWORD WINAPI GetProcessHeaps( DWORD count, HANDLE *heaps )
262 {
263     return RtlGetProcessHeaps( count, heaps );
264 }
265
266
267 /* These are needed so that we can call the functions from inside kernel itself */
268
269 LPVOID WINAPI HeapAlloc( HANDLE heap, DWORD flags, SIZE_T size )
270 {
271     return RtlAllocateHeap( heap, flags, size );
272 }
273
274 BOOL WINAPI HeapFree( HANDLE heap, DWORD flags, LPVOID ptr )
275 {
276     return RtlFreeHeap( heap, flags, ptr );
277 }
278
279 LPVOID WINAPI HeapReAlloc( HANDLE heap, DWORD flags, LPVOID ptr, SIZE_T size )
280 {
281     return RtlReAllocateHeap( heap, flags, ptr, size );
282 }
283
284 SIZE_T WINAPI HeapSize( HANDLE heap, DWORD flags, LPVOID ptr )
285 {
286     return RtlSizeHeap( heap, flags, ptr );
287 }
288
289 /*
290  * Win32 Global heap functions (GlobalXXX).
291  * These functions included in Win32 for compatibility with 16 bit Windows
292  * Especially the moveable blocks and handles are oldish.
293  * But the ability to directly allocate memory with GPTR and LPTR is widely
294  * used.
295  *
296  * The handle stuff looks horrible, but it's implemented almost like Win95
297  * does it.
298  *
299  */
300
301 #define MAGIC_GLOBAL_USED 0x5342
302 #define HANDLE_TO_INTERN(h)  ((PGLOBAL32_INTERN)(((char *)(h))-2))
303 #define INTERN_TO_HANDLE(i)  ((HGLOBAL) &((i)->Pointer))
304 #define POINTER_TO_HANDLE(p) (*(((HGLOBAL *)(p))-2))
305 #define ISHANDLE(h)          (((ULONG_PTR)(h)&2)!=0)
306 #define ISPOINTER(h)         (((ULONG_PTR)(h)&2)==0)
307 /* align the storage needed for the HGLOBAL on an 8byte boundary thus
308  * GlobalAlloc/GlobalReAlloc'ing with GMEM_MOVEABLE of memory with
309  * size = 8*k, where k=1,2,3,... alloc's exactly the given size.
310  * The Minolta DiMAGE Image Viewer heavily relies on this, corrupting
311  * the output jpeg's > 1 MB if not */
312 #define HGLOBAL_STORAGE      8  /* sizeof(HGLOBAL)*2 */
313
314 #include "pshpack1.h"
315
316 typedef struct __GLOBAL32_INTERN
317 {
318    WORD         Magic;
319    LPVOID       Pointer;
320    BYTE         Flags;
321    BYTE         LockCount;
322 } GLOBAL32_INTERN, *PGLOBAL32_INTERN;
323
324 #include "poppack.h"
325
326 /***********************************************************************
327  *           GlobalAlloc   (KERNEL32.@)
328  *
329  * Allocate a global memory object.
330  *
331  * RETURNS
332  *      Handle: Success
333  *      NULL: Failure
334  */
335 HGLOBAL WINAPI GlobalAlloc(
336                  UINT flags, /* [in] Object allocation attributes */
337                  SIZE_T size /* [in] Number of bytes to allocate */
338 ) {
339    PGLOBAL32_INTERN     pintern;
340    DWORD                hpflags;
341    LPVOID               palloc;
342
343    if(flags&GMEM_ZEROINIT)
344       hpflags=HEAP_ZERO_MEMORY;
345    else
346       hpflags=0;
347
348    TRACE("() flags=%04x\n",  flags );
349
350    if((flags & GMEM_MOVEABLE)==0) /* POINTER */
351    {
352       palloc=HeapAlloc(GetProcessHeap(), hpflags, size);
353       return (HGLOBAL) palloc;
354    }
355    else  /* HANDLE */
356    {
357       RtlLockHeap(GetProcessHeap());
358
359       pintern = HeapAlloc(GetProcessHeap(), 0, sizeof(GLOBAL32_INTERN));
360       if (pintern)
361       {
362           pintern->Magic = MAGIC_GLOBAL_USED;
363           pintern->Flags = flags >> 8;
364           pintern->LockCount = 0;
365
366           if (size)
367           {
368               palloc = HeapAlloc(GetProcessHeap(), hpflags, size+HGLOBAL_STORAGE);
369               if (!palloc)
370               {
371                   HeapFree(GetProcessHeap(), 0, pintern);
372                   pintern = NULL;
373               }
374               else
375               {
376                   *(HGLOBAL *)palloc = INTERN_TO_HANDLE(pintern);
377                   pintern->Pointer = (char *)palloc + HGLOBAL_STORAGE;
378               }
379           }
380           else
381               pintern->Pointer = NULL;
382       }
383
384       RtlUnlockHeap(GetProcessHeap());
385       return pintern ? INTERN_TO_HANDLE(pintern) : 0;
386    }
387 }
388
389
390 /***********************************************************************
391  *           GlobalLock   (KERNEL32.@)
392  *
393  * Lock a global memory object.
394  *
395  * RETURNS
396  *      Pointer to first byte of block
397  *      NULL: Failure
398  */
399 LPVOID WINAPI GlobalLock(
400               HGLOBAL hmem /* [in] Handle of global memory object */
401 )
402 {
403     PGLOBAL32_INTERN pintern;
404     LPVOID           palloc;
405
406     if (ISPOINTER(hmem))
407         return IsBadReadPtr(hmem, 1) ? NULL : hmem;
408
409     RtlLockHeap(GetProcessHeap());
410     __TRY
411     {
412         pintern = HANDLE_TO_INTERN(hmem);
413         if (pintern->Magic == MAGIC_GLOBAL_USED)
414         {
415             palloc = pintern->Pointer;
416             if (!pintern->Pointer)
417                 SetLastError(ERROR_DISCARDED);
418             else if (pintern->LockCount < GMEM_LOCKCOUNT)
419                 pintern->LockCount++;
420         }
421         else
422         {
423             WARN("invalid handle %p\n", hmem);
424             palloc = NULL;
425             SetLastError(ERROR_INVALID_HANDLE);
426         }
427     }
428     __EXCEPT_PAGE_FAULT
429     {
430         WARN("page fault on %p\n", hmem);
431         palloc = NULL;
432         SetLastError(ERROR_INVALID_HANDLE);
433     }
434     __ENDTRY
435     RtlUnlockHeap(GetProcessHeap());
436     return palloc;
437 }
438
439
440 /***********************************************************************
441  *           GlobalUnlock   (KERNEL32.@)
442  *
443  * Unlock a global memory object.
444  *
445  * RETURNS
446  *      TRUE: Object is still locked
447  *      FALSE: Object is unlocked
448  */
449 BOOL WINAPI GlobalUnlock(
450               HGLOBAL hmem /* [in] Handle of global memory object */
451 ) {
452     PGLOBAL32_INTERN pintern;
453     BOOL locked;
454
455     if (ISPOINTER(hmem)) return FALSE;
456
457     RtlLockHeap(GetProcessHeap());
458     __TRY
459     {
460         pintern=HANDLE_TO_INTERN(hmem);
461         if(pintern->Magic==MAGIC_GLOBAL_USED)
462         {
463             if(pintern->LockCount)
464             {
465                 pintern->LockCount--;
466                 locked = (pintern->LockCount != 0);
467                 if (!locked) SetLastError(NO_ERROR);
468             }
469             else
470             {
471                 WARN("%p not locked\n", hmem);
472                 SetLastError(ERROR_NOT_LOCKED);
473                 locked = FALSE;
474             }
475         }
476         else
477         {
478             WARN("invalid handle\n");
479             SetLastError(ERROR_INVALID_HANDLE);
480             locked=FALSE;
481         }
482     }
483     __EXCEPT_PAGE_FAULT
484     {
485         ERR("page fault occurred ! Caused by bug ?\n");
486         SetLastError( ERROR_INVALID_PARAMETER );
487         locked=FALSE;
488     }
489     __ENDTRY
490     RtlUnlockHeap(GetProcessHeap());
491     return locked;
492 }
493
494
495 /***********************************************************************
496  *           GlobalHandle   (KERNEL32.@)
497  *
498  * Get the handle associated with the pointer to a global memory block.
499  *
500  * RETURNS
501  *      Handle: Success
502  *      NULL: Failure
503  */
504 HGLOBAL WINAPI GlobalHandle(
505                  LPCVOID pmem /* [in] Pointer to global memory block */
506 ) {
507     HGLOBAL handle;
508     PGLOBAL32_INTERN  maybe_intern;
509     LPCVOID test;
510
511     if (!pmem)
512     {
513         SetLastError( ERROR_INVALID_PARAMETER );
514         return 0;
515     }
516
517     RtlLockHeap(GetProcessHeap());
518     __TRY
519     {
520         handle = 0;
521
522         /* note that if pmem is a pointer to a a block allocated by        */
523         /* GlobalAlloc with GMEM_MOVEABLE then magic test in HeapValidate  */
524         /* will fail.                                                      */
525         if (ISPOINTER(pmem)) {
526             if (HeapValidate( GetProcessHeap(), 0, pmem )) {
527                 handle = (HGLOBAL)pmem;  /* valid fixed block */
528                 break;
529             }
530             handle = POINTER_TO_HANDLE(pmem);
531         } else
532             handle = (HGLOBAL)pmem;
533
534         /* Now test handle either passed in or retrieved from pointer */
535         maybe_intern = HANDLE_TO_INTERN( handle );
536         if (maybe_intern->Magic == MAGIC_GLOBAL_USED) {
537             test = maybe_intern->Pointer;
538             if (HeapValidate( GetProcessHeap(), 0, (const char *)test - HGLOBAL_STORAGE ) && /* obj(-handle) valid arena? */
539                 HeapValidate( GetProcessHeap(), 0, maybe_intern ))  /* intern valid arena? */
540                 break;  /* valid moveable block */
541         }
542         handle = 0;
543         SetLastError( ERROR_INVALID_HANDLE );
544     }
545     __EXCEPT_PAGE_FAULT
546     {
547         SetLastError( ERROR_INVALID_HANDLE );
548         handle = 0;
549     }
550     __ENDTRY
551     RtlUnlockHeap(GetProcessHeap());
552
553     return handle;
554 }
555
556
557 /***********************************************************************
558  *           GlobalReAlloc   (KERNEL32.@)
559  *
560  * Change the size or attributes of a global memory object.
561  *
562  * RETURNS
563  *      Handle: Success
564  *      NULL: Failure
565  */
566 HGLOBAL WINAPI GlobalReAlloc(
567                  HGLOBAL hmem, /* [in] Handle of global memory object */
568                  SIZE_T size,  /* [in] New size of block */
569                  UINT flags    /* [in] How to reallocate object */
570 ) {
571    LPVOID               palloc;
572    HGLOBAL            hnew;
573    PGLOBAL32_INTERN     pintern;
574    DWORD heap_flags = (flags & GMEM_ZEROINIT) ? HEAP_ZERO_MEMORY : 0;
575
576    hnew = 0;
577    RtlLockHeap(GetProcessHeap());
578    if(flags & GMEM_MODIFY) /* modify flags */
579    {
580       if( ISPOINTER(hmem) && (flags & GMEM_MOVEABLE))
581       {
582          /* make a fixed block moveable
583           * actually only NT is able to do this. But it's soo simple
584           */
585          if (hmem == 0)
586          {
587              WARN("GlobalReAlloc with null handle!\n");
588              SetLastError( ERROR_NOACCESS );
589              hnew = 0;
590          }
591          else
592          {
593              size = HeapSize(GetProcessHeap(), 0, (LPVOID)hmem);
594              hnew = GlobalAlloc(flags, size);
595              palloc = GlobalLock(hnew);
596              memcpy(palloc, (LPVOID)hmem, size);
597              GlobalUnlock(hnew);
598              GlobalFree(hmem);
599          }
600       }
601       else if( ISPOINTER(hmem) &&(flags & GMEM_DISCARDABLE))
602       {
603          /* change the flags to make our block "discardable" */
604          pintern=HANDLE_TO_INTERN(hmem);
605          pintern->Flags = pintern->Flags | (GMEM_DISCARDABLE >> 8);
606          hnew=hmem;
607       }
608       else
609       {
610          SetLastError(ERROR_INVALID_PARAMETER);
611          hnew = 0;
612       }
613    }
614    else
615    {
616       if(ISPOINTER(hmem))
617       {
618          /* reallocate fixed memory */
619          hnew=HeapReAlloc(GetProcessHeap(), heap_flags, hmem, size);
620       }
621       else
622       {
623          /* reallocate a moveable block */
624          pintern=HANDLE_TO_INTERN(hmem);
625
626 #if 0
627 /* Apparently Windows doesn't care whether the handle is locked at this point */
628 /* See also the same comment in GlobalFree() */
629          if(pintern->LockCount>1) {
630             ERR("handle 0x%08lx is still locked, cannot realloc!\n",(DWORD)hmem);
631             SetLastError(ERROR_INVALID_HANDLE);
632          } else
633 #endif
634          if(size!=0)
635          {
636             hnew=hmem;
637             if(pintern->Pointer)
638             {
639                if((palloc = HeapReAlloc(GetProcessHeap(), heap_flags,
640                                    (char *) pintern->Pointer-HGLOBAL_STORAGE,
641                                    size+HGLOBAL_STORAGE)) == NULL)
642                    hnew = 0; /* Block still valid */
643                else
644                    pintern->Pointer = (char *)palloc+HGLOBAL_STORAGE;
645             }
646             else
647             {
648                 if((palloc=HeapAlloc(GetProcessHeap(), heap_flags, size+HGLOBAL_STORAGE))
649                    == NULL)
650                     hnew = 0;
651                 else
652                 {
653                     *(HGLOBAL *)palloc = hmem;
654                     pintern->Pointer = (char *)palloc + HGLOBAL_STORAGE;
655                 }
656             }
657          }
658          else
659          {
660             if (pintern->LockCount == 0)
661             {
662                 if(pintern->Pointer)
663                 {
664                     HeapFree(GetProcessHeap(), 0, (char *) pintern->Pointer-HGLOBAL_STORAGE);
665                     pintern->Pointer = NULL;
666                 }
667                 hnew = hmem;
668             }
669             else
670                 WARN("not freeing memory associated with locked handle\n");
671          }
672       }
673    }
674    RtlUnlockHeap(GetProcessHeap());
675    return hnew;
676 }
677
678
679 /***********************************************************************
680  *           GlobalFree   (KERNEL32.@)
681  *
682  * Free a global memory object.
683  *
684  * RETURNS
685  *      NULL: Success
686  *      Handle: Failure
687  */
688 HGLOBAL WINAPI GlobalFree(
689                  HGLOBAL hmem /* [in] Handle of global memory object */
690 ) {
691     PGLOBAL32_INTERN pintern;
692     HGLOBAL hreturned;
693
694     RtlLockHeap(GetProcessHeap());
695     __TRY
696     {
697         hreturned = 0;
698         if(ISPOINTER(hmem)) /* POINTER */
699         {
700             if(!HeapFree(GetProcessHeap(), 0, (LPVOID) hmem)) hmem = 0;
701         }
702         else  /* HANDLE */
703         {
704             pintern=HANDLE_TO_INTERN(hmem);
705
706             if(pintern->Magic==MAGIC_GLOBAL_USED)
707             {
708
709                 /* WIN98 does not make this test. That is you can free a */
710                 /* block you have not unlocked. Go figure!!              */
711                 /* if(pintern->LockCount!=0)  */
712                 /*    SetLastError(ERROR_INVALID_HANDLE);  */
713
714                 if(pintern->Pointer)
715                     if(!HeapFree(GetProcessHeap(), 0, (char *)(pintern->Pointer)-HGLOBAL_STORAGE))
716                         hreturned=hmem;
717                 if(!HeapFree(GetProcessHeap(), 0, pintern))
718                     hreturned=hmem;
719             }
720         }
721     }
722     __EXCEPT_PAGE_FAULT
723     {
724         ERR("page fault occurred ! Caused by bug ?\n");
725         SetLastError( ERROR_INVALID_PARAMETER );
726         hreturned = hmem;
727     }
728     __ENDTRY
729     RtlUnlockHeap(GetProcessHeap());
730     return hreturned;
731 }
732
733
734 /***********************************************************************
735  *           GlobalSize   (KERNEL32.@)
736  *
737  * Get the size of a global memory object.
738  *
739  * RETURNS
740  *      Size in bytes of the global memory object
741  *      0: Failure
742  */
743 SIZE_T WINAPI GlobalSize(
744              HGLOBAL hmem /* [in] Handle of global memory object */
745 ) {
746    DWORD                retval;
747    PGLOBAL32_INTERN     pintern;
748
749    if (!hmem) return 0;
750
751    if(ISPOINTER(hmem))
752    {
753       retval=HeapSize(GetProcessHeap(), 0,  (LPVOID) hmem);
754    }
755    else
756    {
757       RtlLockHeap(GetProcessHeap());
758       pintern=HANDLE_TO_INTERN(hmem);
759
760       if(pintern->Magic==MAGIC_GLOBAL_USED)
761       {
762          if (!pintern->Pointer) /* handle case of GlobalAlloc( ??,0) */
763              retval = 0;
764          else
765          {
766              retval = HeapSize(GetProcessHeap(), 0,
767                          (char *)(pintern->Pointer) - HGLOBAL_STORAGE );
768              if (retval != (DWORD)-1) retval -= HGLOBAL_STORAGE;
769          }
770       }
771       else
772       {
773          WARN("invalid handle\n");
774          retval=0;
775       }
776       RtlUnlockHeap(GetProcessHeap());
777    }
778    /* HeapSize returns 0xffffffff on failure */
779    if (retval == 0xffffffff) retval = 0;
780    return retval;
781 }
782
783
784 /***********************************************************************
785  *           GlobalWire   (KERNEL32.@)
786  */
787 LPVOID WINAPI GlobalWire(HGLOBAL hmem)
788 {
789    return GlobalLock( hmem );
790 }
791
792
793 /***********************************************************************
794  *           GlobalUnWire   (KERNEL32.@)
795  */
796 BOOL WINAPI GlobalUnWire(HGLOBAL hmem)
797 {
798    return GlobalUnlock( hmem);
799 }
800
801
802 /***********************************************************************
803  *           GlobalFix   (KERNEL32.@)
804  */
805 VOID WINAPI GlobalFix(HGLOBAL hmem)
806 {
807     GlobalLock( hmem );
808 }
809
810
811 /***********************************************************************
812  *           GlobalUnfix   (KERNEL32.@)
813  */
814 VOID WINAPI GlobalUnfix(HGLOBAL hmem)
815 {
816    GlobalUnlock( hmem);
817 }
818
819
820 /***********************************************************************
821  *           GlobalFlags   (KERNEL32.@)
822  *
823  * Get information about a global memory object.
824  *
825  * NOTES
826  *      Should this return GMEM_INVALID_HANDLE on invalid handle?
827  *
828  * RETURNS
829  *      Value specifying allocation flags and lock count
830  *      GMEM_INVALID_HANDLE: Failure
831  */
832 UINT WINAPI GlobalFlags(
833               HGLOBAL hmem /* [in] Handle to global memory object */
834 ) {
835    DWORD                retval;
836    PGLOBAL32_INTERN     pintern;
837
838    if(ISPOINTER(hmem))
839    {
840       retval=0;
841    }
842    else
843    {
844       RtlLockHeap(GetProcessHeap());
845       pintern=HANDLE_TO_INTERN(hmem);
846       if(pintern->Magic==MAGIC_GLOBAL_USED)
847       {
848          retval=pintern->LockCount + (pintern->Flags<<8);
849          if(pintern->Pointer==0)
850             retval|= GMEM_DISCARDED;
851       }
852       else
853       {
854          WARN("Invalid handle: %p\n", hmem);
855          retval=0;
856       }
857       RtlUnlockHeap(GetProcessHeap());
858    }
859    return retval;
860 }
861
862
863 /***********************************************************************
864  *           GlobalCompact   (KERNEL32.@)
865  */
866 SIZE_T WINAPI GlobalCompact( DWORD minfree )
867 {
868     return 0;  /* GlobalCompact does nothing in Win32 */
869 }
870
871
872 /***********************************************************************
873  *           LocalAlloc   (KERNEL32.@)
874  *
875  * Allocate a local memory object.
876  *
877  * RETURNS
878  *      Handle: Success
879  *      NULL: Failure
880  *
881  * NOTES
882  *  Windows memory management does not provide a separate local heap
883  *  and global heap.
884  */
885 HLOCAL WINAPI LocalAlloc(
886                 UINT flags, /* [in] Allocation attributes */
887                 SIZE_T size /* [in] Number of bytes to allocate */
888 ) {
889     return (HLOCAL)GlobalAlloc( flags, size );
890 }
891
892
893 /***********************************************************************
894  *           LocalCompact   (KERNEL32.@)
895  */
896 SIZE_T WINAPI LocalCompact( UINT minfree )
897 {
898     return 0;  /* LocalCompact does nothing in Win32 */
899 }
900
901
902 /***********************************************************************
903  *           LocalFlags   (KERNEL32.@)
904  *
905  * Get information about a local memory object.
906  *
907  * RETURNS
908  *      Value specifying allocation flags and lock count.
909  *      LMEM_INVALID_HANDLE: Failure
910  *
911  * NOTES
912  *  Windows memory management does not provide a separate local heap
913  *  and global heap.
914  */
915 UINT WINAPI LocalFlags(
916               HLOCAL handle /* [in] Handle of memory object */
917 ) {
918     return GlobalFlags( (HGLOBAL)handle );
919 }
920
921
922 /***********************************************************************
923  *           LocalFree   (KERNEL32.@)
924  *
925  * Free a local memory object.
926  *
927  * RETURNS
928  *      NULL: Success
929  *      Handle: Failure
930  *
931  * NOTES
932  *  Windows memory management does not provide a separate local heap
933  *  and global heap.
934  */
935 HLOCAL WINAPI LocalFree(
936                 HLOCAL handle /* [in] Handle of memory object */
937 ) {
938     return (HLOCAL)GlobalFree( (HGLOBAL)handle );
939 }
940
941
942 /***********************************************************************
943  *           LocalHandle   (KERNEL32.@)
944  *
945  * Get the handle associated with the pointer to a local memory block.
946  *
947  * RETURNS
948  *      Handle: Success
949  *      NULL: Failure
950  *
951  * NOTES
952  *  Windows memory management does not provide a separate local heap
953  *  and global heap.
954  */
955 HLOCAL WINAPI LocalHandle(
956                 LPCVOID ptr /* [in] Address of local memory block */
957 ) {
958     return (HLOCAL)GlobalHandle( ptr );
959 }
960
961
962 /***********************************************************************
963  *           LocalLock   (KERNEL32.@)
964  * Locks a local memory object and returns pointer to the first byte
965  * of the memory block.
966  *
967  * RETURNS
968  *      Pointer: Success
969  *      NULL: Failure
970  *
971  * NOTES
972  *  Windows memory management does not provide a separate local heap
973  *  and global heap.
974  */
975 LPVOID WINAPI LocalLock(
976               HLOCAL handle /* [in] Address of local memory object */
977 ) {
978     return GlobalLock( (HGLOBAL)handle );
979 }
980
981
982 /***********************************************************************
983  *           LocalReAlloc   (KERNEL32.@)
984  *
985  * Change the size or attributes of a local memory object.
986  *
987  * RETURNS
988  *      Handle: Success
989  *      NULL: Failure
990  *
991  * NOTES
992  *  Windows memory management does not provide a separate local heap
993  *  and global heap.
994  */
995 HLOCAL WINAPI LocalReAlloc(
996                 HLOCAL handle, /* [in] Handle of memory object */
997                 SIZE_T size,   /* [in] New size of block */
998                 UINT flags     /* [in] How to reallocate object */
999 ) {
1000     return (HLOCAL)GlobalReAlloc( (HGLOBAL)handle, size, flags );
1001 }
1002
1003
1004 /***********************************************************************
1005  *           LocalShrink   (KERNEL32.@)
1006  */
1007 SIZE_T WINAPI LocalShrink( HGLOBAL handle, UINT newsize )
1008 {
1009     return 0;  /* LocalShrink does nothing in Win32 */
1010 }
1011
1012
1013 /***********************************************************************
1014  *           LocalSize   (KERNEL32.@)
1015  *
1016  * Get the size of a local memory object.
1017  *
1018  * RETURNS
1019  *      Size: Success
1020  *      0: Failure
1021  *
1022  * NOTES
1023  *  Windows memory management does not provide a separate local heap
1024  *  and global heap.
1025  */
1026 SIZE_T WINAPI LocalSize(
1027               HLOCAL handle /* [in] Handle of memory object */
1028 ) {
1029     return GlobalSize( (HGLOBAL)handle );
1030 }
1031
1032
1033 /***********************************************************************
1034  *           LocalUnlock   (KERNEL32.@)
1035  *
1036  * Unlock a local memory object.
1037  *
1038  * RETURNS
1039  *      TRUE: Object is still locked
1040  *      FALSE: Object is unlocked
1041  *
1042  * NOTES
1043  *  Windows memory management does not provide a separate local heap
1044  *  and global heap.
1045  */
1046 BOOL WINAPI LocalUnlock(
1047               HLOCAL handle /* [in] Handle of memory object */
1048 ) {
1049     return GlobalUnlock( (HGLOBAL)handle );
1050 }
1051
1052
1053 /**********************************************************************
1054  *              AllocMappedBuffer       (KERNEL32.38)
1055  *
1056  * This is an undocumented KERNEL32 function that
1057  * SMapLS's a GlobalAlloc'ed buffer.
1058  *
1059  * RETURNS
1060  *       EDI register: pointer to buffer
1061  *
1062  * NOTES
1063  *       The buffer is preceded by 8 bytes:
1064  *        ...
1065  *       edi+0   buffer
1066  *       edi-4   SEGPTR to buffer
1067  *       edi-8   some magic Win95 needs for SUnMapLS
1068  *               (we use it for the memory handle)
1069  *
1070  *       The SEGPTR is used by the caller!
1071  */
1072 void WINAPI __regs_AllocMappedBuffer(
1073               CONTEXT86 *context /* [in] EDI register: size of buffer to allocate */
1074 ) {
1075     HGLOBAL handle = GlobalAlloc(0, context->Edi + 8);
1076     DWORD *buffer = (DWORD *)GlobalLock(handle);
1077     DWORD ptr = 0;
1078
1079     if (buffer)
1080         if (!(ptr = MapLS(buffer + 2)))
1081         {
1082             GlobalUnlock(handle);
1083             GlobalFree(handle);
1084         }
1085
1086     if (!ptr)
1087         context->Eax = context->Edi = 0;
1088     else
1089     {
1090         buffer[0] = (DWORD)handle;
1091         buffer[1] = ptr;
1092
1093         context->Eax = (DWORD) ptr;
1094         context->Edi = (DWORD)(buffer + 2);
1095     }
1096 }
1097 #ifdef DEFINE_REGS_ENTRYPOINT
1098 DEFINE_REGS_ENTRYPOINT( AllocMappedBuffer, 0, 0 );
1099 #endif
1100
1101 /**********************************************************************
1102  *              FreeMappedBuffer        (KERNEL32.39)
1103  *
1104  * Free a buffer allocated by AllocMappedBuffer
1105  *
1106  * RETURNS
1107  *  Nothing.
1108  */
1109 void WINAPI __regs_FreeMappedBuffer(
1110               CONTEXT86 *context /* [in] EDI register: pointer to buffer */
1111 ) {
1112     if (context->Edi)
1113     {
1114         DWORD *buffer = (DWORD *)context->Edi - 2;
1115
1116         UnMapLS(buffer[1]);
1117
1118         GlobalUnlock((HGLOBAL)buffer[0]);
1119         GlobalFree((HGLOBAL)buffer[0]);
1120     }
1121 }
1122 #ifdef DEFINE_REGS_ENTRYPOINT
1123 DEFINE_REGS_ENTRYPOINT( FreeMappedBuffer, 0, 0 );
1124 #endif
1125
1126 /***********************************************************************
1127  *           GlobalMemoryStatusEx   (KERNEL32.@)
1128  * A version of GlobalMemoryStatus that can deal with memory over 4GB
1129  *
1130  * RETURNS
1131  *      TRUE
1132  */
1133 BOOL WINAPI GlobalMemoryStatusEx( LPMEMORYSTATUSEX lpmemex )
1134 {
1135     static MEMORYSTATUSEX       cached_memstatus;
1136     static int cache_lastchecked = 0;
1137     SYSTEM_INFO si;
1138 #ifdef linux
1139     FILE *f;
1140 #endif
1141 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
1142     int *tmp;
1143     int size_sys;
1144     int mib[2] = { CTL_HW };
1145 #endif
1146 #ifdef sun
1147     long pagesize,maxpages,freepages,swapspace,swapfree;
1148     struct anoninfo swapinf;
1149     int rval;
1150 #endif
1151
1152     if (time(NULL)==cache_lastchecked) {
1153         memcpy(lpmemex,&cached_memstatus,sizeof(*lpmemex));
1154         return TRUE;
1155     }
1156     cache_lastchecked = time(NULL);
1157
1158     lpmemex->dwLength         = sizeof(*lpmemex);
1159     lpmemex->dwMemoryLoad     = 0;
1160     lpmemex->ullTotalPhys     = 16*1024*1024;
1161     lpmemex->ullAvailPhys     = 16*1024*1024;
1162     lpmemex->ullTotalPageFile = 16*1024*1024;
1163     lpmemex->ullAvailPageFile = 16*1024*1024;
1164
1165 #ifdef linux
1166     f = fopen( "/proc/meminfo", "r" );
1167     if (f)
1168     {
1169         char buffer[256];
1170         int total, used, free, shared, buffers, cached;
1171
1172         lpmemex->ullTotalPhys = lpmemex->ullAvailPhys = 0;
1173         lpmemex->ullTotalPageFile = lpmemex->ullAvailPageFile = 0;
1174         while (fgets( buffer, sizeof(buffer), f ))
1175         {
1176             /* old style /proc/meminfo ... */
1177             if (sscanf( buffer, "Mem: %d %d %d %d %d %d", &total, &used, &free, &shared, &buffers, &cached ))
1178             {
1179                 lpmemex->ullTotalPhys += total;
1180                 lpmemex->ullAvailPhys += free + buffers + cached;
1181             }
1182             if (sscanf( buffer, "Swap: %d %d %d", &total, &used, &free ))
1183             {
1184                 lpmemex->ullTotalPageFile += total;
1185                 lpmemex->ullAvailPageFile += free;
1186             }
1187
1188             /* new style /proc/meminfo ... */
1189             if (sscanf(buffer, "MemTotal: %d", &total))
1190                 lpmemex->ullTotalPhys = total*1024;
1191             if (sscanf(buffer, "MemFree: %d", &free))
1192                 lpmemex->ullAvailPhys = free*1024;
1193             if (sscanf(buffer, "SwapTotal: %d", &total))
1194                 lpmemex->ullTotalPageFile = total*1024;
1195             if (sscanf(buffer, "SwapFree: %d", &free))
1196                 lpmemex->ullAvailPageFile = free*1024;
1197             if (sscanf(buffer, "Buffers: %d", &buffers))
1198                 lpmemex->ullAvailPhys += buffers*1024;
1199             if (sscanf(buffer, "Cached: %d", &cached))
1200                 lpmemex->ullAvailPhys += cached*1024;
1201         }
1202         fclose( f );
1203
1204         if (lpmemex->ullTotalPhys)
1205         {
1206             DWORDLONG TotalPhysical = lpmemex->ullTotalPhys+lpmemex->ullTotalPageFile;
1207             DWORDLONG AvailPhysical = lpmemex->ullAvailPhys+lpmemex->ullAvailPageFile;
1208             lpmemex->dwMemoryLoad = (TotalPhysical-AvailPhysical)
1209                                       / (TotalPhysical / 100);
1210         }
1211     }
1212 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
1213     mib[1] = HW_PHYSMEM;
1214     sysctl(mib, 2, NULL, &size_sys, NULL, 0);
1215     tmp = malloc(size_sys * sizeof(int));
1216     sysctl(mib, 2, tmp, &size_sys, NULL, 0);
1217     if (tmp && *tmp)
1218     {
1219         lpmemex->ullTotalPhys = *tmp;
1220         free(tmp);
1221         mib[1] = HW_USERMEM;
1222         sysctl(mib, 2, NULL, &size_sys, NULL, 0);
1223         tmp = malloc(size_sys * sizeof(int));
1224         sysctl(mib, 2, tmp, &size_sys, NULL, 0);
1225         if (tmp && *tmp)
1226         {
1227             lpmemex->ullAvailPhys = *tmp;
1228             lpmemex->ullTotalPageFile = *tmp;
1229             lpmemex->ullAvailPageFile = *tmp;
1230             lpmemex->dwMemoryLoad = lpmemex->ullTotalPhys - lpmemex->ullAvailPhys;
1231         } else
1232         {
1233             lpmemex->ullAvailPhys = lpmemex->ullTotalPhys;
1234             lpmemex->ullTotalPageFile = lpmemex->ullTotalPhys;
1235             lpmemex->ullAvailPageFile = lpmemex->ullTotalPhys;
1236             lpmemex->dwMemoryLoad = 0;
1237         }
1238         free(tmp);
1239
1240     }
1241 #elif defined ( sun )
1242     pagesize=sysconf(_SC_PAGESIZE);
1243     maxpages=sysconf(_SC_PHYS_PAGES);
1244     freepages=sysconf(_SC_AVPHYS_PAGES);
1245     rval=swapctl(SC_AINFO, &swapinf);
1246     if(rval >-1)
1247     {
1248         swapspace=swapinf.ani_max*pagesize;
1249         swapfree=swapinf.ani_free*pagesize;
1250     }else
1251     {
1252
1253         WARN("Swap size cannot be determined , assuming equal to physical memory\n");
1254         swapspace=maxpages*pagesize;
1255         swapfree=maxpages*pagesize;
1256     }
1257     lpmemex->ullTotalPhys=pagesize*maxpages;
1258     lpmemex->ullAvailPhys = pagesize*freepages;
1259     lpmemex->ullTotalPageFile = swapspace;
1260     lpmemex->ullAvailPageFile = swapfree;
1261     lpmemex->dwMemoryLoad =  lpmemex->ullTotalPhys - lpmemex->ullAvailPhys;
1262 #endif
1263
1264     /* Project2k refuses to start if it sees less than 1Mb of free swap */
1265     if (lpmemex->ullTotalPageFile < lpmemex->ullTotalPhys)
1266         lpmemex->ullTotalPageFile = lpmemex->ullTotalPhys;
1267     if (lpmemex->ullAvailPageFile < lpmemex->ullAvailPhys)
1268         lpmemex->ullAvailPageFile = lpmemex->ullAvailPhys;
1269
1270     /* FIXME: should do something for other systems */
1271     GetSystemInfo(&si);
1272     lpmemex->ullTotalVirtual  = (char*)si.lpMaximumApplicationAddress-(char*)si.lpMinimumApplicationAddress;
1273     /* FIXME: we should track down all the already allocated VM pages and substract them, for now arbitrarily remove 64KB so that it matches NT */
1274     lpmemex->ullAvailVirtual  = lpmemex->ullTotalVirtual-64*1024;
1275
1276     /* MSDN says about AvailExtendedVirtual: Size of unreserved and uncommitted
1277        memory in the extended portion of the virtual address space of the calling
1278        process, in bytes.
1279        However, I don't know what this means, so set it to zero :(
1280     */
1281     lpmemex->ullAvailExtendedVirtual = 0;
1282
1283     memcpy(&cached_memstatus,lpmemex,sizeof(*lpmemex));
1284
1285     TRACE("<-- LPMEMORYSTATUSEX: dwLength %ld, dwMemoryLoad %ld, ullTotalPhys %s, ullAvailPhys %s,"
1286           " ullTotalPageFile %s, ullAvailPageFile %s, ullTotalVirtual %s, ullAvailVirtual %s\n",
1287           lpmemex->dwLength, lpmemex->dwMemoryLoad, wine_dbgstr_longlong(lpmemex->ullTotalPhys),
1288           wine_dbgstr_longlong(lpmemex->ullAvailPhys), wine_dbgstr_longlong(lpmemex->ullTotalPageFile),
1289           wine_dbgstr_longlong(lpmemex->ullAvailPageFile), wine_dbgstr_longlong(lpmemex->ullTotalVirtual),
1290           wine_dbgstr_longlong(lpmemex->ullAvailVirtual) );
1291
1292     return TRUE;
1293 }
1294
1295 /***********************************************************************
1296  *           GlobalMemoryStatus   (KERNEL32.@)
1297  * Provides information about the status of the memory, so apps can tell
1298  * roughly how much they are able to allocate
1299  *
1300  * RETURNS
1301  *      None
1302  */
1303 VOID WINAPI GlobalMemoryStatus( LPMEMORYSTATUS lpBuffer )
1304 {
1305     MEMORYSTATUSEX memstatus;
1306     OSVERSIONINFOW osver;
1307
1308     /* Because GlobalMemoryStatus is identical to GlobalMemoryStatusEX save
1309        for one extra field in the struct, and the lack of a bug, we simply
1310        call GlobalMemoryStatusEx and copy the values across. */
1311     GlobalMemoryStatusEx(&memstatus);
1312
1313     lpBuffer->dwLength = sizeof(*lpBuffer);
1314     lpBuffer->dwMemoryLoad = memstatus.dwMemoryLoad;
1315
1316     /* Windows 2000 and later report -1 when values are greater than 4 Gb.
1317      * NT reports values modulo 4 Gb.
1318      * Values between 2 Gb and 4 Gb are rounded down to 2 Gb.
1319      */
1320
1321     osver.dwOSVersionInfoSize = sizeof(osver);
1322     GetVersionExW(&osver);
1323
1324     if ( osver.dwMajorVersion >= 5 )
1325     {
1326         lpBuffer->dwTotalPhys = (memstatus.ullTotalPhys > MAXDWORD) ? MAXDWORD :
1327                                 (memstatus.ullTotalPhys > MAXLONG) ? MAXLONG : memstatus.ullTotalPhys;
1328         lpBuffer->dwAvailPhys = (memstatus.ullAvailPhys > MAXDWORD) ? MAXDWORD :
1329                                 (memstatus.ullAvailPhys > MAXLONG) ? MAXLONG : memstatus.ullAvailPhys; 
1330         lpBuffer->dwTotalPageFile = (memstatus.ullTotalPageFile > MAXDWORD) ? MAXDWORD :
1331                                     (memstatus.ullTotalPageFile > MAXLONG) ? MAXLONG : memstatus.ullTotalPageFile;
1332         lpBuffer->dwAvailPageFile = (memstatus.ullAvailPageFile > MAXDWORD) ? MAXDWORD :
1333                                     (memstatus.ullAvailPageFile > MAXLONG) ? MAXLONG : memstatus.ullAvailPageFile;
1334         lpBuffer->dwTotalVirtual = (memstatus.ullTotalVirtual > MAXDWORD) ? MAXDWORD :
1335                                    (memstatus.ullTotalVirtual > MAXLONG)  ? MAXLONG : memstatus.ullTotalVirtual;
1336         lpBuffer->dwAvailVirtual = (memstatus.ullAvailVirtual > MAXDWORD) ? MAXDWORD :
1337                                    (memstatus.ullAvailVirtual > MAXLONG) ? MAXLONG : memstatus.ullAvailVirtual;
1338     }
1339     else        /* duplicate NT bug */
1340     {
1341         lpBuffer->dwTotalPhys = (memstatus.ullTotalPhys > MAXDWORD) ? memstatus.ullTotalPhys :
1342                                 (memstatus.ullTotalPhys > MAXLONG) ? MAXLONG : memstatus.ullTotalPhys;
1343         lpBuffer->dwAvailPhys = (memstatus.ullAvailPhys > MAXDWORD) ? memstatus.ullAvailPhys :
1344                                 (memstatus.ullAvailPhys > MAXLONG) ? MAXLONG : memstatus.ullAvailPhys;
1345         lpBuffer->dwTotalPageFile = (memstatus.ullTotalPageFile > MAXDWORD) ? memstatus.ullTotalPageFile : 
1346                                     (memstatus.ullTotalPageFile > MAXLONG) ? MAXLONG : memstatus.ullTotalPageFile;
1347         lpBuffer->dwAvailPageFile = (memstatus.ullAvailPageFile > MAXDWORD) ? memstatus.ullAvailPageFile : 
1348                                     (memstatus.ullAvailPageFile > MAXLONG) ? MAXLONG : memstatus.ullAvailPageFile;
1349         lpBuffer->dwTotalVirtual = (memstatus.ullTotalVirtual > MAXDWORD) ? memstatus.ullTotalVirtual : 
1350                                    (memstatus.ullTotalVirtual > MAXLONG)  ? MAXLONG : memstatus.ullTotalVirtual;
1351         lpBuffer->dwAvailVirtual = (memstatus.ullAvailVirtual > MAXDWORD) ? memstatus.ullAvailVirtual :
1352                                    (memstatus.ullAvailVirtual > MAXLONG) ? MAXLONG : memstatus.ullAvailVirtual;
1353     }
1354
1355     /* work around for broken photoshop 4 installer */
1356     if ( lpBuffer->dwAvailPhys +  lpBuffer->dwAvailPageFile >= 2U*1024*1024*1024)
1357          lpBuffer->dwAvailPageFile = 2U*1024*1024*1024 -  lpBuffer->dwAvailPhys - 1;
1358 }