mirror of
https://github.com/reactos/reactos.git
synced 2024-08-19 18:02:55 +00:00
1848 lines
50 KiB
C
1848 lines
50 KiB
C
/*
|
|
* 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 */
|