5d8ee55196defcf28cf4f6bfab4e75f6575614b6
[reactos.git] / reactos / lib / kernel32 / mem / global.c
1 /* $Id$
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
14 #define NDEBUG
15 #include "../include/debug.h"
16
17 #ifdef _GNUC_
18 #define STRUCT_PACK __attribute__((packed))
19 #else
20 #define STRUCT_PACK
21 #endif
22
23 #define MAGIC_GLOBAL_USED 0x5342BEEF
24 #define GLOBAL_LOCK_MAX 0xFF
25
26 /*Wine found that some applications complain if memory isn't 8 byte aligned.
27 * We make use of that experience here.
28 */
29 #define HANDLE_SIZE 8 /*sizeof(HANDLE) *2 */
30
31
32 typedef struct __GLOBAL_LOCAL_HANDLE
33 {
34 DWORD Magic;
35 LPVOID Pointer; STRUCT_PACK
36 BYTE Flags;
37 BYTE LockCount;
38 } GLOBAL_HANDLE, LOCAL_HANDLE, *PGLOBAL_HANDLE, *PLOCAL_HANDLE;
39
40 #define HANDLE_TO_INTERN(h) ((PGLOBAL_HANDLE)(((char *)(h))-4))
41 #define INTERN_TO_HANDLE(i) ((HGLOBAL) &((i)->Pointer))
42 #define POINTER_TO_HANDLE(p) (*(PHANDLE)((ULONG_PTR)p - HANDLE_SIZE))
43 #define ISHANDLE(h) ((((ULONG)(h)) & 0x4)!=0)
44 #define ISPOINTER(h) ((((ULONG)(h)) & 0x4)==0)
45
46
47 static void DbgPrintStruct(PGLOBAL_HANDLE h)
48 {
49 DPRINT("Magic: 0x%X\n", h->Magic);
50 DPRINT("Pointer: 0x%X\n", h->Pointer);
51 DPRINT("Flags: 0x%X\n", h->Flags);
52 DPRINT("LockCount: 0x%X\n", h->LockCount);
53 }
54
55
56
57 /* FUNCTIONS ***************************************************************/
58
59 /*
60 * @implemented
61 */
62 HGLOBAL STDCALL
63 GlobalAlloc(UINT uFlags,
64 DWORD dwBytes)
65 {
66
67 PGLOBAL_HANDLE phandle = 0;
68 PVOID palloc = 0;
69 UINT heap_flags = 0;
70
71 if (uFlags & GMEM_ZEROINIT)
72 {
73 heap_flags = HEAP_ZERO_MEMORY;
74 }
75
76 DPRINT("GlobalAlloc( 0x%X, 0x%lX )\n", uFlags, dwBytes);
77
78 //Changed hProcessHeap to GetProcessHeap()
79 if ((uFlags & GMEM_MOVEABLE)==0) /* POINTER */
80 {
81 palloc = RtlAllocateHeap(GetProcessHeap(), heap_flags, dwBytes);
82 if (! ISPOINTER(palloc))
83 {
84 DPRINT1("GlobalAlloced pointer which is not 8-byte aligned\n");
85 RtlFreeHeap(GetProcessHeap(), 0, palloc);
86 return NULL;
87 }
88 return (HGLOBAL) palloc;
89 }
90 else /* HANDLE */
91 {
92 HeapLock(hProcessHeap);
93
94 phandle = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(GLOBAL_HANDLE));
95 if (phandle)
96 {
97 phandle->Magic = MAGIC_GLOBAL_USED;
98 phandle->Flags = uFlags >> 8;
99 phandle->LockCount = 0;
100 phandle->Pointer = 0;
101
102 if (dwBytes)
103 {
104 palloc = RtlAllocateHeap(GetProcessHeap(), heap_flags, dwBytes + HANDLE_SIZE);
105 if (palloc)
106 {
107 *(PHANDLE)palloc = INTERN_TO_HANDLE(phandle);
108 phandle->Pointer = (PVOID)((ULONG_PTR)palloc + HANDLE_SIZE);
109 }
110 else /*failed to allocate the memory block*/
111 {
112 RtlFreeHeap(GetProcessHeap(), 0, phandle);
113 phandle = 0;
114 }
115 }
116 else
117 {
118 DPRINT("Allocated a 0 size movable block.\n");
119 DbgPrintStruct(phandle);
120 DPRINT("Address of the struct: 0x%X\n", phandle);
121 DPRINT("Address of pointer: 0x%X\n", &(phandle->Pointer));
122 }
123 }
124 HeapUnlock(hProcessHeap);
125
126 if (phandle)
127 {
128 if (ISPOINTER(INTERN_TO_HANDLE(phandle)))
129 {
130 DPRINT1("GlobalAlloced handle which is 8-byte aligned but shouldn't be\n");
131 RtlFreeHeap(GetProcessHeap(), 0, palloc);
132 RtlFreeHeap(GetProcessHeap(), 0, phandle);
133 return NULL;
134 }
135 return INTERN_TO_HANDLE(phandle);
136 }
137 else
138 return (HGLOBAL)0;
139 }
140 }
141
142
143 /*
144 * @implemented
145 */
146 SIZE_T 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 DPRINT("GlobalFlags( 0x%lX )\n", (ULONG)hMem);
173
174 if(!ISHANDLE(hMem))
175 {
176 DPRINT("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 DPRINT1("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 DPRINT1("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, (PVOID)((ULONG_PTR)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 DPRINT("GlobalHandle( 0x%lX )\n", (ULONG)pMem);
263 if (0 == pMem) /*Invalid argument */
264 {
265 SetLastError(ERROR_INVALID_PARAMETER);
266 DPRINT1("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 DPRINT1("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 * @implemented
352 */
353 BOOL
354 STDCALL
355 GlobalMemoryStatusEx(LPMEMORYSTATUSEX lpBuffer)
356 {
357 SYSTEM_BASIC_INFORMATION SysBasicInfo;
358 SYSTEM_PERFORMANCE_INFORMATION SysPerfInfo;
359 ULONG UserMemory;
360 NTSTATUS Status;
361
362 DPRINT("GlobalMemoryStatusEx\n");
363
364 if (lpBuffer->dwLength != sizeof(MEMORYSTATUSEX))
365 {
366 SetLastError(ERROR_INVALID_PARAMETER);
367 return FALSE;
368 }
369
370 Status = ZwQuerySystemInformation(SystemBasicInformation,
371 &SysBasicInfo,
372 sizeof(SysBasicInfo),
373 NULL);
374 if (!NT_SUCCESS(Status))
375 {
376 SetLastErrorByStatus(Status);
377 return FALSE;
378 }
379
380 Status = ZwQuerySystemInformation(SystemPerformanceInformation,
381 &SysPerfInfo,
382 sizeof(SysPerfInfo),
383 NULL);
384 if (!NT_SUCCESS(Status))
385 {
386 SetLastErrorByStatus(Status);
387 return FALSE;
388 }
389
390 Status = ZwQuerySystemInformation(SystemFullMemoryInformation,
391 &UserMemory,
392 sizeof(ULONG),
393 NULL);
394 if (!NT_SUCCESS(Status))
395 {
396 SetLastErrorByStatus(Status);
397 return FALSE;
398 }
399
400 /*
401 * Load percentage 0 thru 100. 0 is good and 100 is bad.
402 *
403 * Um = allocated memory / physical memory
404 * Um = 177 MB / 256 MB = 69.1%
405 *
406 * Mult allocated memory by 100 to move decimal point up.
407 */
408 lpBuffer->dwMemoryLoad = (SysBasicInfo.NumberOfPhysicalPages -
409 SysPerfInfo.AvailablePages) * 100 /
410 SysBasicInfo.NumberOfPhysicalPages;
411
412 DPRINT1("Memory Load: %d%%\n",lpBuffer->dwMemoryLoad );
413
414 lpBuffer->ullTotalPhys = SysBasicInfo.NumberOfPhysicalPages *
415 SysBasicInfo.PageSize;
416 lpBuffer->ullAvailPhys = SysPerfInfo.AvailablePages *
417 SysBasicInfo.PageSize;
418
419 DPRINT("%d\n",SysPerfInfo.AvailablePages );
420 DPRINT("%d\n",lpBuffer->ullAvailPhys );
421
422 lpBuffer->ullTotalPageFile = SysPerfInfo.CommitLimit *
423 SysBasicInfo.PageSize;
424
425 DPRINT("%d\n",lpBuffer->ullTotalPageFile );
426
427 lpBuffer->ullAvailPageFile = ((SysPerfInfo.CommitLimit -
428 SysPerfInfo.CommittedPages) *
429 SysBasicInfo.PageSize);
430
431 /* VM available to the calling processes, User Mem? */
432 lpBuffer->ullTotalVirtual = SysBasicInfo.MaximumUserModeAddress -
433 SysBasicInfo.MinimumUserModeAddress;
434
435 lpBuffer->ullAvailVirtual = (lpBuffer->ullTotalVirtual -
436 (UserMemory *
437 SysBasicInfo.PageSize));
438
439 DPRINT("%d\n",lpBuffer->ullAvailVirtual );
440 DPRINT("%d\n",UserMemory);
441 DPRINT("%d\n",SysBasicInfo.PageSize);
442
443 /* lol! Memory from beyond! */
444 lpBuffer->ullAvailExtendedVirtual = 0;
445 return TRUE;
446 }
447
448 /*
449 * @implemented
450 */
451 VOID STDCALL
452 GlobalMemoryStatus(LPMEMORYSTATUS lpBuffer)
453 {
454 MEMORYSTATUSEX lpBufferEx;
455 #if 0
456 if (lpBuffer->dwLength != sizeof(MEMORYSTATUS))
457 {
458 SetLastError(ERROR_INVALID_PARAMETER);
459 return;
460 }
461 #endif
462 lpBufferEx.dwLength = sizeof(MEMORYSTATUSEX);
463
464 if (GlobalMemoryStatusEx(&lpBufferEx))
465 {
466
467 lpBuffer->dwLength = sizeof(MEMORYSTATUS);
468
469 lpBuffer->dwMemoryLoad = lpBufferEx.dwMemoryLoad;
470 lpBuffer->dwTotalPhys = lpBufferEx.ullTotalPhys;
471 lpBuffer->dwAvailPhys = lpBufferEx.ullAvailPhys;
472 lpBuffer->dwTotalPageFile = lpBufferEx.ullTotalPageFile;
473 lpBuffer->dwAvailPageFile = lpBufferEx.ullAvailPageFile;
474 lpBuffer->dwTotalVirtual = lpBufferEx.ullTotalVirtual;
475 lpBuffer->dwAvailVirtual = lpBufferEx.ullAvailVirtual;
476 }
477 }
478
479
480 HGLOBAL STDCALL
481 GlobalReAlloc(HGLOBAL hMem,
482 DWORD dwBytes,
483 UINT uFlags)
484 {
485
486 LPVOID palloc = 0;
487 HGLOBAL hnew = 0;
488 PGLOBAL_HANDLE phandle = 0;
489 ULONG heap_flags = 0;
490
491 DPRINT("GlobalReAlloc( 0x%lX, 0x%lX, 0x%X )\n", (ULONG)hMem, dwBytes, uFlags);
492
493 if (uFlags & GMEM_ZEROINIT)
494 {
495 heap_flags = HEAP_ZERO_MEMORY;
496 }
497
498 HeapLock(GetProcessHeap());
499
500 if(uFlags & GMEM_MODIFY) /* modify flags */
501 {
502 if( ISPOINTER(hMem) && (uFlags & GMEM_MOVEABLE))
503 {
504 /* make a fixed block moveable
505 * actually only NT is able to do this. And it's soo simple
506 */
507 if (0 == hMem)
508 {
509 SetLastError( ERROR_NOACCESS );
510 hnew = 0;
511 }
512 else
513 {
514 dwBytes = RtlSizeHeap(GetProcessHeap(), 0, (LPVOID) hMem);
515 hnew = GlobalAlloc( uFlags, dwBytes);
516 palloc = GlobalLock(hnew);
517 memcpy(palloc, (LPVOID) hMem, dwBytes);
518 GlobalUnlock(hnew);
519 RtlFreeHeap(GetProcessHeap(),0,hMem);
520 }
521 }
522 else if(ISPOINTER(hMem) && (uFlags & GMEM_DISCARDABLE))
523 {
524 /* change the flags to make our block "discardable" */
525 phandle = HANDLE_TO_INTERN(hMem);
526 phandle->Flags = phandle->Flags | (GMEM_DISCARDABLE >> 8);
527 hnew = hMem;
528 }
529 else
530 {
531 SetLastError(ERROR_INVALID_PARAMETER);
532 hnew = 0;
533 }
534 }
535 else
536 {
537 if(ISPOINTER(hMem))
538 {
539 /* reallocate fixed memory */
540 hnew = (HANDLE)RtlReAllocateHeap(GetProcessHeap(), heap_flags, (LPVOID) hMem, dwBytes);
541 }
542 else
543 {
544 /* reallocate a moveable block */
545 phandle= HANDLE_TO_INTERN(hMem);
546 if (0 != dwBytes)
547 {
548 hnew = hMem;
549 if(phandle->Pointer)
550 {
551 palloc = RtlReAllocateHeap(GetProcessHeap(), heap_flags,
552 (PVOID)((ULONG_PTR)phandle->Pointer - HANDLE_SIZE),
553 dwBytes + HANDLE_SIZE);
554 if (0 == palloc)
555 {
556 hnew = 0;
557 }
558 else
559 {
560 *(PHANDLE)palloc = hMem;
561 phandle->Pointer = (PVOID)((ULONG_PTR)palloc + HANDLE_SIZE);
562 }
563 }
564 else
565 {
566 palloc = RtlAllocateHeap(GetProcessHeap(), heap_flags, dwBytes + HANDLE_SIZE);
567 if (0 == palloc)
568 {
569 hnew = 0;
570 }
571 else
572 {
573 *(PHANDLE)palloc = hMem;
574 phandle->Pointer = (PVOID)((ULONG_PTR)palloc + HANDLE_SIZE);
575 }
576 }
577 }
578 else
579 {
580 if(phandle->Pointer)
581 {
582 RtlFreeHeap(GetProcessHeap(), 0, (PVOID)((ULONG_PTR)phandle->Pointer - HANDLE_SIZE));
583 phandle->Pointer = 0;
584 }
585 }
586 }
587 }
588 HeapUnlock(GetProcessHeap());
589
590 return hnew;
591 }
592
593
594 DWORD STDCALL
595 GlobalSize(HGLOBAL hMem)
596 {
597 SIZE_T retval = 0;
598 PGLOBAL_HANDLE phandle = 0;
599
600 DPRINT("GlobalSize( 0x%lX )\n", (ULONG)hMem);
601
602 if(ISPOINTER(hMem)) /*FIXED*/
603 {
604 retval = RtlSizeHeap(GetProcessHeap(), 0, hMem);
605 }
606 else /*MOVEABLE*/
607 {
608 HeapLock(GetProcessHeap());
609
610 phandle = HANDLE_TO_INTERN(hMem);
611
612 if (MAGIC_GLOBAL_USED == phandle->Magic)
613 {
614 if (0 != phandle->Pointer)/*NOT DISCARDED*/
615 {
616 retval = RtlSizeHeap(GetProcessHeap(), 0, (PVOID)((ULONG_PTR)phandle->Pointer - HANDLE_SIZE));
617
618 if (retval == (SIZE_T)-1) /*RtlSizeHeap failed*/
619 {
620 /*
621 **TODO: RtlSizeHeap does not set last error.
622 ** We should choose an error value to set as
623 ** the last error. Which One?
624 */
625 DPRINT("GlobalSize: RtlSizeHeap failed.\n");
626 retval = 0;
627 }
628 else /*Everything is ok*/
629 {
630 retval = retval - HANDLE_SIZE;
631 }
632 }
633 }
634 else
635 {
636 DPRINT("GlobalSize: invalid handle\n");
637 }
638 HeapUnlock(GetProcessHeap());
639 }
640 return retval;
641 }
642
643
644 /*
645 * @implemented
646 */
647 VOID STDCALL
648 GlobalUnfix(HGLOBAL hMem)
649 {
650 if (hMem != INVALID_HANDLE_VALUE)
651 GlobalUnlock(hMem);
652 }
653
654
655 /*
656 * @implemented
657 */
658 BOOL STDCALL
659 GlobalUnlock(HGLOBAL hMem)
660 {
661
662 PGLOBAL_HANDLE phandle;
663 BOOL locked = FALSE;
664
665 DPRINT("GlobalUnlock( 0x%lX )\n", (ULONG)hMem);
666
667 if(ISPOINTER(hMem))
668 {
669 SetLastError(ERROR_NOT_LOCKED);
670 return FALSE;
671 }
672
673 HeapLock(GetProcessHeap());
674
675 phandle = HANDLE_TO_INTERN(hMem);
676 if(MAGIC_GLOBAL_USED == phandle->Magic)
677 {
678 if (0 >= phandle->LockCount)
679 {
680 locked = FALSE;
681 SetLastError(ERROR_NOT_LOCKED);
682 }
683 else if (GLOBAL_LOCK_MAX > phandle->LockCount)
684 {
685 phandle->LockCount--;
686 locked = (0 != phandle->LockCount) ? TRUE : FALSE;
687 SetLastError(NO_ERROR);
688 }
689 }
690 else
691 {
692 DPRINT("GlobalUnlock: invalid handle\n");
693 locked = FALSE;
694 }
695 HeapUnlock(GetProcessHeap());
696 return locked;
697 }
698
699
700 /*
701 * @implemented
702 */
703 BOOL STDCALL
704 GlobalUnWire(HGLOBAL hMem)
705 {
706 return GlobalUnlock(hMem);
707 }
708
709
710 /*
711 * @implemented
712 */
713 LPVOID STDCALL
714 GlobalWire(HGLOBAL hMem)
715 {
716 return GlobalLock(hMem);
717 }
718
719 /* EOF */