Set last error in GlobalUnlock.
[wine] / memory / global.c
1 /*
2  * Global heap functions
3  *
4  * Copyright 1995 Alexandre Julliard
5  */
6 /* 0xffff sometimes seems to mean: CURRENT_DS */
7
8 #include <sys/types.h>
9 #include <stdlib.h>
10 #include <time.h>
11 #include <stdio.h>
12 #include <unistd.h>
13 #include <string.h>
14
15 #include "wine/winbase16.h"
16 #include "wine/exception.h"
17 #include "wine/port.h"
18 #include "global.h"
19 #include "heap.h"
20 #include "toolhelp.h"
21 #include "selectors.h"
22 #include "miscemu.h"
23 #include "stackframe.h"
24 #include "module.h"
25 #include "debugtools.h"
26 #include "winerror.h"
27
28 DEFAULT_DEBUG_CHANNEL(global);
29
30   /* Global arena block */
31 typedef struct
32 {
33     DWORD     base;          /* Base address (0 if discarded) */
34     DWORD     size;          /* Size in bytes (0 indicates a free block) */
35     HGLOBAL16 handle;        /* Handle for this block */
36     HGLOBAL16 hOwner;        /* Owner of this block */
37     BYTE      lockCount;     /* Count of GlobalFix() calls */
38     BYTE      pageLockCount; /* Count of GlobalPageLock() calls */
39     BYTE      flags;         /* Allocation flags */
40     BYTE      selCount;      /* Number of selectors allocated for this block */
41 } GLOBALARENA;
42
43   /* Flags definitions */
44 #define GA_MOVEABLE     0x02  /* same as GMEM_MOVEABLE */
45 #define GA_DGROUP       0x04
46 #define GA_DISCARDABLE  0x08
47 #define GA_IPCSHARE     0x10  /* same as GMEM_DDESHARE */
48 #define GA_DOSMEM       0x20
49
50   /* Arena array */
51 static GLOBALARENA *pGlobalArena = NULL;
52 static int globalArenaSize = 0;
53
54 #define GLOBAL_MAX_ALLOC_SIZE 0x00ff0000  /* Largest allocation is 16M - 64K */
55
56 #define VALID_HANDLE(handle) (((handle)>>__AHSHIFT)<globalArenaSize)
57 #define GET_ARENA_PTR(handle)  (pGlobalArena + ((handle) >> __AHSHIFT))
58
59 /* filter for page-fault exceptions */
60 /* It is possible for a bogus global pointer to cause a */
61 /* page zero reference, so I include EXCEPTION_PRIV_INSTRUCTION too. */
62
63 static WINE_EXCEPTION_FILTER(page_fault)
64 {
65     switch (GetExceptionCode()) {
66         case (EXCEPTION_ACCESS_VIOLATION):
67         case (EXCEPTION_PRIV_INSTRUCTION):
68            return EXCEPTION_EXECUTE_HANDLER;
69         default:
70            return EXCEPTION_CONTINUE_SEARCH;
71     }
72 }
73
74 /***********************************************************************
75  *           GLOBAL_GetArena
76  *
77  * Return the arena for a given selector, growing the arena array if needed.
78  */
79 static GLOBALARENA *GLOBAL_GetArena( WORD sel, WORD selcount )
80 {
81     if (((sel >> __AHSHIFT) + selcount) > globalArenaSize)
82     {
83         int newsize = ((sel >> __AHSHIFT) + selcount + 0xff) & ~0xff;
84         GLOBALARENA *pNewArena = realloc( pGlobalArena,
85                                           newsize * sizeof(GLOBALARENA) );
86         if (!pNewArena) return 0;
87         pGlobalArena = pNewArena;
88         memset( pGlobalArena + globalArenaSize, 0,
89                 (newsize - globalArenaSize) * sizeof(GLOBALARENA) );
90         globalArenaSize = newsize;
91     }
92     return pGlobalArena + (sel >> __AHSHIFT);
93 }
94
95 void debug_handles(void)
96 {
97     int printed=0;
98     int i;
99     for (i = globalArenaSize-1 ; i>=0 ; i--) {
100         if (pGlobalArena[i].size!=0 && (pGlobalArena[i].handle & 0x8000)){
101             printed=1;
102             DPRINTF("0x%08x, ",pGlobalArena[i].handle);
103         }
104     }
105     if (printed)
106         DPRINTF("\n");
107 }
108
109
110 /***********************************************************************
111  *           GLOBAL_CreateBlock
112  *
113  * Create a global heap block for a fixed range of linear memory.
114  */
115 HGLOBAL16 GLOBAL_CreateBlock( WORD flags, const void *ptr, DWORD size,
116                               HGLOBAL16 hOwner, unsigned char selflags )
117 {
118     WORD sel, selcount;
119     GLOBALARENA *pArena;
120
121       /* Allocate the selector(s) */
122
123     sel = SELECTOR_AllocBlock( ptr, size, selflags );
124     if (!sel) return 0;
125     selcount = (size + 0xffff) / 0x10000;
126
127     if (!(pArena = GLOBAL_GetArena( sel, selcount )))
128     {
129         SELECTOR_FreeBlock( sel );
130         return 0;
131     }
132
133       /* Fill the arena block */
134
135     pArena->base = (DWORD)ptr;
136     pArena->size = GetSelectorLimit16(sel) + 1;
137     pArena->handle = (flags & GMEM_MOVEABLE) ? sel - 1 : sel;
138     pArena->hOwner = hOwner;
139     pArena->lockCount = 0;
140     pArena->pageLockCount = 0;
141     pArena->flags = flags & GA_MOVEABLE;
142     if (flags & GMEM_DISCARDABLE) pArena->flags |= GA_DISCARDABLE;
143     if (flags & GMEM_DDESHARE) pArena->flags |= GA_IPCSHARE;
144     if (!(selflags & (WINE_LDT_FLAGS_CODE^WINE_LDT_FLAGS_DATA))) pArena->flags |= GA_DGROUP;
145     pArena->selCount = selcount;
146     if (selcount > 1)  /* clear the next arena blocks */
147         memset( pArena + 1, 0, (selcount - 1) * sizeof(GLOBALARENA) );
148
149     return pArena->handle;
150 }
151
152
153 /***********************************************************************
154  *           GLOBAL_FreeBlock
155  *
156  * Free a block allocated by GLOBAL_CreateBlock, without touching
157  * the associated linear memory range.
158  */
159 BOOL16 GLOBAL_FreeBlock( HGLOBAL16 handle )
160 {
161     WORD sel;
162     GLOBALARENA *pArena;
163
164     if (!handle) return TRUE;
165     sel = GlobalHandleToSel16( handle ); 
166     if (!VALID_HANDLE(sel))
167         return FALSE;
168     pArena = GET_ARENA_PTR(sel);
169     SELECTOR_FreeBlock( sel );
170     memset( pArena, 0, sizeof(GLOBALARENA) );
171     return TRUE;
172 }
173
174 /***********************************************************************
175  *           GLOBAL_MoveBlock
176  */
177 BOOL16 GLOBAL_MoveBlock( HGLOBAL16 handle, const void *ptr, DWORD size )
178 {
179     WORD sel;
180     GLOBALARENA *pArena;
181
182     if (!handle) return TRUE;
183     sel = GlobalHandleToSel16( handle ); 
184     if (!VALID_HANDLE(sel))
185         return FALSE;
186     pArena = GET_ARENA_PTR(sel);
187     if (pArena->selCount != 1)
188         return FALSE;
189
190     pArena->base = (DWORD)ptr;
191     pArena->size = size;
192     SELECTOR_ReallocBlock( sel, ptr, size );
193     return TRUE;
194 }
195
196 /***********************************************************************
197  *           GLOBAL_Alloc
198  *
199  * Implementation of GlobalAlloc16()
200  */
201 HGLOBAL16 GLOBAL_Alloc( UINT16 flags, DWORD size, HGLOBAL16 hOwner, unsigned char selflags )
202 {
203     void *ptr;
204     HGLOBAL16 handle;
205
206     TRACE("%ld flags=%04x\n", size, flags );
207
208     /* If size is 0, create a discarded block */
209
210     if (size == 0) return GLOBAL_CreateBlock( flags, NULL, 1, hOwner, selflags );
211
212     /* Fixup the size */
213
214     if (size >= GLOBAL_MAX_ALLOC_SIZE - 0x1f) return 0;
215     size = (size + 0x1f) & ~0x1f;
216
217     /* Allocate the linear memory */
218     ptr = HeapAlloc( GetProcessHeap(), 0, size );
219       /* FIXME: free discardable blocks and try again? */
220     if (!ptr) return 0;
221
222       /* Allocate the selector(s) */
223
224     handle = GLOBAL_CreateBlock( flags, ptr, size, hOwner, selflags );
225     if (!handle)
226     {
227         HeapFree( GetProcessHeap(), 0, ptr );
228         return 0;
229     }
230
231     if (flags & GMEM_ZEROINIT) memset( ptr, 0, size );
232     return handle;
233 }
234
235 /***********************************************************************
236  *           GlobalAlloc16   (KERNEL.15)
237  * RETURNS
238  *      Handle: Success
239  *      NULL: Failure
240  */
241 HGLOBAL16 WINAPI GlobalAlloc16(
242                  UINT16 flags, /* [in] Object allocation attributes */
243                  DWORD size    /* [in] Number of bytes to allocate */
244 ) {
245     HANDLE16 owner = GetCurrentPDB16();
246
247     if (flags & GMEM_DDESHARE)
248         owner = GetExePtr(owner);  /* Make it a module handle */
249     return GLOBAL_Alloc( flags, size, owner, WINE_LDT_FLAGS_DATA );
250 }
251
252
253 /***********************************************************************
254  *           GlobalReAlloc16   (KERNEL.16)
255  * RETURNS
256  *      Handle: Success
257  *      NULL: Failure
258  */
259 HGLOBAL16 WINAPI GlobalReAlloc16(
260                  HGLOBAL16 handle, /* [in] Handle of global memory object */
261                  DWORD size,       /* [in] New size of block */
262                  UINT16 flags      /* [in] How to reallocate object */
263 ) {
264     WORD selcount;
265     DWORD oldsize;
266     void *ptr;
267     GLOBALARENA *pArena, *pNewArena;
268     WORD sel = GlobalHandleToSel16( handle );
269
270     TRACE("%04x %ld flags=%04x\n",
271                     handle, size, flags );
272     if (!handle) return 0;
273     
274     if (!VALID_HANDLE(handle)) {
275         WARN("Invalid handle 0x%04x!\n", handle);
276         return 0;
277     }
278     pArena = GET_ARENA_PTR( handle );
279
280       /* Discard the block if requested */
281
282     if ((size == 0) && (flags & GMEM_MOVEABLE) && !(flags & GMEM_MODIFY))
283     {
284         if (!(pArena->flags & GA_MOVEABLE) ||
285             !(pArena->flags & GA_DISCARDABLE) ||
286             (pArena->lockCount > 0) || (pArena->pageLockCount > 0)) return 0;
287         HeapFree( GetProcessHeap(), 0, (void *)pArena->base );
288         pArena->base = 0;
289
290         /* Note: we rely on the fact that SELECTOR_ReallocBlock won't 
291          * change the selector if we are shrinking the block.
292          * FIXME: shouldn't we keep selectors until the block is deleted?
293          */
294         SELECTOR_ReallocBlock( sel, 0, 1 );
295         return handle;
296     }
297
298       /* Fixup the size */
299
300     if (size > GLOBAL_MAX_ALLOC_SIZE - 0x20) return 0;
301     if (size == 0) size = 0x20;
302     else size = (size + 0x1f) & ~0x1f;
303
304       /* Change the flags */
305
306     if (flags & GMEM_MODIFY)
307     {
308           /* Change the flags, leaving GA_DGROUP alone */
309         pArena->flags = (pArena->flags & GA_DGROUP) | (flags & GA_MOVEABLE);
310         if (flags & GMEM_DISCARDABLE) pArena->flags |= GA_DISCARDABLE;
311         return handle;
312     }
313
314       /* Reallocate the linear memory */
315
316     ptr = (void *)pArena->base;
317     oldsize = pArena->size;
318     TRACE("oldsize %08lx\n",oldsize);
319     if (ptr && (size == oldsize)) return handle;  /* Nothing to do */
320
321     if (pArena->flags & GA_DOSMEM)
322         ptr = DOSMEM_ResizeBlock(ptr, size, NULL);
323     else
324         ptr = HeapReAlloc( GetProcessHeap(), 0, ptr, size );
325     if (!ptr)
326     {
327         SELECTOR_FreeBlock( sel );
328         memset( pArena, 0, sizeof(GLOBALARENA) );
329         return 0;
330     }
331
332       /* Reallocate the selector(s) */
333
334     sel = SELECTOR_ReallocBlock( sel, ptr, size );
335     if (!sel)
336     {
337         HeapFree( GetProcessHeap(), 0, ptr );
338         memset( pArena, 0, sizeof(GLOBALARENA) );
339         return 0;
340     }
341     selcount = (size + 0xffff) / 0x10000;
342
343     if (!(pNewArena = GLOBAL_GetArena( sel, selcount )))
344     {
345         HeapFree( GetProcessHeap(), 0, ptr );
346         SELECTOR_FreeBlock( sel );
347         return 0;
348     }
349
350       /* Fill the new arena block */
351
352     if (pNewArena != pArena) memcpy( pNewArena, pArena, sizeof(GLOBALARENA) );
353     pNewArena->base = (DWORD)ptr;
354     pNewArena->size = GetSelectorLimit16(sel) + 1;
355     pNewArena->selCount = selcount;
356     pNewArena->handle = (pNewArena->flags & GA_MOVEABLE) ? sel - 1 : sel;
357
358     if (selcount > 1)  /* clear the next arena blocks */
359         memset( pNewArena + 1, 0, (selcount - 1) * sizeof(GLOBALARENA) );
360
361     if ((oldsize < size) && (flags & GMEM_ZEROINIT))
362         memset( (char *)ptr + oldsize, 0, size - oldsize );
363     return pNewArena->handle;
364 }
365
366
367 /***********************************************************************
368  *           GlobalFree16   (KERNEL.17)
369  * RETURNS
370  *      NULL: Success
371  *      Handle: Failure
372  */
373 HGLOBAL16 WINAPI GlobalFree16(
374                  HGLOBAL16 handle /* [in] Handle of global memory object */
375 ) {
376     void *ptr;
377
378     if (!VALID_HANDLE(handle)) {
379         WARN("Invalid handle 0x%04x passed to GlobalFree16!\n",handle);
380         return 0;
381     }
382     ptr = (void *)GET_ARENA_PTR(handle)->base;
383
384     TRACE("%04x\n", handle );
385     if (!GLOBAL_FreeBlock( handle )) return handle;  /* failed */
386     if (ptr) HeapFree( GetProcessHeap(), 0, ptr );
387     return 0;
388 }
389
390
391 /***********************************************************************
392  *           WIN16_GlobalLock16   (KERNEL.18)
393  *
394  * This is the GlobalLock16() function used by 16-bit code.
395  */
396 SEGPTR WINAPI WIN16_GlobalLock16( HGLOBAL16 handle )
397 {
398     WORD sel = GlobalHandleToSel16( handle );
399     TRACE("(%04x) -> %08lx\n", handle, MAKELONG( 0, sel ) );
400
401     if (handle)
402     {
403         if (handle == (HGLOBAL16)-1) handle = CURRENT_DS;
404
405         if (!VALID_HANDLE(handle)) {
406             WARN("Invalid handle 0x%04x passed to WIN16_GlobalLock16!\n",handle);
407             sel = 0;
408         }
409         else if (!GET_ARENA_PTR(handle)->base) 
410             sel = 0;
411         else
412             GET_ARENA_PTR(handle)->lockCount++;
413     }
414
415     CURRENT_STACK16->ecx = sel;  /* selector must be returned in CX as well */
416     return MAKESEGPTR( sel, 0 );
417 }
418
419
420 /***********************************************************************
421  *           GlobalLock16   (KERNEL.18)
422  *
423  * This is the GlobalLock16() function used by 32-bit code.
424  * 
425  * RETURNS
426  *      Pointer to first byte of memory block
427  *      NULL: Failure
428  */
429 LPVOID WINAPI GlobalLock16(
430               HGLOBAL16 handle /* [in] Handle of global memory object */
431 ) {
432     if (!handle) return 0;
433     if (!VALID_HANDLE(handle))
434         return (LPVOID)0;
435     GET_ARENA_PTR(handle)->lockCount++;
436     return (LPVOID)GET_ARENA_PTR(handle)->base;
437 }
438
439
440 /***********************************************************************
441  *           GlobalUnlock16   (KERNEL.19)
442  * NOTES
443  *      Should the return values be cast to booleans?
444  *
445  * RETURNS
446  *      TRUE: Object is still locked
447  *      FALSE: Object is unlocked
448  */
449 BOOL16 WINAPI GlobalUnlock16(
450               HGLOBAL16 handle /* [in] Handle of global memory object */
451 ) {
452     GLOBALARENA *pArena = GET_ARENA_PTR(handle);
453     if (!VALID_HANDLE(handle)) {
454         WARN("Invalid handle 0x%04x passed to GlobalUnlock16!\n",handle);
455         return 0;
456     }
457     TRACE("%04x\n", handle );
458     if (pArena->lockCount) pArena->lockCount--;
459     return pArena->lockCount;
460 }
461
462 /***********************************************************************
463  *     GlobalChangeLockCount               (KERNEL.365)
464  *
465  * This is declared as a register function as it has to preserve
466  * *all* registers, even AX/DX !
467  *
468  */
469 void WINAPI GlobalChangeLockCount16( HGLOBAL16 handle, INT16 delta,
470                                      CONTEXT86 *context )
471 {
472     if ( delta == 1 )
473         GlobalLock16( handle );
474     else if ( delta == -1 )
475         GlobalUnlock16( handle );
476     else
477         ERR("(%04X, %d): strange delta value\n", handle, delta );
478 }
479
480 /***********************************************************************
481  *           GlobalSize16   (KERNEL.20)
482  * RETURNS
483  *      Size in bytes of object
484  *      0: Failure
485  */
486 DWORD WINAPI GlobalSize16(
487              HGLOBAL16 handle /* [in] Handle of global memory object */
488 ) {
489     TRACE("%04x\n", handle );
490     if (!handle) return 0;
491     if (!VALID_HANDLE(handle))
492         return 0;
493     return GET_ARENA_PTR(handle)->size;
494 }
495
496
497 /***********************************************************************
498  *           GlobalHandle16   (KERNEL.21)
499  * NOTES
500  *      Why is GlobalHandleToSel used here with the sel as input?
501  *
502  * RETURNS
503  *      Handle: Success
504  *      NULL: Failure
505  */
506 DWORD WINAPI GlobalHandle16(
507              WORD sel /* [in] Address of global memory block */
508 ) {
509     TRACE("%04x\n", sel );
510     if (!VALID_HANDLE(sel)) {
511         WARN("Invalid handle 0x%04x passed to GlobalHandle16!\n",sel);
512         return 0;
513     }
514     return MAKELONG( GET_ARENA_PTR(sel)->handle, GlobalHandleToSel16(sel) );
515 }
516
517 /***********************************************************************
518  *           GlobalHandleNoRIP   (KERNEL.159)
519  */
520 DWORD WINAPI GlobalHandleNoRIP16( WORD sel )
521 {
522     int i;
523     for (i = globalArenaSize-1 ; i>=0 ; i--) {
524         if (pGlobalArena[i].size!=0 && pGlobalArena[i].handle == sel)
525                 return MAKELONG( GET_ARENA_PTR(sel)->handle, GlobalHandleToSel16(sel) );
526     }
527     return 0;
528 }
529
530
531 /***********************************************************************
532  *           GlobalFlags16   (KERNEL.22)
533  * NOTES
534  *      Should this return GMEM_INVALID_HANDLE instead of 0 on invalid
535  *      handle?
536  *
537  * RETURNS
538  *      Value specifying flags and lock count
539  *      GMEM_INVALID_HANDLE: Invalid handle
540  */
541 UINT16 WINAPI GlobalFlags16(
542               HGLOBAL16 handle /* [in] Handle of global memory object */
543 ) {
544     GLOBALARENA *pArena;
545
546     TRACE("%04x\n", handle );
547     if (!VALID_HANDLE(handle)) {
548         WARN("Invalid handle 0x%04x passed to GlobalFlags16!\n",handle);
549         return 0;
550     }
551     pArena = GET_ARENA_PTR(handle);
552     return pArena->lockCount |
553            ((pArena->flags & GA_DISCARDABLE) ? GMEM_DISCARDABLE : 0) |
554            ((pArena->base == 0) ? GMEM_DISCARDED : 0);
555 }
556
557
558 /***********************************************************************
559  *           LockSegment16   (KERNEL.23)
560  */
561 HGLOBAL16 WINAPI LockSegment16( HGLOBAL16 handle )
562 {
563     TRACE("%04x\n", handle );
564     if (handle == (HGLOBAL16)-1) handle = CURRENT_DS;
565     if (!VALID_HANDLE(handle)) {
566         WARN("Invalid handle 0x%04x passed to LockSegment16!\n",handle);
567         return 0;
568     }
569     GET_ARENA_PTR(handle)->lockCount++;
570     return handle;
571 }
572
573
574 /***********************************************************************
575  *           UnlockSegment16   (KERNEL.24)
576  */
577 void WINAPI UnlockSegment16( HGLOBAL16 handle )
578 {
579     TRACE("%04x\n", handle );
580     if (handle == (HGLOBAL16)-1) handle = CURRENT_DS;
581     if (!VALID_HANDLE(handle)) {
582         WARN("Invalid handle 0x%04x passed to UnlockSegment16!\n",handle);
583         return;
584     }
585     GET_ARENA_PTR(handle)->lockCount--;
586     /* FIXME: this ought to return the lock count in CX (go figure...) */
587 }
588
589
590 /***********************************************************************
591  *           GlobalCompact16   (KERNEL.25)
592  */
593 DWORD WINAPI GlobalCompact16( DWORD desired )
594 {
595     return GLOBAL_MAX_ALLOC_SIZE;
596 }
597
598
599 /***********************************************************************
600  *           GlobalFreeAll   (KERNEL.26)
601  */
602 void WINAPI GlobalFreeAll16( HGLOBAL16 owner )
603 {
604     DWORD i;
605     GLOBALARENA *pArena;
606
607     pArena = pGlobalArena;
608     for (i = 0; i < globalArenaSize; i++, pArena++)
609     {
610         if ((pArena->size != 0) && (pArena->hOwner == owner))
611             GlobalFree16( pArena->handle );
612     }
613 }
614
615
616 /***********************************************************************
617  *           GlobalWire16   (KERNEL.111)
618  */
619 SEGPTR WINAPI GlobalWire16( HGLOBAL16 handle )
620 {
621     return WIN16_GlobalLock16( handle );
622 }
623
624
625 /***********************************************************************
626  *           GlobalUnWire16   (KERNEL.112)
627  */
628 BOOL16 WINAPI GlobalUnWire16( HGLOBAL16 handle )
629 {
630     return !GlobalUnlock16( handle );
631 }
632
633
634 /***********************************************************************
635  *           SetSwapAreaSize16   (KERNEL.106)
636  */
637 LONG WINAPI SetSwapAreaSize16( WORD size )
638 {
639     FIXME("(%d) - stub!\n", size );
640     return MAKELONG( size, 0xffff );
641 }
642
643
644 /***********************************************************************
645  *           GlobalLRUOldest   (KERNEL.163)
646  */
647 HGLOBAL16 WINAPI GlobalLRUOldest16( HGLOBAL16 handle )
648 {
649     TRACE("%04x\n", handle );
650     if (handle == (HGLOBAL16)-1) handle = CURRENT_DS;
651     return handle;
652 }
653
654
655 /***********************************************************************
656  *           GlobalLRUNewest   (KERNEL.164)
657  */
658 HGLOBAL16 WINAPI GlobalLRUNewest16( HGLOBAL16 handle )
659 {
660     TRACE("%04x\n", handle );
661     if (handle == (HGLOBAL16)-1) handle = CURRENT_DS;
662     return handle;
663 }
664
665
666 /***********************************************************************
667  *           GetFreeSpace16   (KERNEL.169)
668  */
669 DWORD WINAPI GetFreeSpace16( UINT16 wFlags )
670 {
671     MEMORYSTATUS ms;
672     GlobalMemoryStatus( &ms );
673     return ms.dwAvailVirtual;
674 }
675
676 /***********************************************************************
677  *           GlobalDOSAlloc   (KERNEL.184)
678  * RETURNS
679  *      Address (HW=Paragraph segment; LW=Selector)
680  */
681 DWORD WINAPI GlobalDOSAlloc16(
682              DWORD size /* [in] Number of bytes to be allocated */
683 ) {
684    UINT16    uParagraph;
685    LPVOID    lpBlock = DOSMEM_GetBlock( size, &uParagraph );
686
687    if( lpBlock )
688    {
689        HMODULE16 hModule = GetModuleHandle16("KERNEL");
690        WORD      wSelector;
691        GLOBALARENA *pArena;
692    
693        wSelector = GLOBAL_CreateBlock(GMEM_FIXED, lpBlock, size, hModule, WINE_LDT_FLAGS_DATA );
694        pArena = GET_ARENA_PTR(wSelector);
695        pArena->flags |= GA_DOSMEM;
696        return MAKELONG(wSelector,uParagraph);
697    }
698    return 0;
699 }
700
701
702 /***********************************************************************
703  *           GlobalDOSFree      (KERNEL.185)
704  * RETURNS
705  *      NULL: Success
706  *      sel: Failure
707  */
708 WORD WINAPI GlobalDOSFree16(
709             WORD sel /* [in] Selector */
710 ) {
711    DWORD   block = GetSelectorBase(sel);
712
713    if( block && block < 0x100000 ) 
714    {
715        LPVOID lpBlock = DOSMEM_MapDosToLinear( block );
716        if( DOSMEM_FreeBlock( lpBlock ) )
717            GLOBAL_FreeBlock( sel );
718        sel = 0;
719    }
720    return sel;
721 }
722
723
724 /***********************************************************************
725  *           GlobalPageLock   (KERNEL.191)
726  */
727 WORD WINAPI GlobalPageLock16( HGLOBAL16 handle )
728 {
729     TRACE("%04x\n", handle );
730     if (!VALID_HANDLE(handle)) {
731         WARN("Invalid handle 0x%04x passed to GlobalPageLock!\n",handle);
732         return 0;
733     }
734     return ++(GET_ARENA_PTR(handle)->pageLockCount);
735 }
736
737
738 /***********************************************************************
739  *           GlobalPageUnlock   (KERNEL.192)
740  */
741 WORD WINAPI GlobalPageUnlock16( HGLOBAL16 handle )
742 {
743     TRACE("%04x\n", handle );
744     if (!VALID_HANDLE(handle)) {
745         WARN("Invalid handle 0x%04x passed to GlobalPageUnlock!\n",handle);
746         return 0;
747     }
748     return --(GET_ARENA_PTR(handle)->pageLockCount);
749 }
750
751
752 /***********************************************************************
753  *           GlobalFix16   (KERNEL.197)
754  */
755 WORD WINAPI GlobalFix16( HGLOBAL16 handle )
756 {
757     TRACE("%04x\n", handle );
758     if (!VALID_HANDLE(handle)) {
759         WARN("Invalid handle 0x%04x passed to GlobalFix16!\n",handle);
760         return 0;
761     }
762     GET_ARENA_PTR(handle)->lockCount++;
763
764     return GlobalHandleToSel16(handle);
765 }
766
767
768 /***********************************************************************
769  *           GlobalUnfix16   (KERNEL.198)
770  */
771 void WINAPI GlobalUnfix16( HGLOBAL16 handle )
772 {
773     TRACE("%04x\n", handle );
774     if (!VALID_HANDLE(handle)) {
775         WARN("Invalid handle 0x%04x passed to GlobalUnfix16!\n",handle);
776         return;
777     }
778     GET_ARENA_PTR(handle)->lockCount--;
779 }
780
781
782 /***********************************************************************
783  *           FarSetOwner   (KERNEL.403)
784  */
785 void WINAPI FarSetOwner16( HGLOBAL16 handle, HANDLE16 hOwner )
786 {
787     if (!VALID_HANDLE(handle)) {
788         WARN("Invalid handle 0x%04x passed to FarSetOwner!\n",handle);
789         return;
790     }
791     GET_ARENA_PTR(handle)->hOwner = hOwner;
792 }
793
794
795 /***********************************************************************
796  *           FarGetOwner   (KERNEL.404)
797  */
798 HANDLE16 WINAPI FarGetOwner16( HGLOBAL16 handle )
799 {
800     if (!VALID_HANDLE(handle)) {
801         WARN("Invalid handle 0x%04x passed to FarGetOwner!\n",handle);
802         return 0;
803     }
804     return GET_ARENA_PTR(handle)->hOwner;
805 }
806
807
808 /***********************************************************************
809  *           GlobalHandleToSel   (TOOLHELP.50)
810  */
811 WORD WINAPI GlobalHandleToSel16( HGLOBAL16 handle )
812 {
813     if (!handle) return 0;
814     if (!VALID_HANDLE(handle)) {
815         WARN("Invalid handle 0x%04x passed to GlobalHandleToSel!\n",handle);
816         return 0;
817     }
818     if (!(handle & 7))
819     {
820         WARN("Program attempted invalid selector conversion\n" );
821         return handle - 1;
822     }
823     return handle | 7;
824 }
825
826
827 /***********************************************************************
828  *           GlobalFirst   (TOOLHELP.51)
829  */
830 BOOL16 WINAPI GlobalFirst16( GLOBALENTRY *pGlobal, WORD wFlags )
831 {
832     if (wFlags == GLOBAL_LRU) return FALSE;
833     pGlobal->dwNext = 0;
834     return GlobalNext16( pGlobal, wFlags );
835 }
836
837
838 /***********************************************************************
839  *           GlobalNext   (TOOLHELP.52)
840  */
841 BOOL16 WINAPI GlobalNext16( GLOBALENTRY *pGlobal, WORD wFlags)
842 {
843     GLOBALARENA *pArena;
844
845     if (pGlobal->dwNext >= globalArenaSize) return FALSE;
846     pArena = pGlobalArena + pGlobal->dwNext;
847     if (wFlags == GLOBAL_FREE)  /* only free blocks */
848     {
849         int i;
850         for (i = pGlobal->dwNext; i < globalArenaSize; i++, pArena++)
851             if (pArena->size == 0) break;  /* block is free */
852         if (i >= globalArenaSize) return FALSE;
853         pGlobal->dwNext = i;
854     }
855
856     pGlobal->dwAddress    = pArena->base;
857     pGlobal->dwBlockSize  = pArena->size;
858     pGlobal->hBlock       = pArena->handle;
859     pGlobal->wcLock       = pArena->lockCount;
860     pGlobal->wcPageLock   = pArena->pageLockCount;
861     pGlobal->wFlags       = (GetCurrentPDB16() == pArena->hOwner);
862     pGlobal->wHeapPresent = FALSE;
863     pGlobal->hOwner       = pArena->hOwner;
864     pGlobal->wType        = GT_UNKNOWN;
865     pGlobal->wData        = 0;
866     pGlobal->dwNext++;
867     return TRUE;
868 }
869
870
871 /***********************************************************************
872  *           GlobalInfo   (TOOLHELP.53)
873  */
874 BOOL16 WINAPI GlobalInfo16( GLOBALINFO *pInfo )
875 {
876     int i;
877     GLOBALARENA *pArena;
878
879     pInfo->wcItems = globalArenaSize;
880     pInfo->wcItemsFree = 0;
881     pInfo->wcItemsLRU = 0;
882     for (i = 0, pArena = pGlobalArena; i < globalArenaSize; i++, pArena++)
883         if (pArena->size == 0) pInfo->wcItemsFree++;
884     return TRUE;
885 }
886
887
888 /***********************************************************************
889  *           GlobalEntryHandle   (TOOLHELP.54)
890  */
891 BOOL16 WINAPI GlobalEntryHandle16( GLOBALENTRY *pGlobal, HGLOBAL16 hItem )
892 {
893     GLOBALARENA *pArena = GET_ARENA_PTR(hItem);
894
895     pGlobal->dwAddress    = pArena->base;
896     pGlobal->dwBlockSize  = pArena->size;
897     pGlobal->hBlock       = pArena->handle;
898     pGlobal->wcLock       = pArena->lockCount;
899     pGlobal->wcPageLock   = pArena->pageLockCount;
900     pGlobal->wFlags       = (GetCurrentPDB16() == pArena->hOwner);
901     pGlobal->wHeapPresent = FALSE;
902     pGlobal->hOwner       = pArena->hOwner;
903     pGlobal->wType        = GT_UNKNOWN;
904     pGlobal->wData        = 0;
905     pGlobal->dwNext++;
906     return TRUE;
907 }
908
909
910 /***********************************************************************
911  *           GlobalEntryModule   (TOOLHELP.55)
912  */
913 BOOL16 WINAPI GlobalEntryModule16( GLOBALENTRY *pGlobal, HMODULE16 hModule,
914                                  WORD wSeg )
915 {
916     FIXME("(%p, 0x%04x, 0x%04x), stub.\n", pGlobal, hModule, wSeg);
917     return FALSE;
918 }
919
920
921 /***********************************************************************
922  *           MemManInfo   (TOOLHELP.72)
923  */
924 BOOL16 WINAPI MemManInfo16( MEMMANINFO *info )
925 {
926     MEMORYSTATUS status;
927
928     /*
929      * Not unsurprisingly although the documention says you 
930      * _must_ provide the size in the dwSize field, this function
931      * (under Windows) always fills the structure and returns true.
932      */
933     GlobalMemoryStatus( &status );
934     info->wPageSize            = getpagesize();
935     info->dwLargestFreeBlock   = status.dwAvailVirtual;
936     info->dwMaxPagesAvailable  = info->dwLargestFreeBlock / info->wPageSize;
937     info->dwMaxPagesLockable   = info->dwMaxPagesAvailable;
938     info->dwTotalLinearSpace   = status.dwTotalVirtual / info->wPageSize;
939     info->dwTotalUnlockedPages = info->dwTotalLinearSpace;
940     info->dwFreePages          = info->dwMaxPagesAvailable;
941     info->dwTotalPages         = info->dwTotalLinearSpace;
942     info->dwFreeLinearSpace    = info->dwMaxPagesAvailable;
943     info->dwSwapFilePages      = status.dwTotalPageFile / info->wPageSize;
944     return TRUE;
945 }
946
947 /***********************************************************************
948  *           GetFreeMemInfo   (KERNEL.316)
949  */
950 DWORD WINAPI GetFreeMemInfo16(void)
951 {
952     MEMMANINFO info;
953     MemManInfo16( &info );
954     return MAKELONG( info.dwTotalLinearSpace, info.dwMaxPagesAvailable );
955 }
956
957 /*
958  * Win32 Global heap functions (GlobalXXX).
959  * These functions included in Win32 for compatibility with 16 bit Windows
960  * Especially the moveable blocks and handles are oldish. 
961  * But the ability to directly allocate memory with GPTR and LPTR is widely
962  * used.
963  *
964  * The handle stuff looks horrible, but it's implemented almost like Win95
965  * does it. 
966  *
967  */
968
969 #define MAGIC_GLOBAL_USED 0x5342
970 #define GLOBAL_LOCK_MAX   0xFF
971 #define HANDLE_TO_INTERN(h)  ((PGLOBAL32_INTERN)(((char *)(h))-2))
972 #define INTERN_TO_HANDLE(i)  ((HGLOBAL) &((i)->Pointer))
973 #define POINTER_TO_HANDLE(p) (*(((HGLOBAL *)(p))-1))
974 #define ISHANDLE(h)          (((DWORD)(h)&2)!=0)
975 #define ISPOINTER(h)         (((DWORD)(h)&2)==0)
976
977 typedef struct __GLOBAL32_INTERN
978 {
979    WORD         Magic;
980    LPVOID       Pointer WINE_PACKED;
981    BYTE         Flags;
982    BYTE         LockCount;
983 } GLOBAL32_INTERN, *PGLOBAL32_INTERN;
984
985 /***********************************************************************
986  *           GLOBAL_GetHeap
987  *
988  * Returns the appropriate heap to be used. If the object was created
989  * With GMEM_DDESHARE we allocated it on the system heap.
990  */
991 static HANDLE GLOBAL_GetHeap( HGLOBAL hmem )
992 {
993    HANDLE heap;
994     
995    TRACE("() hmem=%x\n", hmem);
996    
997    /* Get the appropriate heap to be used for this object */
998    if (ISPOINTER(hmem))
999       heap = GetProcessHeap();
1000    else
1001    {
1002       PGLOBAL32_INTERN pintern;
1003       pintern=HANDLE_TO_INTERN(hmem);
1004       
1005       /* If it was DDESHARE it was created on the shared system heap */
1006       pintern=HANDLE_TO_INTERN(hmem);
1007       heap = ( pintern->Flags & (GMEM_DDESHARE >> 8) )
1008            ? SystemHeap : GetProcessHeap();
1009    }
1010
1011    return heap;
1012 }
1013
1014 /***********************************************************************
1015  *           GlobalAlloc   (KERNEL32.315)
1016  * RETURNS
1017  *      Handle: Success
1018  *      NULL: Failure
1019  */
1020 HGLOBAL WINAPI GlobalAlloc(
1021                  UINT flags, /* [in] Object allocation attributes */
1022                  DWORD size    /* [in] Number of bytes to allocate */
1023 ) {
1024    PGLOBAL32_INTERN     pintern;
1025    DWORD                hpflags;
1026    LPVOID               palloc;
1027
1028    if(flags&GMEM_ZEROINIT)
1029       hpflags=HEAP_ZERO_MEMORY;
1030    else
1031       hpflags=0;
1032    
1033    TRACE("() flags=%04x\n",  flags );
1034    
1035    if((flags & GMEM_MOVEABLE)==0) /* POINTER */
1036    {
1037       palloc=HeapAlloc(GetProcessHeap(), hpflags, size);
1038       return (HGLOBAL) palloc;
1039    }
1040    else  /* HANDLE */
1041    {
1042       HANDLE heap;
1043        
1044       /* If DDESHARE is set, create on the shared system heap */
1045       heap = (flags & GMEM_DDESHARE) ? SystemHeap : GetProcessHeap();
1046
1047       /* HeapLock(heap); */
1048
1049       pintern=HeapAlloc(heap, 0,  sizeof(GLOBAL32_INTERN));
1050       if (!pintern) return 0;
1051       if(size)
1052       {
1053          size = (size + 0x1f) & ~0x1f;
1054          if (!(palloc=HeapAlloc(heap, hpflags, size+sizeof(HGLOBAL)))) {
1055             HeapFree(heap, 0, pintern);
1056             return 0;
1057          }
1058          *(HGLOBAL *)palloc=INTERN_TO_HANDLE(pintern);
1059          pintern->Pointer=(char *) palloc+sizeof(HGLOBAL);
1060       }
1061       else
1062          pintern->Pointer=NULL;
1063       pintern->Magic=MAGIC_GLOBAL_USED;
1064       pintern->Flags=flags>>8;
1065       pintern->LockCount=0;
1066       
1067       /* HeapUnlock(heap); */
1068        
1069       return INTERN_TO_HANDLE(pintern);
1070    }
1071 }
1072
1073
1074 /***********************************************************************
1075  *           GlobalLock   (KERNEL32.326)
1076  * RETURNS
1077  *      Pointer to first byte of block
1078  *      NULL: Failure
1079  */
1080 LPVOID WINAPI GlobalLock(
1081               HGLOBAL hmem /* [in] Handle of global memory object */
1082 ) {
1083    PGLOBAL32_INTERN pintern;
1084    LPVOID           palloc;
1085
1086    if(ISPOINTER(hmem))
1087       return (LPVOID) hmem;
1088
1089    /* HeapLock(GetProcessHeap()); */
1090    
1091    pintern=HANDLE_TO_INTERN(hmem);
1092    if(pintern->Magic==MAGIC_GLOBAL_USED)
1093    {
1094       if(pintern->LockCount<GLOBAL_LOCK_MAX)
1095          pintern->LockCount++;
1096       palloc=pintern->Pointer;
1097    }
1098    else
1099    {
1100       WARN("invalid handle\n");
1101       palloc=(LPVOID) NULL;
1102       SetLastError(ERROR_INVALID_HANDLE);
1103    }
1104    /* HeapUnlock(GetProcessHeap()); */;
1105    return palloc;
1106 }
1107
1108
1109 /***********************************************************************
1110  *           GlobalUnlock   (KERNEL32.332)
1111  * RETURNS
1112  *      TRUE: Object is still locked
1113  *      FALSE: Object is unlocked
1114  */
1115 BOOL WINAPI GlobalUnlock(
1116               HGLOBAL hmem /* [in] Handle of global memory object */
1117 ) {
1118    PGLOBAL32_INTERN       pintern;
1119    BOOL                 locked;
1120
1121    if(ISPOINTER(hmem))
1122       return FALSE;
1123
1124    /* HeapLock(GetProcessHeap()); */
1125    pintern=HANDLE_TO_INTERN(hmem);
1126    
1127    if(pintern->Magic==MAGIC_GLOBAL_USED)
1128    {
1129       if((pintern->LockCount<GLOBAL_LOCK_MAX)&&(pintern->LockCount>0))
1130          pintern->LockCount--;
1131
1132       locked = (pintern->LockCount != 0);
1133       if (!locked) SetLastError(NO_ERROR);
1134    }
1135    else
1136    {
1137       WARN("invalid handle\n");
1138       SetLastError(ERROR_INVALID_HANDLE);
1139       locked=FALSE;
1140    }
1141    /* HeapUnlock(GetProcessHeap()); */
1142    return locked;
1143 }
1144
1145
1146 /***********************************************************************
1147  *           GlobalHandle   (KERNEL32.325)
1148  * Returns the handle associated with the specified pointer.
1149  *
1150  * RETURNS
1151  *      Handle: Success
1152  *      NULL: Failure
1153  */
1154 HGLOBAL WINAPI GlobalHandle(
1155                  LPCVOID pmem /* [in] Pointer to global memory block */
1156 ) {
1157     HGLOBAL handle;
1158     HANDLE heap;
1159     PGLOBAL32_INTERN  maybe_intern;
1160     LPCVOID test;
1161
1162     if (!pmem)
1163     {
1164         SetLastError( ERROR_INVALID_PARAMETER );
1165         return 0;
1166     }
1167
1168     __TRY
1169     {
1170         handle = 0;
1171
1172         /* note that if pmem is a pointer to a a block allocated by        */
1173         /* GlobalAlloc with GMEM_MOVEABLE then magic test in HeapValidate  */
1174         /* will fail.                                                      */
1175         if (ISPOINTER(pmem)) {
1176             heap = GLOBAL_GetHeap( (HGLOBAL)pmem );
1177             if (HeapValidate( heap, 0, pmem )) {
1178                 handle = (HGLOBAL)pmem;  /* valid fixed block */
1179                 break;
1180             }
1181             handle = POINTER_TO_HANDLE(pmem);
1182         } else
1183             handle = (HGLOBAL)pmem;
1184
1185         /* Now test handle either passed in or retrieved from pointer */
1186         heap = GLOBAL_GetHeap( handle );
1187         maybe_intern = HANDLE_TO_INTERN( handle );
1188         if (maybe_intern->Magic == MAGIC_GLOBAL_USED) {
1189             test = maybe_intern->Pointer;
1190             if (HeapValidate( heap, 0, ((HGLOBAL *)test)-1 ) && /* obj(-handle) valid arena? */
1191                 HeapValidate( heap, 0, maybe_intern ))  /* intern valid arena? */
1192                 break;  /* valid moveable block */
1193         }
1194         handle = 0;
1195         SetLastError( ERROR_INVALID_HANDLE );
1196     }
1197     __EXCEPT(page_fault)
1198     {
1199         SetLastError( ERROR_INVALID_HANDLE );
1200         return 0;
1201     }
1202     __ENDTRY
1203
1204     return handle;
1205 }
1206
1207
1208 /***********************************************************************
1209  *           GlobalReAlloc   (KERNEL32.328)
1210  * RETURNS
1211  *      Handle: Success
1212  *      NULL: Failure
1213  */
1214 HGLOBAL WINAPI GlobalReAlloc(
1215                  HGLOBAL hmem, /* [in] Handle of global memory object */
1216                  DWORD size,     /* [in] New size of block */
1217                  UINT flags    /* [in] How to reallocate object */
1218 ) {
1219    LPVOID               palloc;
1220    HGLOBAL            hnew;
1221    PGLOBAL32_INTERN     pintern;
1222    HANDLE heap = GLOBAL_GetHeap( hmem );
1223    DWORD heap_flags = (flags & GMEM_ZEROINIT) ? HEAP_ZERO_MEMORY : 0;
1224
1225    hnew = 0;
1226    /* HeapLock(heap); */
1227    if(flags & GMEM_MODIFY) /* modify flags */
1228    {
1229       if( ISPOINTER(hmem) && (flags & GMEM_MOVEABLE))
1230       {
1231          /* make a fixed block moveable
1232           * actually only NT is able to do this. But it's soo simple
1233           */
1234          if (hmem == 0)
1235          {
1236              ERR("GlobalReAlloc32 with null handle!\n");
1237              SetLastError( ERROR_NOACCESS );
1238              return 0;
1239          }
1240          size=HeapSize(heap, 0, (LPVOID) hmem);
1241          hnew=GlobalAlloc( flags, size);
1242          palloc=GlobalLock(hnew);
1243          memcpy(palloc, (LPVOID) hmem, size);
1244          GlobalUnlock(hnew);
1245          GlobalFree(hmem);
1246       }
1247       else if( ISPOINTER(hmem) &&(flags & GMEM_DISCARDABLE))
1248       {
1249          /* change the flags to make our block "discardable" */
1250          pintern=HANDLE_TO_INTERN(hmem);
1251          pintern->Flags = pintern->Flags | (GMEM_DISCARDABLE >> 8);
1252          hnew=hmem;
1253       }
1254       else
1255       {
1256          SetLastError(ERROR_INVALID_PARAMETER);
1257          hnew = 0;
1258       }
1259    }
1260    else
1261    {
1262       if(ISPOINTER(hmem))
1263       {
1264          /* reallocate fixed memory */
1265          hnew=(HGLOBAL)HeapReAlloc(heap, heap_flags, (LPVOID) hmem, size);
1266       }
1267       else
1268       {
1269          /* reallocate a moveable block */
1270          pintern=HANDLE_TO_INTERN(hmem);
1271
1272 #if 0
1273 /* Apparently Windows doesn't care whether the handle is locked at this point */
1274 /* See also the same comment in GlobalFree() */
1275          if(pintern->LockCount>1) {
1276             ERR("handle 0x%08lx is still locked, cannot realloc!\n",(DWORD)hmem);
1277             SetLastError(ERROR_INVALID_HANDLE);
1278          } else
1279 #endif
1280          if(size!=0)
1281          {
1282             hnew=hmem;
1283             if(pintern->Pointer)
1284             {
1285                if((palloc = HeapReAlloc(heap, heap_flags,
1286                                    (char *) pintern->Pointer-sizeof(HGLOBAL),
1287                                    size+sizeof(HGLOBAL))) == NULL)
1288                    return 0; /* Block still valid */
1289                pintern->Pointer=(char *) palloc+sizeof(HGLOBAL);
1290             }
1291             else
1292             {
1293                 if((palloc=HeapAlloc(heap, heap_flags, size+sizeof(HGLOBAL)))
1294                    == NULL)
1295                     return 0;
1296                *(HGLOBAL *)palloc=hmem;
1297                pintern->Pointer=(char *) palloc+sizeof(HGLOBAL);
1298             }
1299          }
1300          else
1301          {
1302             if(pintern->Pointer)
1303             {
1304                HeapFree(heap, 0, (char *) pintern->Pointer-sizeof(HGLOBAL));
1305                pintern->Pointer=NULL;
1306             }
1307          }
1308       }
1309    }
1310    /* HeapUnlock(heap); */
1311    return hnew;
1312 }
1313
1314
1315 /***********************************************************************
1316  *           GlobalFree   (KERNEL32.322)
1317  * RETURNS
1318  *      NULL: Success
1319  *      Handle: Failure
1320  */
1321 HGLOBAL WINAPI GlobalFree(
1322                  HGLOBAL hmem /* [in] Handle of global memory object */
1323 ) {
1324    PGLOBAL32_INTERN pintern;
1325    HGLOBAL        hreturned = 0;
1326    HANDLE heap = GLOBAL_GetHeap( hmem );
1327    
1328    if(ISPOINTER(hmem)) /* POINTER */
1329    {
1330       if(!HeapFree(heap, 0, (LPVOID) hmem)) hmem = 0;
1331    }
1332    else  /* HANDLE */
1333    {
1334       /* HeapLock(heap); */
1335       pintern=HANDLE_TO_INTERN(hmem);
1336       
1337       if(pintern->Magic==MAGIC_GLOBAL_USED)
1338       {  
1339
1340 /* WIN98 does not make this test. That is you can free a */
1341 /* block you have not unlocked. Go figure!!              */
1342       /* if(pintern->LockCount!=0)  */
1343       /*    SetLastError(ERROR_INVALID_HANDLE);  */
1344
1345          if(pintern->Pointer)
1346             if(!HeapFree(heap, 0,
1347                          (char *)(pintern->Pointer)-sizeof(HGLOBAL)))
1348                hreturned=hmem;
1349          if(!HeapFree(heap, 0, pintern))
1350             hreturned=hmem;
1351       }      
1352       /* HeapUnlock(heap); */
1353    }
1354    return hreturned;
1355 }
1356
1357
1358 /***********************************************************************
1359  *           GlobalSize   (KERNEL32.329)
1360  * RETURNS
1361  *      Size in bytes of the global memory object
1362  *      0: Failure
1363  */
1364 DWORD WINAPI GlobalSize(
1365              HGLOBAL hmem /* [in] Handle of global memory object */
1366 ) {
1367    DWORD                retval;
1368    PGLOBAL32_INTERN     pintern;
1369    HANDLE heap          = GLOBAL_GetHeap( hmem );
1370
1371    if(ISPOINTER(hmem)) 
1372    {
1373       retval=HeapSize(heap, 0,  (LPVOID) hmem);
1374    }
1375    else
1376    {
1377       /* HeapLock(heap); */
1378       pintern=HANDLE_TO_INTERN(hmem);
1379       
1380       if(pintern->Magic==MAGIC_GLOBAL_USED)
1381       {
1382         if (!pintern->Pointer) /* handle case of GlobalAlloc( ??,0) */
1383             return 0;
1384          retval=HeapSize(heap, 0,
1385                          (char *)(pintern->Pointer)-sizeof(HGLOBAL))-4;
1386          if (retval == 0xffffffff-4) retval = 0;
1387       }
1388       else
1389       {
1390          WARN("invalid handle\n");
1391          retval=0;
1392       }
1393       /* HeapUnlock(heap); */
1394    }
1395    /* HeapSize returns 0xffffffff on failure */
1396    if (retval == 0xffffffff) retval = 0;
1397    return retval;
1398 }
1399
1400
1401 /***********************************************************************
1402  *           GlobalWire   (KERNEL32.333)
1403  */
1404 LPVOID WINAPI GlobalWire(HGLOBAL hmem)
1405 {
1406    return GlobalLock( hmem );
1407 }
1408
1409
1410 /***********************************************************************
1411  *           GlobalUnWire   (KERNEL32.330)
1412  */
1413 BOOL WINAPI GlobalUnWire(HGLOBAL hmem)
1414 {
1415    return GlobalUnlock( hmem);
1416 }
1417
1418
1419 /***********************************************************************
1420  *           GlobalFix   (KERNEL32.320)
1421  */
1422 VOID WINAPI GlobalFix(HGLOBAL hmem)
1423 {
1424     GlobalLock( hmem );
1425 }
1426
1427
1428 /***********************************************************************
1429  *           GlobalUnfix   (KERNEL32.331)
1430  */
1431 VOID WINAPI GlobalUnfix(HGLOBAL hmem)
1432 {
1433    GlobalUnlock( hmem);
1434 }
1435
1436
1437 /***********************************************************************
1438  *           GlobalFlags   (KERNEL32.321)
1439  * Returns information about the specified global memory object
1440  *
1441  * NOTES
1442  *      Should this return GMEM_INVALID_HANDLE on invalid handle?
1443  *
1444  * RETURNS
1445  *      Value specifying allocation flags and lock count
1446  *      GMEM_INVALID_HANDLE: Failure
1447  */
1448 UINT WINAPI GlobalFlags(
1449               HGLOBAL hmem /* [in] Handle to global memory object */
1450 ) {
1451    DWORD                retval;
1452    PGLOBAL32_INTERN     pintern;
1453    
1454    if(ISPOINTER(hmem))
1455    {
1456       retval=0;
1457    }
1458    else
1459    {
1460       /* HeapLock(GetProcessHeap()); */
1461       pintern=HANDLE_TO_INTERN(hmem);
1462       if(pintern->Magic==MAGIC_GLOBAL_USED)
1463       {               
1464          retval=pintern->LockCount + (pintern->Flags<<8);
1465          if(pintern->Pointer==0)
1466             retval|= GMEM_DISCARDED;
1467       }
1468       else
1469       {
1470          WARN("Invalid handle: %04x", hmem);
1471          retval=0;
1472       }
1473       /* HeapUnlock(GetProcessHeap()); */
1474    }
1475    return retval;
1476 }
1477
1478
1479 /***********************************************************************
1480  *           GlobalCompact   (KERNEL32.316)
1481  */
1482 DWORD WINAPI GlobalCompact( DWORD minfree )
1483 {
1484     return 0;  /* GlobalCompact does nothing in Win32 */
1485 }
1486
1487
1488 /***********************************************************************
1489  *           GlobalMemoryStatus   (KERNEL32.327)
1490  * RETURNS
1491  *      None
1492  */
1493 VOID WINAPI GlobalMemoryStatus(
1494             LPMEMORYSTATUS lpmem
1495 ) {
1496     static MEMORYSTATUS cached_memstatus;
1497     static int cache_lastchecked = 0;
1498     SYSTEM_INFO si;
1499 #ifdef linux
1500     FILE *f;
1501 #endif
1502
1503     if (time(NULL)==cache_lastchecked) {
1504         memcpy(lpmem,&cached_memstatus,sizeof(MEMORYSTATUS));
1505         return;
1506     }
1507     cache_lastchecked = time(NULL);
1508
1509 #ifdef linux
1510     f = fopen( "/proc/meminfo", "r" );
1511     if (f)
1512     {
1513         char buffer[256];
1514         int total, used, free, shared, buffers, cached;
1515
1516         lpmem->dwLength = sizeof(MEMORYSTATUS);
1517         lpmem->dwTotalPhys = lpmem->dwAvailPhys = 0;
1518         lpmem->dwTotalPageFile = lpmem->dwAvailPageFile = 0;
1519         while (fgets( buffer, sizeof(buffer), f ))
1520         {
1521             /* old style /proc/meminfo ... */
1522             if (sscanf( buffer, "Mem: %d %d %d %d %d %d", &total, &used, &free, &shared, &buffers, &cached ))
1523             {
1524                 lpmem->dwTotalPhys += total;
1525                 lpmem->dwAvailPhys += free + buffers + cached;
1526             }
1527             if (sscanf( buffer, "Swap: %d %d %d", &total, &used, &free ))
1528             {
1529                 lpmem->dwTotalPageFile += total;
1530                 lpmem->dwAvailPageFile += free;
1531             }
1532
1533             /* new style /proc/meminfo ... */
1534             if (sscanf(buffer, "MemTotal: %d", &total))
1535                 lpmem->dwTotalPhys = total*1024;
1536             if (sscanf(buffer, "MemFree: %d", &free))
1537                 lpmem->dwAvailPhys = free*1024;
1538             if (sscanf(buffer, "SwapTotal: %d", &total))
1539                 lpmem->dwTotalPageFile = total*1024;
1540             if (sscanf(buffer, "SwapFree: %d", &free))
1541                 lpmem->dwAvailPageFile = free*1024;
1542             if (sscanf(buffer, "Buffers: %d", &buffers))
1543                 lpmem->dwAvailPhys += buffers*1024;
1544             if (sscanf(buffer, "Cached: %d", &cached))
1545                 lpmem->dwAvailPhys += cached*1024;
1546         }
1547         fclose( f );
1548
1549         if (lpmem->dwTotalPhys)
1550         {
1551             DWORD TotalPhysical = lpmem->dwTotalPhys+lpmem->dwTotalPageFile;
1552             DWORD AvailPhysical = lpmem->dwAvailPhys+lpmem->dwAvailPageFile;
1553             lpmem->dwMemoryLoad = (TotalPhysical-AvailPhysical)
1554                                       / (TotalPhysical / 100);
1555         }
1556     } else
1557 #endif
1558     {
1559         /* FIXME: should do something for other systems */
1560         lpmem->dwMemoryLoad    = 0;
1561         lpmem->dwTotalPhys     = 16*1024*1024;
1562         lpmem->dwAvailPhys     = 16*1024*1024;
1563         lpmem->dwTotalPageFile = 16*1024*1024;
1564         lpmem->dwAvailPageFile = 16*1024*1024;
1565     }
1566     GetSystemInfo(&si);
1567     lpmem->dwTotalVirtual  = si.lpMaximumApplicationAddress-si.lpMinimumApplicationAddress;
1568     /* FIXME: we should track down all the already allocated VM pages and substract them, for now arbitrarily remove 64KB so that it matches NT */
1569     lpmem->dwAvailVirtual  = lpmem->dwTotalVirtual-64*1024;
1570     memcpy(&cached_memstatus,lpmem,sizeof(MEMORYSTATUS));
1571 }
1572
1573 /***********************************************************************
1574  *           A20Proc16   (KERNEL.165)
1575  */
1576 void WINAPI A20Proc16( WORD unused )
1577 {
1578     /* this is also a NOP in Windows */
1579 }
1580
1581 /***********************************************************************
1582  *           LimitEMSPages16   (KERNEL.156)
1583  */
1584 DWORD WINAPI LimitEMSPages16( DWORD unused )
1585 {
1586     return 0;
1587 }