1 /* $Id: global.c,v 1.19 2004/01/21 18:57:21 navaraf Exp $
3 * Win32 Global/Local heap functions (GlobalXXX, LocalXXX).
4 * These functions included in Win32 for compatibility with 16 bit Windows
5 * Especially the moveable blocks and handles are oldish.
6 * But the ability to directly allocate memory with GPTR and LPTR is widely
9 * Updated to support movable memory with algorithms taken from wine.
16 #include <kernel32/kernel32.h>
19 #define STRUCT_PACK __attribute__((packed))
24 #define MAGIC_GLOBAL_USED 0x5342BEEF
25 #define GLOBAL_LOCK_MAX 0xFF
27 /*Wine found that some applications complain if memory isn't 8 byte aligned.
28 * We make use of that experience here.
30 #define HANDLE_SIZE 8 /*sizeof(HANDLE) *2 */
33 typedef struct __GLOBAL_LOCAL_HANDLE
36 LPVOID Pointer
; STRUCT_PACK
39 } GLOBAL_HANDLE
, LOCAL_HANDLE
, *PGLOBAL_HANDLE
, *PLOCAL_HANDLE
;
41 #define HANDLE_TO_INTERN(h) ((PGLOBAL_HANDLE)(((char *)(h))-4))
42 #define INTERN_TO_HANDLE(i) ((HGLOBAL) &((i)->Pointer))
43 #define POINTER_TO_HANDLE(p) (*(PHANDLE)(p - HANDLE_SIZE))
44 #define ISHANDLE(h) ((((ULONG)(h)) & 0x4)!=0)
45 #define ISPOINTER(h) ((((ULONG)(h)) & 0x4)==0)
48 static void DbgPrintStruct(PGLOBAL_HANDLE h
)
50 DbgPrint("Magic: 0x%X\n", h
->Magic
);
51 DbgPrint("Pointer: 0x%X\n", h
->Pointer
);
52 DbgPrint("Flags: 0x%X\n", h
->Flags
);
53 DbgPrint("LockCount: 0x%X\n", h
->LockCount
);
58 /* FUNCTIONS ***************************************************************/
64 GlobalAlloc(UINT uFlags
,
68 PGLOBAL_HANDLE phandle
= 0;
71 /*Fixme: When we are sure all allocations are 8-byte aligned,
72 **we can remove this hack.
74 PGLOBAL_HANDLE hack_fix
= 0;
76 if (uFlags
& GMEM_ZEROINIT
)
78 heap_flags
= HEAP_ZERO_MEMORY
;
81 DPRINT("GlobalAlloc( 0x%X, 0x%lX )\n", uFlags
, dwBytes
);
83 //Changed hProcessHeap to GetProcessHeap()
84 if ((uFlags
& GMEM_MOVEABLE
)==0) /* POINTER */
86 return ((HGLOBAL
)RtlAllocateHeap(GetProcessHeap(), heap_flags
, dwBytes
));
90 HeapLock(hProcessHeap
);
92 phandle
= RtlAllocateHeap(GetProcessHeap(), 0, sizeof(GLOBAL_HANDLE
));
93 /* This little hack is to make sure that we get a pointer with 8-byte
95 ** Fixme: When we are sure all allocations are 8-byte aligned,
96 ** we can remove this hack.
98 if (ISPOINTER(INTERN_TO_HANDLE(phandle
)))
100 hack_fix
= RtlAllocateHeap(GetProcessHeap(), 0, sizeof(GLOBAL_HANDLE
));
101 RtlFreeHeap(GetProcessHeap(), 0, phandle
);
106 phandle
->Magic
= MAGIC_GLOBAL_USED
;
107 phandle
->Flags
= uFlags
>> 8;
108 phandle
->LockCount
= 0;
109 phandle
->Pointer
= 0;
113 palloc
= RtlAllocateHeap(GetProcessHeap(), heap_flags
, dwBytes
+ HANDLE_SIZE
);
116 *(PHANDLE
)palloc
= INTERN_TO_HANDLE(phandle
);
117 phandle
->Pointer
= palloc
+ HANDLE_SIZE
;
119 else /*failed to allocate the memory block*/
121 RtlFreeHeap(GetProcessHeap(), 0, phandle
);
127 DbgPrint("Allocated a 0 size movable block.\n");
128 DbgPrintStruct(phandle
);
129 DbgPrint("Address of the struct: 0x%X\n", phandle
);
130 DbgPrint("Address of pointer: 0x%X\n", &(phandle
->Pointer
));
133 HeapUnlock(hProcessHeap
);
136 return INTERN_TO_HANDLE(phandle
);
147 GlobalCompact(DWORD dwMinFree
)
149 return RtlCompactHeap(hProcessHeap
, 0);
157 GlobalFix(HGLOBAL hMem
)
159 if (INVALID_HANDLE_VALUE
!= hMem
)
167 GlobalFlags(HGLOBAL hMem
)
170 PGLOBAL_HANDLE phandle
;
172 DbgPrint("GlobalFlags( 0x%lX )\n", (ULONG
)hMem
);
176 DbgPrint("GlobalFlags: Fixed memory.\n");
181 HeapLock(GetProcessHeap());
183 phandle
= HANDLE_TO_INTERN(hMem
);
185 /*DbgPrintStruct(phandle);*/
187 if (MAGIC_GLOBAL_USED
== phandle
->Magic
)
189 /*DbgPrint("GlobalFlags: Magic number ok\n");
190 **DbgPrint("GlobalFlags: pointer is 0x%X\n", phandle->Pointer);
192 retval
= phandle
->LockCount
+ (phandle
->Flags
<< 8);
193 if (0 == phandle
->Pointer
)
195 retval
= retval
| GMEM_DISCARDED
;
200 DbgPrint("GlobalSize: invalid handle\n");
203 HeapUnlock(GetProcessHeap());
213 GlobalFree(HGLOBAL hMem
)
215 PGLOBAL_HANDLE phandle
;
217 DPRINT("GlobalFree( 0x%lX )\n", (ULONG
)hMem
);
219 if (ISPOINTER(hMem
)) /* POINTER */
221 RtlFreeHeap(GetProcessHeap(), 0, (PVOID
)hMem
);
226 HeapLock(GetProcessHeap());
228 phandle
= HANDLE_TO_INTERN(hMem
);
230 if(MAGIC_GLOBAL_USED
== phandle
->Magic
)
233 if(phandle
->LockCount
!=0)
235 DbgPrint("Warning! GlobalFree(0x%X) Freeing a handle to a locked object.\n", hMem
);
236 SetLastError(ERROR_INVALID_HANDLE
);
240 RtlFreeHeap(GetProcessHeap(), 0, phandle
->Pointer
- HANDLE_SIZE
);
242 RtlFreeHeap(GetProcessHeap(), 0, phandle
);
244 HeapUnlock(GetProcessHeap());
256 GlobalHandle(LPCVOID pMem
)
259 PGLOBAL_HANDLE test
= 0;
260 LPCVOID pointer_test
= 0;
262 DbgPrint("GlobalHandle( 0x%lX )\n", (ULONG
)pMem
);
263 if (0 == pMem
) /*Invalid argument */
265 SetLastError(ERROR_INVALID_PARAMETER
);
266 DbgPrint("Error: 0 handle.\n");
270 HeapLock(GetProcessHeap());
271 /* Now test to see if this pointer is associated with a handle.
272 * This is done by calling RtlValidateHeap() and seeing if it fails.
274 if (RtlValidateHeap(GetProcessHeap(), 0, (char *)pMem
)) /*FIXED*/
276 handle
= (HGLOBAL
)pMem
;
281 handle
= POINTER_TO_HANDLE(pMem
);
285 /* Test to see if this memory is valid*/
286 test
= HANDLE_TO_INTERN(handle
);
287 if (!IsBadReadPtr(test
, sizeof(GLOBAL_HANDLE
)))
289 if (MAGIC_GLOBAL_USED
== test
->Magic
)
291 pointer_test
= test
->Pointer
;
292 if (!RtlValidateHeap(GetProcessHeap(), 0, ((char *)pointer_test
) - HANDLE_SIZE
) ||
293 !RtlValidateHeap(GetProcessHeap(), 0, test
))
295 SetLastError(ERROR_INVALID_HANDLE
);
302 DbgPrint("GlobalHandle: Bad read pointer.\n");
303 SetLastError(ERROR_INVALID_HANDLE
);
307 HeapUnlock(GetProcessHeap());
317 GlobalLock(HGLOBAL hMem
)
319 PGLOBAL_HANDLE phandle
;
322 DPRINT("GlobalLock( 0x%lX )\n", (ULONG
)hMem
);
325 return (LPVOID
) hMem
;
327 HeapLock(GetProcessHeap());
329 phandle
= HANDLE_TO_INTERN(hMem
);
331 if(MAGIC_GLOBAL_USED
== phandle
->Magic
)
333 if(GLOBAL_LOCK_MAX
> phandle
->LockCount
)
335 phandle
->LockCount
++;
337 palloc
= phandle
->Pointer
;
341 DPRINT("GlobalLock: invalid handle\n");
342 palloc
= (LPVOID
) hMem
;
345 HeapUnlock(GetProcessHeap());
355 GlobalMemoryStatus(LPMEMORYSTATUS lpBuffer
)
357 static MEMORYSTATUS cached_memstatus
;
358 // static int cache_lastchecked = 0;
361 // if (GetSystemTimeAsFileTime(NULL)==cache_lastchecked) {
362 // memcpy(lpBuffer,&cached_memstatus,sizeof(MEMORYSTATUS));
365 // cache_lastchecked = GetSystemTimeAsFileTime(NULL);
367 lpBuffer
->dwLength
= sizeof(MEMORYSTATUS
);
368 lpBuffer
->dwMemoryLoad
= 0;
369 lpBuffer
->dwTotalPhys
= 32*1024*1024;
370 lpBuffer
->dwAvailPhys
= 32*1024*1024;
371 lpBuffer
->dwTotalPageFile
= 32*1024*1024;
372 lpBuffer
->dwAvailPageFile
= 32*1024*1024;
374 /* Some applications (e.g. QuickTime 6) crash if we tell them there
375 * is more than 2GB of physical memory.
377 if (lpBuffer
->dwTotalPhys
>2U*1024*1024*1024)
379 lpBuffer
->dwTotalPhys
=2U*1024*1024*1024;
380 lpBuffer
->dwAvailPhys
=2U*1024*1024*1024;
383 /* FIXME: should do something for other systems */
385 lpBuffer
->dwTotalVirtual
= (char*)si
.lpMaximumApplicationAddress
-(char*)si
.lpMinimumApplicationAddress
;
386 /* FIXME: we should track down all the already allocated VM pages and substract them, for now arbitrarily remove 64KB so that it matches NT */
387 lpBuffer
->dwAvailVirtual
= lpBuffer
->dwTotalVirtual
-64*1024;
388 memcpy(&cached_memstatus
,lpBuffer
,sizeof(MEMORYSTATUS
));
390 /* it appears some memory display programs want to divide by these values */
391 if(lpBuffer
->dwTotalPageFile
==0)
392 lpBuffer
->dwTotalPageFile
++;
394 if(lpBuffer
->dwAvailPageFile
==0)
395 lpBuffer
->dwAvailPageFile
++;
397 DPRINT1("<-- LPMEMORYSTATUS: dwLength %ld, dwMemoryLoad %ld, dwTotalPhys %ld, dwAvailPhys %ld,"
398 " dwTotalPageFile %ld, dwAvailPageFile %ld, dwTotalVirtual %ld, dwAvailVirtual %ld\n",
399 lpBuffer
->dwLength
, lpBuffer
->dwMemoryLoad
, lpBuffer
->dwTotalPhys
, lpBuffer
->dwAvailPhys
,
400 lpBuffer
->dwTotalPageFile
, lpBuffer
->dwAvailPageFile
, lpBuffer
->dwTotalVirtual
,
401 lpBuffer
->dwAvailVirtual
);
406 GlobalReAlloc(HGLOBAL hMem
,
413 PGLOBAL_HANDLE phandle
= 0;
414 ULONG heap_flags
= 0;
416 DPRINT("GlobalReAlloc( 0x%lX, 0x%lX, 0x%X )\n", (ULONG
)hMem
, dwBytes
, uFlags
);
420 if (uFlags
& GMEM_ZEROINIT
)
422 heap_flags
= HEAP_ZERO_MEMORY
;
425 HeapLock(GetProcessHeap());
427 if(uFlags
& GMEM_MODIFY
) /* modify flags */
429 if( ISPOINTER(hMem
) && (uFlags
& GMEM_MOVEABLE
))
431 /* make a fixed block moveable
432 * actually only NT is able to do this. And it's soo simple
436 SetLastError( ERROR_NOACCESS
);
441 dwBytes
= RtlSizeHeap(GetProcessHeap(), 0, (LPVOID
) hMem
);
442 hnew
= GlobalAlloc( uFlags
, dwBytes
);
443 palloc
= GlobalLock(hnew
);
444 memcpy(palloc
, (LPVOID
) hMem
, dwBytes
);
446 RtlFreeHeap(GetProcessHeap(),0,hMem
);
449 else if(ISPOINTER(hMem
) && (uFlags
& GMEM_DISCARDABLE
))
451 /* change the flags to make our block "discardable" */
452 phandle
= HANDLE_TO_INTERN(hMem
);
453 phandle
->Flags
= phandle
->Flags
| (GMEM_DISCARDABLE
>> 8);
458 SetLastError(ERROR_INVALID_PARAMETER
);
466 /* reallocate fixed memory */
467 hnew
= (HANDLE
)RtlReAllocateHeap(GetProcessHeap(), heap_flags
, (LPVOID
) hMem
, dwBytes
);
471 /* reallocate a moveable block */
472 phandle
= HANDLE_TO_INTERN(hMem
);
474 if(phandle
->LockCount
!= 0)
476 SetLastError(ERROR_INVALID_HANDLE
);
485 palloc
= RtlReAllocateHeap(GetProcessHeap(), heap_flags
,
486 phandle
->Pointer
- HANDLE_SIZE
,
487 dwBytes
+ HANDLE_SIZE
);
494 *(PHANDLE
)palloc
= hMem
;
495 phandle
->Pointer
= palloc
+ HANDLE_SIZE
;
500 palloc
= RtlAllocateHeap(GetProcessHeap(), heap_flags
, dwBytes
+ HANDLE_SIZE
);
507 *(PHANDLE
)palloc
= hMem
;
508 phandle
->Pointer
= palloc
+ HANDLE_SIZE
;
517 RtlFreeHeap(GetProcessHeap(), 0, phandle
->Pointer
- HANDLE_SIZE
);
518 phandle
->Pointer
= 0;
523 HeapUnlock(GetProcessHeap());
530 GlobalSize(HGLOBAL hMem
)
533 PGLOBAL_HANDLE phandle
= 0;
535 DbgPrint("GlobalSize( 0x%lX )\n", (ULONG
)hMem
);
537 if(ISPOINTER(hMem
)) /*FIXED*/
539 retval
= RtlSizeHeap(GetProcessHeap(), 0, hMem
);
543 HeapLock(GetProcessHeap());
545 phandle
= HANDLE_TO_INTERN(hMem
);
547 if (MAGIC_GLOBAL_USED
== phandle
->Magic
)
549 if (0 != phandle
->Pointer
)/*NOT DISCARDED*/
551 retval
= RtlSizeHeap(GetProcessHeap(), 0, phandle
->Pointer
- HANDLE_SIZE
);
553 if (retval
== (SIZE_T
)-1) /*RtlSizeHeap failed*/
556 **TODO: RtlSizeHeap does not set last error.
557 ** We should choose an error value to set as
558 ** the last error. Which One?
560 DbgPrint("GlobalSize: RtlSizeHeap failed.\n");
563 else /*Everything is ok*/
565 retval
= retval
- HANDLE_SIZE
;
571 DPRINT("GlobalSize: invalid handle\n");
573 HeapUnlock(GetProcessHeap());
583 GlobalUnfix(HGLOBAL hMem
)
585 if (hMem
!= INVALID_HANDLE_VALUE
)
594 GlobalUnlock(HGLOBAL hMem
)
597 PGLOBAL_HANDLE phandle
;
600 DPRINT("GlobalUnlock( 0x%lX )\n", (ULONG
)hMem
);
604 SetLastError(ERROR_NOT_LOCKED
);
608 HeapLock(GetProcessHeap());
610 phandle
= HANDLE_TO_INTERN(hMem
);
611 if(MAGIC_GLOBAL_USED
== phandle
->Magic
)
613 if (0 >= phandle
->LockCount
)
616 SetLastError(ERROR_NOT_LOCKED
);
618 else if (GLOBAL_LOCK_MAX
> phandle
->LockCount
)
620 phandle
->LockCount
--;
621 locked
= (0 == phandle
->LockCount
) ? TRUE
: FALSE
;
622 SetLastError(NO_ERROR
);
627 DPRINT("GlobalUnlock: invalid handle\n");
630 HeapUnlock(GetProcessHeap());
639 GlobalUnWire(HGLOBAL hMem
)
641 return GlobalUnlock(hMem
);
649 GlobalWire(HGLOBAL hMem
)
651 return GlobalLock(hMem
);
658 //GlobalDiscard(HGLOBAL hMem)
660 // return GlobalReAlloc(hMem, 0, GMEM_MOVEABLE);