Minor fix in GlobalMemoryStatus to get some applications happy.
[reactos.git] / reactos / lib / kernel32 / mem / global.c
1 /* $Id: global.c,v 1.19 2004/01/21 18:57:21 navaraf Exp $
2 *
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
7 * used.
8 *
9 * Updated to support movable memory with algorithms taken from wine.
10 */
11
12 #include <k32.h>
13 #include <time.h>
14
15 #define NDEBUG
16 #include <kernel32/kernel32.h>
17
18 #ifdef _GNUC_
19 #define STRUCT_PACK __attribute__((packed))
20 #else
21 #define STRUCT_PACK
22 #endif
23
24 #define MAGIC_GLOBAL_USED 0x5342BEEF
25 #define GLOBAL_LOCK_MAX 0xFF
26
27 /*Wine found that some applications complain if memory isn't 8 byte aligned.
28 * We make use of that experience here.
29 */
30 #define HANDLE_SIZE 8 /*sizeof(HANDLE) *2 */
31
32
33 typedef struct __GLOBAL_LOCAL_HANDLE
34 {
35 DWORD Magic;
36 LPVOID Pointer; STRUCT_PACK
37 BYTE Flags;
38 BYTE LockCount;
39 } GLOBAL_HANDLE, LOCAL_HANDLE, *PGLOBAL_HANDLE, *PLOCAL_HANDLE;
40
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)
46
47
48 static void DbgPrintStruct(PGLOBAL_HANDLE h)
49 {
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);
54 }
55
56
57
58 /* FUNCTIONS ***************************************************************/
59
60 /*
61 * @implemented
62 */
63 HGLOBAL STDCALL
64 GlobalAlloc(UINT uFlags,
65 DWORD dwBytes)
66 {
67
68 PGLOBAL_HANDLE phandle = 0;
69 PVOID palloc = 0;
70 UINT heap_flags = 0;
71 /*Fixme: When we are sure all allocations are 8-byte aligned,
72 **we can remove this hack.
73 */
74 PGLOBAL_HANDLE hack_fix = 0;
75
76 if (uFlags & GMEM_ZEROINIT)
77 {
78 heap_flags = HEAP_ZERO_MEMORY;
79 }
80
81 DPRINT("GlobalAlloc( 0x%X, 0x%lX )\n", uFlags, dwBytes);
82
83 //Changed hProcessHeap to GetProcessHeap()
84 if ((uFlags & GMEM_MOVEABLE)==0) /* POINTER */
85 {
86 return ((HGLOBAL)RtlAllocateHeap(GetProcessHeap(), heap_flags, dwBytes));
87 }
88 else /* HANDLE */
89 {
90 HeapLock(hProcessHeap);
91
92 phandle = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(GLOBAL_HANDLE));
93 /* This little hack is to make sure that we get a pointer with 8-byte
94 ** alignment.
95 ** Fixme: When we are sure all allocations are 8-byte aligned,
96 ** we can remove this hack.
97 */
98 if (ISPOINTER(INTERN_TO_HANDLE(phandle)))
99 {
100 hack_fix = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(GLOBAL_HANDLE));
101 RtlFreeHeap(GetProcessHeap(), 0, phandle);
102 phandle = hack_fix;
103 }
104 if (phandle)
105 {
106 phandle->Magic = MAGIC_GLOBAL_USED;
107 phandle->Flags = uFlags >> 8;
108 phandle->LockCount = 0;
109 phandle->Pointer = 0;
110
111 if (dwBytes)
112 {
113 palloc = RtlAllocateHeap(GetProcessHeap(), heap_flags, dwBytes + HANDLE_SIZE);
114 if (palloc)
115 {
116 *(PHANDLE)palloc = INTERN_TO_HANDLE(phandle);
117 phandle->Pointer = palloc + HANDLE_SIZE;
118 }
119 else /*failed to allocate the memory block*/
120 {
121 RtlFreeHeap(GetProcessHeap(), 0, phandle);
122 phandle = 0;
123 }
124 }
125 else
126 {
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));
131 }
132 }
133 HeapUnlock(hProcessHeap);
134
135 if (phandle)
136 return INTERN_TO_HANDLE(phandle);
137 else
138 return (HGLOBAL)0;
139 }
140 }
141
142
143 /*
144 * @implemented
145 */
146 UINT STDCALL
147 GlobalCompact(DWORD dwMinFree)
148 {
149 return RtlCompactHeap(hProcessHeap, 0);
150 }
151
152
153 /*
154 * @implemented
155 */
156 VOID STDCALL
157 GlobalFix(HGLOBAL hMem)
158 {
159 if (INVALID_HANDLE_VALUE != hMem)
160 GlobalLock(hMem);
161 }
162
163 /*
164 * @implemented
165 */
166 UINT STDCALL
167 GlobalFlags(HGLOBAL hMem)
168 {
169 DWORD retval;
170 PGLOBAL_HANDLE phandle;
171
172 DbgPrint("GlobalFlags( 0x%lX )\n", (ULONG)hMem);
173
174 if(!ISHANDLE(hMem))
175 {
176 DbgPrint("GlobalFlags: Fixed memory.\n");
177 retval = 0;
178 }
179 else
180 {
181 HeapLock(GetProcessHeap());
182
183 phandle = HANDLE_TO_INTERN(hMem);
184
185 /*DbgPrintStruct(phandle);*/
186
187 if (MAGIC_GLOBAL_USED == phandle->Magic)
188 {
189 /*DbgPrint("GlobalFlags: Magic number ok\n");
190 **DbgPrint("GlobalFlags: pointer is 0x%X\n", phandle->Pointer);
191 */
192 retval = phandle->LockCount + (phandle->Flags << 8);
193 if (0 == phandle->Pointer)
194 {
195 retval = retval | GMEM_DISCARDED;
196 }
197 }
198 else
199 {
200 DbgPrint("GlobalSize: invalid handle\n");
201 retval = 0;
202 }
203 HeapUnlock(GetProcessHeap());
204 }
205 return retval;
206 }
207
208
209 /*
210 * @implemented
211 */
212 HGLOBAL STDCALL
213 GlobalFree(HGLOBAL hMem)
214 {
215 PGLOBAL_HANDLE phandle;
216
217 DPRINT("GlobalFree( 0x%lX )\n", (ULONG)hMem);
218
219 if (ISPOINTER(hMem)) /* POINTER */
220 {
221 RtlFreeHeap(GetProcessHeap(), 0, (PVOID)hMem);
222 hMem = 0;
223 }
224 else /* HANDLE */
225 {
226 HeapLock(GetProcessHeap());
227
228 phandle = HANDLE_TO_INTERN(hMem);
229
230 if(MAGIC_GLOBAL_USED == phandle->Magic)
231 {
232
233 if(phandle->LockCount!=0)
234 {
235 DbgPrint("Warning! GlobalFree(0x%X) Freeing a handle to a locked object.\n", hMem);
236 SetLastError(ERROR_INVALID_HANDLE);
237 }
238
239 if(phandle->Pointer)
240 RtlFreeHeap(GetProcessHeap(), 0, phandle->Pointer - HANDLE_SIZE);
241
242 RtlFreeHeap(GetProcessHeap(), 0, phandle);
243 }
244 HeapUnlock(GetProcessHeap());
245
246 hMem = 0;
247 }
248 return hMem;
249 }
250
251
252 /*
253 * @implemented
254 */
255 HGLOBAL STDCALL
256 GlobalHandle(LPCVOID pMem)
257 {
258 HGLOBAL handle = 0;
259 PGLOBAL_HANDLE test = 0;
260 LPCVOID pointer_test = 0;
261
262 DbgPrint("GlobalHandle( 0x%lX )\n", (ULONG)pMem);
263 if (0 == pMem) /*Invalid argument */
264 {
265 SetLastError(ERROR_INVALID_PARAMETER);
266 DbgPrint("Error: 0 handle.\n");
267 return 0;
268 }
269
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.
273 */
274 if (RtlValidateHeap(GetProcessHeap(), 0, (char *)pMem)) /*FIXED*/
275 {
276 handle = (HGLOBAL)pMem;
277 return handle;
278 }
279 else /*MOVABLE*/
280 {
281 handle = POINTER_TO_HANDLE(pMem);
282 }
283
284
285 /* Test to see if this memory is valid*/
286 test = HANDLE_TO_INTERN(handle);
287 if (!IsBadReadPtr(test, sizeof(GLOBAL_HANDLE)))
288 {
289 if (MAGIC_GLOBAL_USED == test->Magic)
290 {
291 pointer_test = test->Pointer;
292 if (!RtlValidateHeap(GetProcessHeap(), 0, ((char *)pointer_test) - HANDLE_SIZE) ||
293 !RtlValidateHeap(GetProcessHeap(), 0, test))
294 {
295 SetLastError(ERROR_INVALID_HANDLE);
296 handle = 0;
297 }
298 }
299 }
300 else
301 {
302 DbgPrint("GlobalHandle: Bad read pointer.\n");
303 SetLastError(ERROR_INVALID_HANDLE);
304 handle = 0;
305 }
306
307 HeapUnlock(GetProcessHeap());
308
309 return handle;
310 }
311
312
313 /*
314 * @implemented
315 */
316 LPVOID STDCALL
317 GlobalLock(HGLOBAL hMem)
318 {
319 PGLOBAL_HANDLE phandle;
320 LPVOID palloc;
321
322 DPRINT("GlobalLock( 0x%lX )\n", (ULONG)hMem);
323
324 if (ISPOINTER(hMem))
325 return (LPVOID) hMem;
326
327 HeapLock(GetProcessHeap());
328
329 phandle = HANDLE_TO_INTERN(hMem);
330
331 if(MAGIC_GLOBAL_USED == phandle->Magic)
332 {
333 if(GLOBAL_LOCK_MAX > phandle->LockCount)
334 {
335 phandle->LockCount++;
336 }
337 palloc = phandle->Pointer;
338 }
339 else
340 {
341 DPRINT("GlobalLock: invalid handle\n");
342 palloc = (LPVOID) hMem;
343 }
344
345 HeapUnlock(GetProcessHeap());
346
347 return palloc;
348 }
349
350
351 /*
352 * @unimplemented
353 */
354 VOID STDCALL
355 GlobalMemoryStatus(LPMEMORYSTATUS lpBuffer)
356 {
357 static MEMORYSTATUS cached_memstatus;
358 // static int cache_lastchecked = 0;
359 SYSTEM_INFO si;
360
361 // if (GetSystemTimeAsFileTime(NULL)==cache_lastchecked) {
362 // memcpy(lpBuffer,&cached_memstatus,sizeof(MEMORYSTATUS));
363 // return;
364 // }
365 // cache_lastchecked = GetSystemTimeAsFileTime(NULL);
366
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;
373
374 /* Some applications (e.g. QuickTime 6) crash if we tell them there
375 * is more than 2GB of physical memory.
376 */
377 if (lpBuffer->dwTotalPhys>2U*1024*1024*1024)
378 {
379 lpBuffer->dwTotalPhys=2U*1024*1024*1024;
380 lpBuffer->dwAvailPhys=2U*1024*1024*1024;
381 }
382
383 /* FIXME: should do something for other systems */
384 GetSystemInfo(&si);
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));
389
390 /* it appears some memory display programs want to divide by these values */
391 if(lpBuffer->dwTotalPageFile==0)
392 lpBuffer->dwTotalPageFile++;
393
394 if(lpBuffer->dwAvailPageFile==0)
395 lpBuffer->dwAvailPageFile++;
396
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);
402 }
403
404
405 HGLOBAL STDCALL
406 GlobalReAlloc(HGLOBAL hMem,
407 DWORD dwBytes,
408 UINT uFlags)
409 {
410
411 LPVOID palloc = 0;
412 HGLOBAL hnew = 0;
413 PGLOBAL_HANDLE phandle = 0;
414 ULONG heap_flags = 0;
415
416 DPRINT("GlobalReAlloc( 0x%lX, 0x%lX, 0x%X )\n", (ULONG)hMem, dwBytes, uFlags);
417
418 hnew = 0;
419
420 if (uFlags & GMEM_ZEROINIT)
421 {
422 heap_flags = HEAP_ZERO_MEMORY;
423 }
424
425 HeapLock(GetProcessHeap());
426
427 if(uFlags & GMEM_MODIFY) /* modify flags */
428 {
429 if( ISPOINTER(hMem) && (uFlags & GMEM_MOVEABLE))
430 {
431 /* make a fixed block moveable
432 * actually only NT is able to do this. And it's soo simple
433 */
434 if (0 == hMem)
435 {
436 SetLastError( ERROR_NOACCESS );
437 hnew = 0;
438 }
439 else
440 {
441 dwBytes = RtlSizeHeap(GetProcessHeap(), 0, (LPVOID) hMem);
442 hnew = GlobalAlloc( uFlags, dwBytes);
443 palloc = GlobalLock(hnew);
444 memcpy(palloc, (LPVOID) hMem, dwBytes);
445 GlobalUnlock(hnew);
446 RtlFreeHeap(GetProcessHeap(),0,hMem);
447 }
448 }
449 else if(ISPOINTER(hMem) && (uFlags & GMEM_DISCARDABLE))
450 {
451 /* change the flags to make our block "discardable" */
452 phandle = HANDLE_TO_INTERN(hMem);
453 phandle->Flags = phandle->Flags | (GMEM_DISCARDABLE >> 8);
454 hnew = hMem;
455 }
456 else
457 {
458 SetLastError(ERROR_INVALID_PARAMETER);
459 hnew = 0;
460 }
461 }
462 else
463 {
464 if(ISPOINTER(hMem))
465 {
466 /* reallocate fixed memory */
467 hnew = (HANDLE)RtlReAllocateHeap(GetProcessHeap(), heap_flags, (LPVOID) hMem, dwBytes);
468 }
469 else
470 {
471 /* reallocate a moveable block */
472 phandle= HANDLE_TO_INTERN(hMem);
473 #if 0
474 if(phandle->LockCount != 0)
475 {
476 SetLastError(ERROR_INVALID_HANDLE);
477 }
478 else
479 #endif
480 if (0 != dwBytes)
481 {
482 hnew = hMem;
483 if(phandle->Pointer)
484 {
485 palloc = RtlReAllocateHeap(GetProcessHeap(), heap_flags,
486 phandle->Pointer - HANDLE_SIZE,
487 dwBytes + HANDLE_SIZE);
488 if (0 == palloc)
489 {
490 hnew = 0;
491 }
492 else
493 {
494 *(PHANDLE)palloc = hMem;
495 phandle->Pointer = palloc + HANDLE_SIZE;
496 }
497 }
498 else
499 {
500 palloc = RtlAllocateHeap(GetProcessHeap(), heap_flags, dwBytes + HANDLE_SIZE);
501 if (0 == palloc)
502 {
503 hnew = 0;
504 }
505 else
506 {
507 *(PHANDLE)palloc = hMem;
508 phandle->Pointer = palloc + HANDLE_SIZE;
509 }
510 }
511 }
512 else
513 {
514 hnew = hMem;
515 if(phandle->Pointer)
516 {
517 RtlFreeHeap(GetProcessHeap(), 0, phandle->Pointer - HANDLE_SIZE);
518 phandle->Pointer = 0;
519 }
520 }
521 }
522 }
523 HeapUnlock(GetProcessHeap());
524
525 return hnew;
526 }
527
528
529 DWORD STDCALL
530 GlobalSize(HGLOBAL hMem)
531 {
532 SIZE_T retval = 0;
533 PGLOBAL_HANDLE phandle = 0;
534
535 DbgPrint("GlobalSize( 0x%lX )\n", (ULONG)hMem);
536
537 if(ISPOINTER(hMem)) /*FIXED*/
538 {
539 retval = RtlSizeHeap(GetProcessHeap(), 0, hMem);
540 }
541 else /*MOVEABLE*/
542 {
543 HeapLock(GetProcessHeap());
544
545 phandle = HANDLE_TO_INTERN(hMem);
546
547 if (MAGIC_GLOBAL_USED == phandle->Magic)
548 {
549 if (0 != phandle->Pointer)/*NOT DISCARDED*/
550 {
551 retval = RtlSizeHeap(GetProcessHeap(), 0, phandle->Pointer - HANDLE_SIZE);
552
553 if (retval == (SIZE_T)-1) /*RtlSizeHeap failed*/
554 {
555 /*
556 **TODO: RtlSizeHeap does not set last error.
557 ** We should choose an error value to set as
558 ** the last error. Which One?
559 */
560 DbgPrint("GlobalSize: RtlSizeHeap failed.\n");
561 retval = 0;
562 }
563 else /*Everything is ok*/
564 {
565 retval = retval - HANDLE_SIZE;
566 }
567 }
568 }
569 else
570 {
571 DPRINT("GlobalSize: invalid handle\n");
572 }
573 HeapUnlock(GetProcessHeap());
574 }
575 return retval;
576 }
577
578
579 /*
580 * @implemented
581 */
582 VOID STDCALL
583 GlobalUnfix(HGLOBAL hMem)
584 {
585 if (hMem != INVALID_HANDLE_VALUE)
586 GlobalUnlock(hMem);
587 }
588
589
590 /*
591 * @implemented
592 */
593 BOOL STDCALL
594 GlobalUnlock(HGLOBAL hMem)
595 {
596
597 PGLOBAL_HANDLE phandle;
598 BOOL locked;
599
600 DPRINT("GlobalUnlock( 0x%lX )\n", (ULONG)hMem);
601
602 if(ISPOINTER(hMem))
603 {
604 SetLastError(ERROR_NOT_LOCKED);
605 return FALSE;
606 }
607
608 HeapLock(GetProcessHeap());
609
610 phandle = HANDLE_TO_INTERN(hMem);
611 if(MAGIC_GLOBAL_USED == phandle->Magic)
612 {
613 if (0 >= phandle->LockCount)
614 {
615 locked = FALSE;
616 SetLastError(ERROR_NOT_LOCKED);
617 }
618 else if (GLOBAL_LOCK_MAX > phandle->LockCount)
619 {
620 phandle->LockCount--;
621 locked = (0 == phandle->LockCount) ? TRUE : FALSE;
622 SetLastError(NO_ERROR);
623 }
624 }
625 else
626 {
627 DPRINT("GlobalUnlock: invalid handle\n");
628 locked = FALSE;
629 }
630 HeapUnlock(GetProcessHeap());
631 return locked;
632 }
633
634
635 /*
636 * @implemented
637 */
638 BOOL STDCALL
639 GlobalUnWire(HGLOBAL hMem)
640 {
641 return GlobalUnlock(hMem);
642 }
643
644
645 /*
646 * @implemented
647 */
648 LPVOID STDCALL
649 GlobalWire(HGLOBAL hMem)
650 {
651 return GlobalLock(hMem);
652 }
653
654 /*
655 * @implemented
656 */
657 //HGLOBAL STDCALL
658 //GlobalDiscard(HGLOBAL hMem)
659 //{
660 // return GlobalReAlloc(hMem, 0, GMEM_MOVEABLE);
661 //}
662 /* EOF */