Git conversion: Make reactos the root directory, move rosapps, rostests, wallpapers...
[reactos.git] / dll / win32 / kernel32 / client / heapmem.c
diff --git a/dll/win32/kernel32/client/heapmem.c b/dll/win32/kernel32/client/heapmem.c
new file mode 100644 (file)
index 0000000..0ef199e
--- /dev/null
@@ -0,0 +1,1847 @@
+/*
+ * PROJECT:         ReactOS Win32 Base API
+ * LICENSE:         GPL - See COPYING in the top level directory
+ * FILE:            dll/win32/kernel32/client/heapmem.c
+ * PURPOSE:         Heap Memory APIs (wrappers for RtlHeap*)
+ * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
+ */
+
+/* INCLUDES *******************************************************************/
+
+#include <k32.h>
+
+#define NDEBUG
+#include <debug.h>
+
+/* GLOBALS ********************************************************************/
+
+RTL_HANDLE_TABLE BaseHeapHandleTable;
+HANDLE BaseHeap;
+ULONG_PTR SystemRangeStart;
+
+/* PRIVATE FUNCTIONS **********************************************************/
+
+VOID
+NTAPI
+BaseDllInitializeMemoryManager(VOID)
+{
+    BaseHeap = RtlGetProcessHeap();
+    RtlInitializeHandleTable(0xFFFF,
+                             sizeof(BASE_HEAP_HANDLE_ENTRY),
+                             &BaseHeapHandleTable);
+    NtQuerySystemInformation(SystemRangeStartInformation,
+                             &SystemRangeStart,
+                             sizeof(SystemRangeStart),
+                             NULL);
+}
+
+/* PUBLIC FUNCTIONS ***********************************************************/
+
+/*
+ * @implemented
+ */
+HANDLE
+WINAPI
+HeapCreate(DWORD flOptions,
+           SIZE_T dwInitialSize,
+           SIZE_T dwMaximumSize)
+{
+    HANDLE hRet;
+    ULONG Flags;
+
+    /* Remove non-Win32 flags and tag this allocation */
+    Flags = (flOptions & (HEAP_GENERATE_EXCEPTIONS | HEAP_NO_SERIALIZE)) |
+            HEAP_CLASS_1;
+
+    /* Check if heap is growable and ensure max size is correct */
+    if (dwMaximumSize == 0)
+        Flags |= HEAP_GROWABLE;
+    else if (dwMaximumSize < BaseStaticServerData->SysInfo.PageSize &&
+            dwInitialSize > dwMaximumSize)
+    {
+        /* Max size is non-zero but less than page size which can't be correct.
+           Fix it up by bumping it to the initial size whatever it is. */
+        dwMaximumSize = dwInitialSize;
+    }
+
+    /* Call RTL Heap */
+    hRet = RtlCreateHeap(Flags,
+                         NULL,
+                         dwMaximumSize,
+                         dwInitialSize,
+                         NULL,
+                         NULL);
+
+    /* Set the last error if we failed, and return the pointer */
+    if (!hRet) SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+    return hRet;
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+HeapDestroy(HANDLE hHeap)
+{
+    /* Return TRUE if the heap was destroyed */
+   if (!RtlDestroyHeap(hHeap)) return TRUE;
+
+    /* Otherwise, we got the handle back, so fail */
+    SetLastError(ERROR_INVALID_HANDLE);
+    return FALSE;
+}
+
+/*
+ * @implemented
+ */
+HANDLE
+WINAPI
+GetProcessHeap(VOID)
+{
+    /* Call the RTL API */
+    return RtlGetProcessHeap();
+}
+
+/*
+ * @implemented
+ */
+DWORD
+WINAPI
+GetProcessHeaps(DWORD NumberOfHeaps,
+                PHANDLE ProcessHeaps)
+{
+    /* Call the RTL API */
+    return RtlGetProcessHeaps(NumberOfHeaps, ProcessHeaps);
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+HeapLock(HANDLE hHeap)
+{
+    /* Call the RTL API */
+    return RtlLockHeap(hHeap);
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+HeapUnlock(HANDLE hHeap)
+{
+    /* Call the RTL API */
+    return RtlUnlockHeap(hHeap);
+}
+
+/*
+ * @implemented
+ */
+SIZE_T
+WINAPI
+HeapCompact(HANDLE hHeap, DWORD dwFlags)
+{
+    /* Call the RTL API */
+    return RtlCompactHeap(hHeap, dwFlags);
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+HeapValidate(HANDLE hHeap,
+             DWORD dwFlags,
+             LPCVOID lpMem)
+{
+    /* Call the RTL API */
+    return RtlValidateHeap(hHeap, dwFlags, (PVOID)lpMem);
+}
+
+/*
+ * @implemented
+ */
+DWORD
+WINAPI
+HeapCreateTagsW(HANDLE hHeap,
+                DWORD dwFlags,
+                PWSTR lpTagName,
+                PWSTR lpTagSubName)
+{
+    /* Call the RTL API */
+    return RtlCreateTagHeap(hHeap,
+                            dwFlags,
+                            lpTagName,
+                            lpTagSubName);
+}
+
+/*
+ * @implemented
+ */
+DWORD
+WINAPI
+HeapExtend(HANDLE hHeap,
+           DWORD dwFlags,
+           PVOID BaseAddress,
+           DWORD dwBytes)
+{
+    NTSTATUS Status;
+
+    /* Call the RTL API. Gone in Vista, so commented out. */
+    Status = STATUS_NOT_IMPLEMENTED; //RtlExtendHeap(hHeap, dwFlags, BaseAddress, dwBytes);
+    if (!NT_SUCCESS(Status))
+    {
+        /* We failed */
+        BaseSetLastNTError(Status);
+        return FALSE;
+    }
+
+    /* Return success */
+    return TRUE;
+}
+
+/*
+ * @implemented
+ */
+PWSTR
+WINAPI
+HeapQueryTagW(HANDLE hHeap,
+              DWORD dwFlags,
+              WORD wTagIndex,
+              BOOL bResetCounters,
+              PVOID lpTagInfo)
+{
+    /* Call the RTL API */
+    return RtlQueryTagHeap(hHeap,
+                           dwFlags,
+                           wTagIndex,
+                           (BOOLEAN)bResetCounters,
+                           lpTagInfo);
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+HeapSummary(HANDLE hHeap,
+            DWORD dwFlags,
+            PVOID Summary)
+{
+    NTSTATUS Status;
+    RTL_HEAP_USAGE Usage;
+
+    /* Fill in the length information */
+    Usage.Length = sizeof(Usage);
+
+    /* Call RTL. Gone in Vista, so commented out */
+    Status = STATUS_NOT_IMPLEMENTED; //RtlUsageHeap(hHeap, dwFlags, &Usage);
+    if (!NT_SUCCESS(Status))
+    {
+        /* We failed */
+        BaseSetLastNTError(Status);
+        return FALSE;
+    }
+
+    /* FIXME: Summary == Usage?! */
+    RtlCopyMemory(Summary, &Usage, sizeof(Usage));
+    return TRUE;
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+HeapUsage(HANDLE hHeap,
+          DWORD dwFlags,
+          DWORD Unknown,
+          DWORD Unknown2,
+          IN PVOID Usage)
+{
+    NTSTATUS Status;
+
+    /* Call RTL. Gone in Vista, so commented out */
+    Status = STATUS_NOT_IMPLEMENTED; //RtlUsageHeap(hHeap, dwFlags, &Usage);
+    if (!NT_SUCCESS(Status))
+    {
+        /* We failed */
+        BaseSetLastNTError(Status);
+        return FALSE;
+    }
+    else if (Status == STATUS_MORE_ENTRIES)
+    {
+        /* There are still more entries to parse */
+        return TRUE;
+    }
+
+    /* Otherwise, we're completely done, so we return FALSE, but NO_ERROR */
+    SetLastError(NO_ERROR);
+    return FALSE;
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+HeapWalk(HANDLE        hHeap,
+         LPPROCESS_HEAP_ENTRY lpEntry)
+{
+    NTSTATUS Status;
+
+    DPRINT1("Warning, HeapWalk is calling RtlWalkHeap with Win32 parameters\n");
+
+    Status = RtlWalkHeap(hHeap, lpEntry);
+
+    if (!NT_SUCCESS(Status))
+    {
+        SetLastError(RtlNtStatusToDosError(Status));
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+HeapQueryInformation(HANDLE HeapHandle,
+                     HEAP_INFORMATION_CLASS HeapInformationClass,
+                     PVOID HeapInformation OPTIONAL,
+                     SIZE_T HeapInformationLength OPTIONAL,
+                     PSIZE_T ReturnLength OPTIONAL)
+{
+    NTSTATUS Status;
+
+    Status = RtlQueryHeapInformation(HeapHandle,
+                                     HeapInformationClass,
+                                     HeapInformation,
+                                     HeapInformationLength,
+                                     ReturnLength);
+
+    if (!NT_SUCCESS(Status))
+    {
+        BaseSetLastNTError(Status);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+HeapSetInformation(HANDLE HeapHandle,
+                   HEAP_INFORMATION_CLASS HeapInformationClass,
+                   PVOID HeapInformation OPTIONAL,
+                   SIZE_T HeapInformationLength OPTIONAL)
+{
+    NTSTATUS Status;
+
+    Status = RtlSetHeapInformation(HeapHandle,
+                                   HeapInformationClass,
+                                   HeapInformation,
+                                   HeapInformationLength);
+
+    if (!NT_SUCCESS(Status))
+    {
+        BaseSetLastNTError(Status);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+/*
+ * @implemented
+ */
+HGLOBAL
+NTAPI
+GlobalAlloc(UINT uFlags,
+            SIZE_T dwBytes)
+{
+    ULONG Flags = 0;
+    PVOID Ptr = NULL;
+    HANDLE hMemory;
+    PBASE_HEAP_HANDLE_ENTRY HandleEntry;
+    BASE_TRACE_ALLOC(dwBytes, uFlags);
+    ASSERT(BaseHeap);
+
+    /* Make sure the flags are valid */
+    if (uFlags & ~GMEM_VALID_FLAGS)
+    {
+        /* They aren't, fail */
+        BASE_TRACE_FAILURE();
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return NULL;
+    }
+
+    /* Convert ZEROINIT */
+    if (uFlags & GMEM_ZEROINIT) Flags |= HEAP_ZERO_MEMORY;
+
+    /* Check if we're not movable, which means pointer-based heap */
+    if (!(uFlags & GMEM_MOVEABLE))
+    {
+        /* Check if this is DDESHARE (deprecated) */
+        if (uFlags & GMEM_DDESHARE) Flags |= BASE_HEAP_ENTRY_FLAG_DDESHARE;
+
+        /* Allocate heap for it */
+        Ptr = RtlAllocateHeap(BaseHeap, Flags, dwBytes ? dwBytes : 1);
+        if (!Ptr) SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+        BASE_TRACE_ALLOC2(Ptr);
+        return Ptr;
+    }
+
+    /* This is heap based, so lock it in first */
+    RtlLockHeap(BaseHeap);
+
+    /*
+     * Disable locking, enable custom flags, and write the
+     * movable flag (deprecated)
+     */
+    Flags |= HEAP_NO_SERIALIZE |
+             HEAP_SETTABLE_USER_VALUE |
+             BASE_HEAP_FLAG_MOVABLE;
+
+    /* Allocate the handle */
+    HandleEntry = BaseHeapAllocEntry();
+    if (!HandleEntry)
+    {
+        /* Fail */
+        hMemory = NULL;
+        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+        BASE_TRACE_FAILURE();
+    }
+    else
+    {
+        /* Get the object and make sure we have size */
+        hMemory = &HandleEntry->Object;
+        if (dwBytes)
+        {
+            /* Allocate the actual memory for it */
+            Ptr = RtlAllocateHeap(BaseHeap, Flags, dwBytes);
+            BASE_TRACE_PTR(HandleEntry, Ptr);
+            if (!Ptr)
+            {
+                /* We failed, manually set the allocate flag and free the handle */
+                HandleEntry->Flags = RTL_HANDLE_VALID;
+                BaseHeapFreeEntry(HandleEntry);
+
+                /* For the cleanup case */
+                HandleEntry = NULL;
+            }
+            else
+            {
+                /* All worked well, save our heap entry */
+                RtlSetUserValueHeap(BaseHeap, HEAP_NO_SERIALIZE, Ptr, hMemory);
+            }
+        }
+    }
+
+    /* Cleanup! First unlock the heap */
+    RtlUnlockHeap(BaseHeap);
+
+    /* Check if a handle was allocated */
+    if (HandleEntry)
+    {
+        /* Set the pointer and allocated flag */
+        HandleEntry->Object = Ptr;
+        HandleEntry->Flags = RTL_HANDLE_VALID;
+        if (!Ptr)
+        {
+            /* We don't have a valid pointer, but so reuse this handle */
+            HandleEntry->Flags |= BASE_HEAP_ENTRY_FLAG_REUSE;
+        }
+
+        /* Check if the handle is discardable */
+        if (uFlags & GMEM_DISCARDABLE)
+        {
+            /* Save it in the handle entry */
+            HandleEntry->Flags |= BASE_HEAP_ENTRY_FLAG_REUSABLE;
+        }
+
+        /* Check if the handle is moveable */
+        if (uFlags & GMEM_MOVEABLE)
+        {
+            /* Save it in the handle entry */
+            HandleEntry->Flags |= BASE_HEAP_ENTRY_FLAG_MOVABLE;
+        }
+
+        /* Check if the handle is DDE Shared */
+        if (uFlags & GMEM_DDESHARE)
+        {
+            /* Save it in the handle entry */
+            HandleEntry->Flags |= BASE_HEAP_ENTRY_FLAG_DDESHARE;
+        }
+
+        /* Set the pointer */
+        Ptr = hMemory;
+    }
+
+    /* Return the pointer */
+    return Ptr;
+}
+
+/*
+ * @implemented
+ */
+SIZE_T
+NTAPI
+GlobalCompact(DWORD dwMinFree)
+{
+    /* Call the RTL Heap Manager */
+    return RtlCompactHeap(BaseHeap, 0);
+}
+
+/*
+ * @implemented
+ */
+VOID
+NTAPI
+GlobalFix(HGLOBAL hMem)
+{
+    /* Lock the memory if it the handle is valid */
+    if (INVALID_HANDLE_VALUE != hMem) GlobalLock(hMem);
+}
+
+/*
+ * @implemented
+ */
+UINT
+NTAPI
+GlobalFlags(HGLOBAL hMem)
+{
+    PBASE_HEAP_HANDLE_ENTRY HandleEntry;
+    HANDLE Handle = NULL;
+    ULONG Flags = 0;
+    UINT uFlags = GMEM_INVALID_HANDLE;
+
+    /* Start by locking the heap */
+    RtlLockHeap(BaseHeap);
+    _SEH2_TRY
+    {
+        /* Check if this is a simple RTL Heap Managed block */
+        if (!((ULONG_PTR)hMem & BASE_HEAP_IS_HANDLE_ENTRY))
+        {
+            /* Then we'll query RTL Heap */
+            RtlGetUserInfoHeap(BaseHeap, Flags, hMem, &Handle, &Flags);
+            BASE_TRACE_PTR(Handle, hMem);
+
+            /*
+             * Check if RTL Heap didn't find a handle associated with us or
+             * said that this heap isn't movable, which means something we're
+             * really not a handle-based heap.
+             */
+            if (!(Handle) || !(Flags & BASE_HEAP_FLAG_MOVABLE))
+            {
+                /* Then set the flags to 0 */
+                uFlags = 0;
+            }
+            else
+            {
+                /* Otherwise we're handle-based, so get the internal handle */
+                hMem = Handle;
+            }
+        }
+
+        /* Check if the handle is actually an entry in our table */
+        if ((ULONG_PTR)hMem & BASE_HEAP_IS_HANDLE_ENTRY)
+        {
+            /* Then get the entry */
+            HandleEntry = BaseHeapGetEntry(hMem);
+            BASE_TRACE_HANDLE(HandleEntry, hMem);
+
+            /* Make sure it's a valid handle */
+            if (BaseHeapValidateEntry(HandleEntry))
+            {
+                /* Get the lock count first */
+                uFlags = HandleEntry->LockCount & GMEM_LOCKCOUNT;
+
+                /* Now check if it's discardable */
+                if (HandleEntry->Flags & BASE_HEAP_ENTRY_FLAG_REUSABLE)
+                {
+                    /* Set the Win32 Flag */
+                    uFlags |= GMEM_DISCARDABLE;
+                }
+
+                /* Check if it's DDE Shared */
+                if (HandleEntry->Flags & BASE_HEAP_ENTRY_FLAG_DDESHARE)
+                {
+                    /* Set the Win32 Flag */
+                    uFlags |= GMEM_DDESHARE;
+                }
+
+                /* Now check if it's discarded */
+                if (HandleEntry->Flags & BASE_HEAP_ENTRY_FLAG_REUSE)
+                {
+                   /* Set the Win32 Flag */
+                   uFlags |= GMEM_DISCARDED;
+               }
+            }
+        }
+
+        /* Check if by now, we still haven't gotten any useful flags */
+        if (uFlags == GMEM_INVALID_HANDLE) SetLastError(ERROR_INVALID_HANDLE);
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        /* Set the exception code */
+        BaseSetLastNTError(_SEH2_GetExceptionCode());
+    }
+    _SEH2_END;
+
+    /* All done! Unlock heap and return Win32 Flags */
+    RtlUnlockHeap(BaseHeap);
+    return uFlags;
+}
+
+/*
+ * @implemented
+ */
+HGLOBAL
+NTAPI
+GlobalFree(HGLOBAL hMem)
+{
+    PBASE_HEAP_HANDLE_ENTRY HandleEntry;
+    LPVOID Ptr;
+    BASE_TRACE_DEALLOC(hMem);
+
+    /* Check if this was a simple allocated heap entry */
+    if (!((ULONG_PTR)hMem & BASE_HEAP_IS_HANDLE_ENTRY))
+    {
+        /* Free it with the RTL Heap Manager */
+        if (RtlFreeHeap(BaseHeap, 0, hMem))
+        {
+            /* Return NULL since there's no handle */
+            return NULL;
+        }
+        else
+        {
+            /* Otherwise fail */
+            BASE_TRACE_FAILURE();
+            SetLastError(ERROR_INVALID_HANDLE);
+            return hMem;
+        }
+    }
+
+    /* It's a handle probably, so lock the heap */
+    RtlLockHeap(BaseHeap);
+    _SEH2_TRY
+    {
+        /* Make sure that this is an entry in our handle database */
+        if ((ULONG_PTR)hMem & BASE_HEAP_IS_HANDLE_ENTRY)
+        {
+            /* Get the entry */
+            HandleEntry = BaseHeapGetEntry(hMem);
+            BASE_TRACE_HANDLE(HandleEntry, hMem);
+
+            /* Make sure the handle is valid */
+            if (!BaseHeapValidateEntry(HandleEntry))
+            {
+                /* It's not, fail */
+                SetLastError(ERROR_INVALID_HANDLE);
+                Ptr = NULL;
+            }
+            else
+            {
+                /* It's valid, so get the pointer */
+                Ptr = HandleEntry->Object;
+
+                /* Free this handle */
+                BaseHeapFreeEntry(HandleEntry);
+
+                /* If the pointer is 0, then we don't have a handle either */
+                if (!Ptr) hMem = NULL;
+            }
+        }
+        else
+        {
+            /* Otherwise, reuse the handle as a pointer */
+            BASE_TRACE_FAILURE();
+            Ptr = hMem;
+        }
+
+        /* Check if we got here with a valid heap pointer */
+        if (Ptr)
+        {
+            /* Free it with the RTL Heap Manager */
+            if (RtlFreeHeap(BaseHeap, HEAP_NO_SERIALIZE, Ptr))
+            {
+                /* Everything worked */
+                hMem = NULL;
+            }
+            else
+            {
+                /* This wasn't a real heap handle */
+                SetLastError(ERROR_INVALID_HANDLE);
+            }
+        }
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        /* Set the exception code */
+        BaseSetLastNTError(_SEH2_GetExceptionCode());
+    }
+    _SEH2_END;
+
+    /* We're done, so unlock the heap and return the handle */
+    RtlUnlockHeap(BaseHeap);
+    return hMem;
+}
+
+/*
+ * @implemented
+ */
+HGLOBAL
+NTAPI
+GlobalHandle(LPCVOID pMem)
+{
+    HANDLE Handle = NULL;
+    ULONG Flags;
+
+    /* Lock the heap */
+    RtlLockHeap(BaseHeap);
+    _SEH2_TRY
+    {
+        /* Query RTL Heap */
+        if (!RtlGetUserInfoHeap(BaseHeap,
+                                HEAP_NO_SERIALIZE,
+                                (PVOID)pMem,
+                                &Handle,
+                                &Flags))
+        {
+            /* RTL Heap Manager does not know about this heap */
+            SetLastError(ERROR_INVALID_HANDLE);
+        }
+        else
+        {
+            /*
+             * Check if RTL Heap didn't find a handle for us or said that
+             * this heap isn't movable.
+             */
+            BASE_TRACE_PTR(Handle, pMem);
+            if (!(Handle) || !(Flags & BASE_HEAP_FLAG_MOVABLE))
+            {
+                /* We're actually handle-based, so the pointer is a handle */
+                Handle = (HANDLE)pMem;
+            }
+        }
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        /* Set the exception code */
+        BaseSetLastNTError(_SEH2_GetExceptionCode());
+    }
+    _SEH2_END;
+
+    /* All done, unlock the heap and return the handle */
+    RtlUnlockHeap(BaseHeap);
+    return Handle;
+}
+
+/*
+ * @implemented
+ */
+LPVOID
+NTAPI
+GlobalLock(HGLOBAL hMem)
+{
+    PBASE_HEAP_HANDLE_ENTRY HandleEntry;
+    LPVOID Ptr;
+
+    /* Check if this was a simple allocated heap entry */
+    if (!((ULONG_PTR)hMem & BASE_HEAP_IS_HANDLE_ENTRY))
+    {
+        /* Make sure it's not a kernel or invalid address */
+        if ((hMem >= (HGLOBAL)SystemRangeStart) || (IsBadReadPtr(hMem, 1)))
+        {
+            /* Signal an error */
+            SetLastError(ERROR_INVALID_HANDLE);
+            return NULL;
+        }
+
+        /* It's all good */
+        return hMem;
+    }
+
+    /* Otherwise, lock the heap */
+    RtlLockHeap(BaseHeap);
+    _SEH2_TRY
+    {
+        /* Get the handle entry */
+        HandleEntry = BaseHeapGetEntry(hMem);
+        BASE_TRACE_HANDLE(HandleEntry, hMem);
+
+        /* Make sure it's valid */
+        if (!BaseHeapValidateEntry(HandleEntry))
+        {
+            /* It's not, fail */
+            BASE_TRACE_FAILURE();
+            SetLastError(ERROR_INVALID_HANDLE);
+            Ptr = NULL;
+        }
+        else
+        {
+            /* Otherwise, get the pointer */
+            Ptr = HandleEntry->Object;
+            if (Ptr)
+            {
+                /* Increase the lock count, unless we've went too far */
+                if (HandleEntry->LockCount++ == GMEM_LOCKCOUNT)
+                {
+                    /* In which case we simply unlock once */
+                    HandleEntry->LockCount--;
+                }
+            }
+            else
+            {
+                /* The handle is still there but the memory was already freed */
+                SetLastError(ERROR_DISCARDED);
+            }
+        }
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
+        Ptr = NULL;
+    }
+    _SEH2_END;
+
+    /* All done. Unlock the heap and return the pointer */
+    RtlUnlockHeap(BaseHeap);
+    return Ptr;
+}
+
+HGLOBAL
+NTAPI
+GlobalReAlloc(HGLOBAL hMem,
+              SIZE_T dwBytes,
+              UINT uFlags)
+{
+    PBASE_HEAP_HANDLE_ENTRY HandleEntry;
+    HANDLE Handle;
+    LPVOID Ptr;
+    ULONG Flags = 0;
+
+    /* Throw out invalid flags */
+    if (uFlags & ~(GMEM_VALID_FLAGS | GMEM_MODIFY))
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return NULL;
+    }
+
+    /* Throw out invalid combo */
+    if ((uFlags & GMEM_DISCARDABLE) && !(uFlags & GMEM_MODIFY))
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return NULL;
+    }
+
+    /* Convert ZEROINIT */
+    if (uFlags & GMEM_ZEROINIT) Flags |= HEAP_ZERO_MEMORY;
+
+    /* If this wasn't a movable heap, then we MUST re-alloc in place */
+    if (!(uFlags & GMEM_MOVEABLE)) Flags |= HEAP_REALLOC_IN_PLACE_ONLY;
+
+    /* Lock the heap and disable built-in locking in the RTL Heap functions */
+    RtlLockHeap(BaseHeap);
+    Flags |= HEAP_NO_SERIALIZE;
+
+    /* Check if this is a simple handle-based block */
+    if (((ULONG_PTR)hMem & BASE_HEAP_IS_HANDLE_ENTRY))
+    {
+        /* Get the entry */
+        HandleEntry = BaseHeapGetEntry(hMem);
+        BASE_TRACE_HANDLE(HandleEntry, hMem);
+
+        /* Make sure the handle is valid */
+        if (!BaseHeapValidateEntry(HandleEntry))
+        {
+            /* Fail */
+            BASE_TRACE_FAILURE();
+            SetLastError(ERROR_INVALID_HANDLE);
+            hMem = NULL;
+        }
+        else if (uFlags & GMEM_MODIFY)
+        {
+            /* User is changing flags... check if the memory was discardable */
+            if (uFlags & GMEM_DISCARDABLE)
+            {
+                /* Then set the flag */
+                HandleEntry->Flags |= BASE_HEAP_ENTRY_FLAG_REUSABLE;
+            }
+            else
+            {
+                /* Otherwise, remove the flag */
+                HandleEntry->Flags &= ~BASE_HEAP_ENTRY_FLAG_REUSABLE;
+            }
+        }
+        else
+        {
+            /* Otherwise, get the object and check if we have no size */
+            Ptr = HandleEntry->Object;
+            if (!dwBytes)
+            {
+                /* Clear the handle and check for a pointer */
+                hMem = NULL;
+                if (Ptr)
+                {
+                    /* Make sure the handle isn't locked */
+                    if ((uFlags & GMEM_MOVEABLE) && !(HandleEntry->LockCount))
+                    {
+                        /* Free the current heap */
+                        if (RtlFreeHeap(BaseHeap, Flags, Ptr))
+                        {
+                            /* Free the handle */
+                            HandleEntry->Object = NULL;
+                            HandleEntry->Flags |= BASE_HEAP_ENTRY_FLAG_REUSE;
+
+                            /* Get the object pointer */
+                            hMem = &HandleEntry->Object;
+                        }
+                    }
+                }
+                else
+                {
+                    /* Otherwise just return the object pointer */
+                    hMem = &HandleEntry->Object;
+                }
+            }
+            else
+            {
+                /* Otherwise, we're allocating, so set the new flags needed */
+                Flags |= HEAP_SETTABLE_USER_VALUE | BASE_HEAP_FLAG_MOVABLE;
+                if (!Ptr)
+                {
+                    /* We don't have a base, so allocate one */
+                    Ptr = RtlAllocateHeap(BaseHeap, Flags, dwBytes);
+                    BASE_TRACE_ALLOC2(Ptr);
+                    if (Ptr)
+                    {
+                        /* Allocation succeeded, so save our entry */
+                        RtlSetUserValueHeap(BaseHeap,
+                                            HEAP_NO_SERIALIZE,
+                                            Ptr,
+                                            hMem);
+                    }
+                }
+                else
+                {
+                    /*
+                     * If it's not movable or currently locked, we MUST allocate
+                     * in-place!
+                     */
+                    if (!(uFlags & GMEM_MOVEABLE) && (HandleEntry->LockCount))
+                    {
+                        /* Set the flag */
+                        Flags |= HEAP_REALLOC_IN_PLACE_ONLY;
+                    }
+                    else
+                    {
+                        /* Otherwise clear the flag if we set it previously */
+                        Flags &= ~HEAP_REALLOC_IN_PLACE_ONLY;
+                    }
+
+                    /* Do the re-allocation. No need to save the entry again */
+                    Ptr = RtlReAllocateHeap(BaseHeap, Flags, Ptr, dwBytes);
+                }
+
+                /* Make sure we have a pointer by now */
+                if (Ptr)
+                {
+                    /* Write it in the handle entry and mark it in use */
+                    HandleEntry->Object = Ptr;
+                    HandleEntry->Flags &= ~BASE_HEAP_ENTRY_FLAG_REUSE;
+                }
+                else
+                {
+                    /* Otherwise we failed */
+                    hMem = NULL;
+                    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+                }
+            }
+        }
+    }
+    else if (uFlags & GMEM_MODIFY)
+    {
+        /* This is not a handle-based heap and the caller wants it to be one */
+        if (uFlags & GMEM_MOVEABLE)
+        {
+            /* Get information on its current state */
+            Handle = hMem;
+            if (RtlGetUserInfoHeap(BaseHeap,
+                                   HEAP_NO_SERIALIZE,
+                                   hMem,
+                                   &Handle,
+                                   NULL))
+            {
+                /*
+                 * Check if the handle matches the pointer or the moveable flag
+                 * isn't there, which is what we expect since it currently isn't.
+                 */
+                if ((Handle == hMem) || !(Flags & BASE_HEAP_FLAG_MOVABLE))
+                {
+                    /* Allocate a handle for it */
+                    HandleEntry = BaseHeapAllocEntry();
+                    if (!HandleEntry)
+                    {
+                        /* No entry could be allocated */
+                        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+                        RtlUnlockHeap(BaseHeap);
+                        return NULL;
+                    }
+
+                    /* Calculate the size of the current heap */
+                    dwBytes = RtlSizeHeap(BaseHeap, HEAP_NO_SERIALIZE, hMem);
+
+                    /* Set the movable flag */
+                    Flags |= HEAP_SETTABLE_USER_VALUE | BASE_HEAP_FLAG_MOVABLE;
+
+                    /* Now allocate the actual heap for it */
+                    HandleEntry->Object = RtlAllocateHeap(BaseHeap,
+                                                          Flags,
+                                                          dwBytes);
+                    BASE_TRACE_PTR(HandleEntry->Object, HandleEntry);
+                    if (!HandleEntry->Object)
+                    {
+                        /*
+                         * We failed, manually set the allocate flag and
+                         * free the handle
+                         */
+                        HandleEntry->Flags = RTL_HANDLE_VALID;
+                        BaseHeapFreeEntry(HandleEntry);
+
+                        /* For the cleanup case */
+                        BASE_TRACE_FAILURE();
+                        HandleEntry = NULL;
+                        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+                    }
+                    else
+                    {
+                        /* Otherwise, copy the new heap and free the old one */
+                        RtlMoveMemory(HandleEntry->Object, hMem, dwBytes);
+                        RtlFreeHeap(BaseHeap, HEAP_NO_SERIALIZE, hMem);
+
+                        /* Select the heap pointer */
+                        hMem = (HANDLE)&HandleEntry->Object;
+
+                        /* Initialize the count and default flags */
+                        HandleEntry->LockCount = 0;
+                        HandleEntry->Flags = RTL_HANDLE_VALID |
+                                             BASE_HEAP_ENTRY_FLAG_MOVABLE;
+
+                        /* Check if it's also discardable */
+                        if (uFlags & GMEM_DISCARDABLE)
+                        {
+                            /* Set the internal flag */
+                            HandleEntry->Flags |= BASE_HEAP_ENTRY_FLAG_REUSABLE;
+                        }
+
+                        /* Check if it's also DDE Shared */
+                        if (uFlags & GMEM_DDESHARE)
+                        {
+                            /* Set the internal flag */
+                            HandleEntry->Flags |= BASE_HEAP_ENTRY_FLAG_DDESHARE;
+                        }
+
+                        /* Allocation succeeded, so save our entry */
+                        RtlSetUserValueHeap(BaseHeap,
+                                            HEAP_NO_SERIALIZE,
+                                            HandleEntry->Object,
+                                            hMem);
+                    }
+                }
+            }
+        }
+    }
+    else
+    {
+        /* Otherwise, this is a simple RTL Managed Heap, so just call it */
+        hMem = RtlReAllocateHeap(BaseHeap,
+                                 Flags | HEAP_NO_SERIALIZE,
+                                 hMem,
+                                 dwBytes);
+        if (!hMem)
+        {
+            /* Fail */
+            BASE_TRACE_FAILURE();
+            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+        }
+    }
+
+    /* All done, unlock the heap and return the pointer */
+    RtlUnlockHeap(BaseHeap);
+    return hMem;
+}
+
+/*
+ * @implemented
+ */
+SIZE_T
+NTAPI
+GlobalSize(HGLOBAL hMem)
+{
+    PBASE_HEAP_HANDLE_ENTRY HandleEntry;
+    PVOID Handle = NULL;
+    ULONG Flags = 0;
+    SIZE_T dwSize = MAXULONG_PTR;
+
+    /* Lock the heap */
+    RtlLockHeap(BaseHeap);
+    _SEH2_TRY
+    {
+        /* Check if this is a simple RTL Heap Managed block */
+        if (!((ULONG_PTR)hMem & BASE_HEAP_IS_HANDLE_ENTRY))
+        {
+            /* Then we'll query RTL Heap */
+            if (RtlGetUserInfoHeap(BaseHeap, Flags, hMem, &Handle, &Flags))
+            {
+                BASE_TRACE_PTR(Handle, hMem);
+                /*
+                 * Check if RTL Heap didn't give us a handle or said that this
+                 * heap isn't movable.
+                 */
+                if (!(Handle) || !(Flags & BASE_HEAP_FLAG_MOVABLE))
+                {
+                    /* We're not a handle heap, so use the generic call */
+                    dwSize = RtlSizeHeap(BaseHeap, HEAP_NO_SERIALIZE, hMem);
+                }
+                else
+                {
+                    /* We're a handle heap so get the internal handle */
+                    hMem = Handle;
+                }
+            }
+        }
+
+        /* Make sure that this is an entry in our handle database */
+        if ((ULONG_PTR)hMem & BASE_HEAP_IS_HANDLE_ENTRY)
+        {
+            /* Get the entry */
+            HandleEntry = BaseHeapGetEntry(hMem);
+            BASE_TRACE_HANDLE(HandleEntry, hMem);
+
+            /* Make sure the handle is valid */
+            if (!BaseHeapValidateEntry(HandleEntry))
+            {
+                /* Fail */
+                BASE_TRACE_FAILURE();
+                SetLastError(ERROR_INVALID_HANDLE);
+            }
+            else if (HandleEntry->Flags & BASE_HEAP_ENTRY_FLAG_REUSE)
+            {
+                /* We've reused this block, but we've saved the size for you */
+                dwSize = HandleEntry->OldSize;
+            }
+            else
+            {
+                /* Otherwise, query RTL about it */
+                dwSize = RtlSizeHeap(BaseHeap,
+                                     HEAP_NO_SERIALIZE,
+                                     HandleEntry->Object);
+            }
+        }
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        /* Set failure for later */
+        dwSize = MAXULONG_PTR;
+    }
+    _SEH2_END;
+
+    /* Check if by now, we still haven't gotten any useful size */
+    if (dwSize == MAXULONG_PTR)
+    {
+        /* Fail */
+        BASE_TRACE_FAILURE();
+        SetLastError(ERROR_INVALID_HANDLE);
+        dwSize = 0;
+    }
+
+    /* All done! Unlock heap and return the size */
+    RtlUnlockHeap(BaseHeap);
+    return dwSize;
+}
+
+/*
+ * @implemented
+ */
+VOID
+NTAPI
+GlobalUnfix(HGLOBAL hMem)
+{
+    /* If the handle is valid, unlock it */
+    if (hMem != INVALID_HANDLE_VALUE) GlobalUnlock(hMem);
+}
+
+/*
+ * @implemented
+ */
+BOOL
+NTAPI
+GlobalUnlock(HGLOBAL hMem)
+{
+    PBASE_HEAP_HANDLE_ENTRY HandleEntry;
+    BOOL RetVal = TRUE;
+
+    /* Check if this was a simple allocated heap entry */
+    if (!((ULONG_PTR)hMem & BASE_HEAP_IS_HANDLE_ENTRY)) return RetVal;
+
+    /* Otherwise, lock the heap */
+    RtlLockHeap(BaseHeap);
+
+    /* Get the handle entry */
+    HandleEntry = BaseHeapGetEntry(hMem);
+    BASE_TRACE_HANDLE(HandleEntry, hMem);
+
+    _SEH2_TRY
+    {
+        /* Make sure it's valid */
+        if (!BaseHeapValidateEntry(HandleEntry))
+        {
+            /* It's not, fail */
+            BASE_TRACE_FAILURE();
+            SetLastError(ERROR_INVALID_HANDLE);
+            RetVal = FALSE;
+        }
+        else
+        {
+            /* Otherwise, decrement lock count, unless we're already at 0*/
+            if (!HandleEntry->LockCount--)
+            {
+                /* In which case we simply lock it back and fail */
+                HandleEntry->LockCount++;
+                SetLastError(ERROR_NOT_LOCKED);
+                RetVal = FALSE;
+            }
+            else if (!HandleEntry->LockCount)
+            {
+                /* Nothing to unlock */
+                SetLastError(NO_ERROR);
+                RetVal = FALSE;
+            }
+        }
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        RetVal = FALSE;
+    }
+    _SEH2_END;
+
+    /* All done. Unlock the heap and return the pointer */
+    RtlUnlockHeap(BaseHeap);
+    return RetVal;
+}
+
+/*
+ * @implemented
+ */
+BOOL
+NTAPI
+GlobalUnWire(HGLOBAL hMem)
+{
+    /* This is simply an unlock */
+    return GlobalUnlock(hMem);
+}
+
+/*
+ * @implemented
+ */
+LPVOID
+NTAPI
+GlobalWire(HGLOBAL hMem)
+{
+    /* This is just a lock */
+    return GlobalLock(hMem);
+}
+
+/*
+ * @implemented
+ */
+BOOL
+NTAPI
+GlobalMemoryStatusEx(LPMEMORYSTATUSEX lpBuffer)
+{
+    SYSTEM_PERFORMANCE_INFORMATION PerformanceInfo;
+    VM_COUNTERS VmCounters;
+    QUOTA_LIMITS QuotaLimits;
+    ULONGLONG PageFile, PhysicalMemory;
+
+    if (lpBuffer->dwLength != sizeof(*lpBuffer))
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    /* Query performance information */
+    NtQuerySystemInformation(SystemPerformanceInformation,
+                             &PerformanceInfo,
+                             sizeof(PerformanceInfo),
+                             NULL);
+
+    /* Calculate memory load */
+    lpBuffer->dwMemoryLoad = ((DWORD)(BaseStaticServerData->SysInfo.NumberOfPhysicalPages -
+                                      PerformanceInfo.AvailablePages) * 100) /
+                                      BaseStaticServerData->SysInfo.NumberOfPhysicalPages;
+
+    /* Save physical memory */
+    PhysicalMemory = BaseStaticServerData->SysInfo.NumberOfPhysicalPages *
+                     BaseStaticServerData->SysInfo.PageSize;
+    lpBuffer->ullTotalPhys = PhysicalMemory;
+
+    /* Now save available physical memory */
+    PhysicalMemory = PerformanceInfo.AvailablePages *
+                     BaseStaticServerData->SysInfo.PageSize;
+    lpBuffer->ullAvailPhys = PhysicalMemory;
+
+    /* Query VM and Quota Limits */
+    NtQueryInformationProcess(NtCurrentProcess(),
+                              ProcessQuotaLimits,
+                              &QuotaLimits,
+                              sizeof(QUOTA_LIMITS),
+                              NULL);
+    NtQueryInformationProcess(NtCurrentProcess(),
+                              ProcessVmCounters,
+                              &VmCounters,
+                              sizeof(VM_COUNTERS),
+                              NULL);
+
+    /* Save the commit limit */
+    lpBuffer->ullTotalPageFile = min(QuotaLimits.PagefileLimit,
+                                     PerformanceInfo.CommitLimit);
+    lpBuffer->ullTotalPageFile *= BaseStaticServerData->SysInfo.PageSize;
+
+    /* Calculate how many pages are left */
+    PageFile = PerformanceInfo.CommitLimit - PerformanceInfo.CommittedPages;
+
+    /* Save the total */
+    lpBuffer->ullAvailPageFile = min(PageFile,
+                                     QuotaLimits.PagefileLimit -
+                                     VmCounters.PagefileUsage);
+    lpBuffer->ullAvailPageFile *= BaseStaticServerData->SysInfo.PageSize;
+
+    /* Now calculate the total virtual space */
+    lpBuffer->ullTotalVirtual = (BaseStaticServerData->SysInfo.MaximumUserModeAddress -
+                                 BaseStaticServerData->SysInfo.MinimumUserModeAddress) + 1;
+
+    /* And finally the available virtual space */
+    lpBuffer->ullAvailVirtual = lpBuffer->ullTotalVirtual - VmCounters.VirtualSize;
+    lpBuffer->ullAvailExtendedVirtual = 0;
+
+    return TRUE;
+}
+
+/*
+ * @implemented
+ */
+VOID
+NTAPI
+GlobalMemoryStatus(LPMEMORYSTATUS lpBuffer)
+{
+    MEMORYSTATUSEX lpBufferEx;
+
+    /* Call the extended function */
+    lpBufferEx.dwLength = sizeof(MEMORYSTATUSEX);
+    if (GlobalMemoryStatusEx(&lpBufferEx))
+    {
+        /* Reset the right size and fill out the information */
+        lpBuffer->dwLength = sizeof(MEMORYSTATUS);
+        lpBuffer->dwMemoryLoad = lpBufferEx.dwMemoryLoad;
+        lpBuffer->dwTotalPhys = (SIZE_T)min(lpBufferEx.ullTotalPhys, MAXULONG_PTR);
+        lpBuffer->dwAvailPhys = (SIZE_T)min(lpBufferEx.ullAvailPhys, MAXULONG_PTR);
+        lpBuffer->dwTotalPageFile = (SIZE_T)min(lpBufferEx.ullTotalPageFile, MAXULONG_PTR);
+        lpBuffer->dwAvailPageFile = (SIZE_T)min(lpBufferEx.ullAvailPageFile, MAXULONG_PTR);
+        lpBuffer->dwTotalVirtual = (SIZE_T)min(lpBufferEx.ullTotalVirtual, MAXULONG_PTR);
+        lpBuffer->dwAvailVirtual = (SIZE_T)min(lpBufferEx.ullAvailVirtual, MAXULONG_PTR);
+    }
+}
+
+/*
+ * @implemented
+ */
+HLOCAL
+NTAPI
+LocalAlloc(UINT uFlags,
+           SIZE_T dwBytes)
+{
+    ULONG Flags = 0;
+    PVOID Ptr = NULL;
+    HANDLE hMemory;
+    PBASE_HEAP_HANDLE_ENTRY HandleEntry;
+    BASE_TRACE_ALLOC(dwBytes, uFlags);
+    ASSERT(BaseHeap);
+
+    /* Make sure the flags are valid */
+    if (uFlags & ~LMEM_VALID_FLAGS)
+    {
+        /* They aren't, fail */
+        BASE_TRACE_FAILURE();
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return NULL;
+    }
+
+    /* Convert ZEROINIT */
+    if (uFlags & LMEM_ZEROINIT) Flags |= HEAP_ZERO_MEMORY;
+
+    /* Check if we're not movable, which means pointer-based heap */
+    if (!(uFlags & LMEM_MOVEABLE))
+    {
+        /* Allocate heap for it */
+        Ptr = RtlAllocateHeap(BaseHeap, Flags, dwBytes);
+        BASE_TRACE_ALLOC2(Ptr);
+        return Ptr;
+    }
+
+    /* This is heap based, so lock it in first */
+    RtlLockHeap(BaseHeap);
+
+    /*
+     * Disable locking, enable custom flags, and write the
+     * movable flag (deprecated)
+     */
+    Flags |= HEAP_NO_SERIALIZE |
+             HEAP_SETTABLE_USER_VALUE |
+             BASE_HEAP_FLAG_MOVABLE;
+
+    /* Allocate the handle */
+    HandleEntry = BaseHeapAllocEntry();
+    if (!HandleEntry)
+    {
+        /* Fail */
+        hMemory = NULL;
+        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+        BASE_TRACE_FAILURE();
+        goto Quickie;
+    }
+
+    /* Get the object and make sure we have size */
+    hMemory = &HandleEntry->Object;
+    if (dwBytes)
+    {
+        /* Allocate the actual memory for it */
+        Ptr = RtlAllocateHeap(BaseHeap, Flags, dwBytes);
+        BASE_TRACE_PTR(HandleEntry, Ptr);
+        if (!Ptr)
+        {
+            /* We failed, manually set the allocate flag and free the handle */
+            HandleEntry->Flags = RTL_HANDLE_VALID;
+            BaseHeapFreeEntry(HandleEntry);
+
+            /* For the cleanup case */
+            HandleEntry = NULL;
+        }
+        else
+        {
+            /* All worked well, save our heap entry */
+            RtlSetUserValueHeap(BaseHeap, HEAP_NO_SERIALIZE, Ptr, hMemory);
+        }
+    }
+
+Quickie:
+    /* Cleanup! First unlock the heap */
+    RtlUnlockHeap(BaseHeap);
+
+    /* Check if a handle was allocated */
+    if (HandleEntry)
+    {
+        /* Set the pointer and allocated flag */
+        HandleEntry->Object = Ptr;
+        HandleEntry->Flags = RTL_HANDLE_VALID;
+        if (!Ptr)
+        {
+            /* We don't have a valid pointer, but so reuse this handle */
+            HandleEntry->Flags |= BASE_HEAP_ENTRY_FLAG_REUSE;
+        }
+
+        /* Check if the handle is discardable */
+        if (uFlags & GMEM_DISCARDABLE)
+        {
+            /* Save it in the handle entry */
+            HandleEntry->Flags |= BASE_HEAP_ENTRY_FLAG_REUSABLE;
+        }
+
+        /* Check if the handle is moveable */
+        if (uFlags & GMEM_MOVEABLE)
+        {
+            /* Save it in the handle entry */
+            HandleEntry->Flags |= BASE_HEAP_ENTRY_FLAG_MOVABLE;
+        }
+
+        /* Set the pointer */
+        Ptr = hMemory;
+    }
+
+    /* Return the pointer */
+    return Ptr;
+}
+
+/*
+ * @implemented
+ */
+SIZE_T
+NTAPI
+LocalCompact(UINT dwMinFree)
+{
+    /* Call the RTL Heap Manager */
+    return RtlCompactHeap(BaseHeap, 0);
+}
+
+/*
+ * @implemented
+ */
+UINT
+NTAPI
+LocalFlags(HLOCAL hMem)
+{
+    PBASE_HEAP_HANDLE_ENTRY HandleEntry;
+    HANDLE Handle = NULL;
+    ULONG Flags = 0;
+    UINT uFlags = LMEM_INVALID_HANDLE;
+
+    /* Start by locking the heap */
+    RtlLockHeap(BaseHeap);
+
+    /* Check if this is a simple RTL Heap Managed block */
+    if (!((ULONG_PTR)hMem & BASE_HEAP_IS_HANDLE_ENTRY))
+    {
+        /* Then we'll query RTL Heap */
+        RtlGetUserInfoHeap(BaseHeap, Flags, hMem, &Handle, &Flags);
+        BASE_TRACE_PTR(Handle, hMem);
+
+        /*
+         * Check if RTL Heap didn't find a handle associated with us or
+         * said that this heap isn't movable, which means something we're
+         * really not a handle-based heap.
+         */
+        if (!(Handle) || !(Flags & BASE_HEAP_FLAG_MOVABLE))
+        {
+            /* Then set the flags to 0 */
+            uFlags = 0;
+        }
+        else
+        {
+            /* Otherwise we're handle-based, so get the internal handle */
+            hMem = Handle;
+        }
+    }
+
+    /* Check if the handle is actually an entry in our table */
+    if ((ULONG_PTR)hMem & BASE_HEAP_IS_HANDLE_ENTRY)
+    {
+        /* Then get the entry */
+        HandleEntry = BaseHeapGetEntry(hMem);
+        BASE_TRACE_HANDLE(HandleEntry, hMem);
+
+        /* Make sure it's a valid handle */
+        if (BaseHeapValidateEntry(HandleEntry))
+        {
+            /* Get the lock count first */
+            uFlags = HandleEntry->LockCount & LMEM_LOCKCOUNT;
+
+            /* Now check if it's discardable */
+            if (HandleEntry->Flags & BASE_HEAP_ENTRY_FLAG_REUSABLE)
+            {
+                /* Set the Win32 Flag */
+                uFlags |= LMEM_DISCARDABLE;
+            }
+
+            /* Now check if it's discarded */
+            if (HandleEntry->Flags & BASE_HEAP_ENTRY_FLAG_REUSE)
+               /* Set the Win32 Flag */
+               uFlags |= LMEM_DISCARDED;
+        }
+    }
+
+    /* Check if by now, we still haven't gotten any useful flags */
+    if (uFlags == LMEM_INVALID_HANDLE) SetLastError(ERROR_INVALID_HANDLE);
+
+    /* All done! Unlock heap and return Win32 Flags */
+    RtlUnlockHeap(BaseHeap);
+    return uFlags;
+}
+
+/*
+ * @implemented
+ */
+HLOCAL
+NTAPI
+LocalFree(HLOCAL hMem)
+{
+    /* This is identical to a Global Free */
+    return GlobalFree(hMem);
+}
+
+/*
+ * @implemented
+ */
+HLOCAL
+NTAPI
+LocalHandle(LPCVOID pMem)
+{
+    /* This is identical to a Global Handle */
+    return GlobalHandle(pMem);
+}
+
+/*
+ * @implemented
+ */
+LPVOID
+NTAPI
+LocalLock(HLOCAL hMem)
+{
+    /* This is the same as a GlobalLock, assuming these never change */
+    C_ASSERT(LMEM_LOCKCOUNT == GMEM_LOCKCOUNT);
+    return GlobalLock(hMem);
+}
+
+HLOCAL
+NTAPI
+LocalReAlloc(HLOCAL hMem,
+             SIZE_T dwBytes,
+             UINT uFlags)
+{
+    PBASE_HEAP_HANDLE_ENTRY HandleEntry;
+    LPVOID Ptr;
+    ULONG Flags = 0;
+
+    /* Convert ZEROINIT */
+    if (uFlags & LMEM_ZEROINIT) Flags |= HEAP_ZERO_MEMORY;
+
+    /* If this wasn't a movable heap, then we MUST re-alloc in place */
+    if (!(uFlags & LMEM_MOVEABLE)) Flags |= HEAP_REALLOC_IN_PLACE_ONLY;
+
+    /* Lock the heap and disable built-in locking in the RTL Heap functions */
+    RtlLockHeap(BaseHeap);
+    Flags |= HEAP_NO_SERIALIZE;
+
+    /* Check if this is a simple handle-based block */
+    if (((ULONG_PTR)hMem & BASE_HEAP_IS_HANDLE_ENTRY))
+    {
+        /* Get the entry */
+        HandleEntry = BaseHeapGetEntry(hMem);
+        BASE_TRACE_HANDLE(HandleEntry, hMem);
+
+        /* Make sure the handle is valid */
+        if (!BaseHeapValidateEntry(HandleEntry))
+        {
+            /* Fail */
+            BASE_TRACE_FAILURE();
+            SetLastError(ERROR_INVALID_HANDLE);
+            hMem = NULL;
+        }
+        else if (uFlags & LMEM_MODIFY)
+        {
+            /* User is changing flags... check if the memory was discardable */
+            if (uFlags & LMEM_DISCARDABLE)
+            {
+                /* Then set the flag */
+                HandleEntry->Flags |= BASE_HEAP_ENTRY_FLAG_REUSABLE;
+            }
+            else
+            {
+                /* Otherwise, remove the flag */
+                HandleEntry->Flags &= ~BASE_HEAP_ENTRY_FLAG_REUSABLE;
+            }
+        }
+        else
+        {
+            /* Otherwise, get the object and check if we have no size */
+            Ptr = HandleEntry->Object;
+            if (!dwBytes)
+            {
+                /* Clear the handle and check for a pointer */
+                hMem = NULL;
+                if (Ptr)
+                {
+                    /* Make sure the handle isn't locked */
+                    if ((uFlags & LMEM_MOVEABLE) && !(HandleEntry->LockCount))
+                    {
+                        /* Free the current heap */
+                        RtlFreeHeap(BaseHeap, Flags, Ptr);
+
+                        /* Free the handle */
+                        HandleEntry->Object = NULL;
+                        HandleEntry->Flags |= BASE_HEAP_ENTRY_FLAG_REUSE;
+
+                        /* Get the object pointer */
+                        hMem = &HandleEntry->Object;
+                    }
+                }
+                else
+                {
+                    /* Otherwise just return the object pointer */
+                    hMem = &HandleEntry->Object;
+                }
+            }
+            else
+            {
+                /* Otherwise, we're allocating, so set the new flags needed */
+                Flags |= HEAP_SETTABLE_USER_VALUE | BASE_HEAP_FLAG_MOVABLE;
+                if (!Ptr)
+                {
+                    /* We don't have a base, so allocate one */
+                    Ptr = RtlAllocateHeap(BaseHeap, Flags, dwBytes);
+                    BASE_TRACE_ALLOC2(Ptr);
+                    if (Ptr)
+                    {
+                        /* Allocation succeeded, so save our entry */
+                        RtlSetUserValueHeap(BaseHeap,
+                                            HEAP_NO_SERIALIZE,
+                                            Ptr,
+                                            hMem);
+                    }
+                }
+                else
+                {
+                    /*
+                     * If it's not movable or currently locked, we MUST allocate
+                     * in-place!
+                     */
+                    if (!(uFlags & LMEM_MOVEABLE) && (HandleEntry->LockCount))
+                    {
+                        /* Set the flag */
+                        Flags |= HEAP_REALLOC_IN_PLACE_ONLY;
+                    }
+                    else
+                    {
+                        /* Otherwise clear the flag if we set it previously */
+                        Flags &= ~HEAP_REALLOC_IN_PLACE_ONLY;
+                    }
+
+                    /* And do the re-allocation */
+                    Ptr = RtlReAllocateHeap(BaseHeap, Flags, Ptr, dwBytes);
+                }
+
+                /* Make sure we have a pointer by now */
+                if (Ptr)
+                {
+                    /* Write it in the handle entry and mark it in use */
+                    HandleEntry->Object = Ptr;
+                    HandleEntry->Flags &= ~BASE_HEAP_ENTRY_FLAG_REUSE;
+                }
+                else
+                {
+                    /* Otherwise we failed */
+                    hMem = NULL;
+                    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+                }
+            }
+        }
+    }
+    else if (!(uFlags & LMEM_MODIFY))
+    {
+        /* Otherwise, this is a simple RTL Managed Heap, so just call it */
+        hMem = RtlReAllocateHeap(BaseHeap,
+                                 Flags | HEAP_NO_SERIALIZE,
+                                 hMem,
+                                 dwBytes);
+        if (!hMem)
+        {
+            /* Fail */
+            BASE_TRACE_FAILURE();
+            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+        }
+    }
+
+    /* All done, unlock the heap and return the pointer */
+    RtlUnlockHeap(BaseHeap);
+    return hMem;
+}
+
+/*
+ * @implemented
+ */
+SIZE_T
+WINAPI
+LocalShrink(HLOCAL hMem,
+            UINT cbNewSize)
+{
+    /* Call RTL */
+    return RtlCompactHeap(BaseHeap, 0);
+}
+
+/*
+ * @implemented
+ */
+SIZE_T
+NTAPI
+LocalSize(HLOCAL hMem)
+{
+    /* This is the same as a Global Size */
+    return GlobalSize(hMem);
+}
+
+/*
+ * @implemented
+ */
+BOOL
+NTAPI
+LocalUnlock(HLOCAL hMem)
+{
+    PBASE_HEAP_HANDLE_ENTRY HandleEntry;
+    BOOL RetVal = TRUE;
+
+    /* Check if this was a simple allocated heap entry */
+    if (!((ULONG_PTR)hMem & BASE_HEAP_IS_HANDLE_ENTRY))
+    {
+       /* Fail, because LocalUnlock is not supported on LMEM_FIXED allocations */
+       SetLastError(ERROR_NOT_LOCKED);
+       return FALSE;
+    }
+
+    /* Otherwise, lock the heap */
+    RtlLockHeap(BaseHeap);
+
+    /* Get the handle entry */
+    HandleEntry = BaseHeapGetEntry(hMem);
+    BASE_TRACE_HANDLE(HandleEntry, hMem);
+    _SEH2_TRY
+    {
+        /* Make sure it's valid */
+        if (!BaseHeapValidateEntry(HandleEntry))
+        {
+            /* It's not, fail */
+            BASE_TRACE_FAILURE();
+            SetLastError(ERROR_INVALID_HANDLE);
+            RetVal = FALSE;
+        }
+        else
+        {
+            /* Otherwise, decrement lock count, unless we're already at 0*/
+            if (!HandleEntry->LockCount--)
+            {
+                /* In which case we simply lock it back and fail */
+                HandleEntry->LockCount++;
+                SetLastError(ERROR_NOT_LOCKED);
+                RetVal = FALSE;
+            }
+            else if (!HandleEntry->LockCount)
+            {
+                /* Nothing to unlock */
+                SetLastError(NO_ERROR);
+                RetVal = FALSE;
+            }
+        }
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        RetVal = FALSE;
+    }
+    _SEH2_END;
+
+    /* All done. Unlock the heap and return the pointer */
+    RtlUnlockHeap(BaseHeap);
+    return RetVal;
+}
+
+/* EOF */