mirror of
https://github.com/reactos/reactos.git
synced 2024-07-08 13:45:06 +00:00
[HEAP]
- Fix RtlQuery/SetHeapInformation prototypes, move them to an appropriate place. - Implement RtlQuery/SetHeapInformation. - Implement in-place block growing. - Amount of winetest heap failures is down to 6 (3 exceptions still happen). svn path=/trunk/; revision=49111
This commit is contained in:
parent
dff2d51166
commit
0568f4e2ce
|
@ -707,6 +707,17 @@ RtlProtectHeap(
|
|||
IN BOOLEAN Protect
|
||||
);
|
||||
|
||||
NTSYSAPI
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
RtlQueryHeapInformation (
|
||||
IN PVOID HeapHandle,
|
||||
IN HEAP_INFORMATION_CLASS HeapInformationClass,
|
||||
OUT PVOID HeapInformation OPTIONAL,
|
||||
IN SIZE_T HeapInformationLength OPTIONAL,
|
||||
OUT PSIZE_T ReturnLength OPTIONAL
|
||||
);
|
||||
|
||||
NTSYSAPI
|
||||
PWSTR
|
||||
NTAPI
|
||||
|
@ -728,6 +739,16 @@ RtlReAllocateHeap(
|
|||
SIZE_T Size
|
||||
);
|
||||
|
||||
NTSYSAPI
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
RtlSetHeapInformation (
|
||||
IN PVOID HeapHandle,
|
||||
IN HEAP_INFORMATION_CLASS HeapInformationClass,
|
||||
IN PVOID HeapInformation OPTIONAL,
|
||||
IN SIZE_T HeapInformationLength OPTIONAL
|
||||
);
|
||||
|
||||
NTSYSAPI
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
|
@ -735,6 +756,27 @@ RtlLockHeap(
|
|||
IN HANDLE Heap
|
||||
);
|
||||
|
||||
NTSYSAPI
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
RtlMultipleAllocateHeap (
|
||||
IN PVOID HeapHandle,
|
||||
IN DWORD Flags,
|
||||
IN SIZE_T Size,
|
||||
IN DWORD Count,
|
||||
OUT PVOID * Array
|
||||
);
|
||||
|
||||
NTSYSAPI
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
RtlMultipleFreeHeap (
|
||||
IN PVOID HeapHandle,
|
||||
IN DWORD Flags,
|
||||
IN DWORD Count,
|
||||
OUT PVOID * Array
|
||||
);
|
||||
|
||||
NTSYSAPI
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
|
|
|
@ -4958,50 +4958,6 @@ typedef enum _HEAP_INFORMATION_CLASS {
|
|||
HeapEnableTerminationOnCorruption
|
||||
} HEAP_INFORMATION_CLASS;
|
||||
|
||||
NTSYSAPI
|
||||
DWORD
|
||||
NTAPI
|
||||
RtlSetHeapInformation (
|
||||
IN PVOID HeapHandle,
|
||||
IN HEAP_INFORMATION_CLASS HeapInformationClass,
|
||||
IN PVOID HeapInformation OPTIONAL,
|
||||
IN SIZE_T HeapInformationLength OPTIONAL
|
||||
);
|
||||
|
||||
NTSYSAPI
|
||||
DWORD
|
||||
NTAPI
|
||||
RtlQueryHeapInformation (
|
||||
IN PVOID HeapHandle,
|
||||
IN HEAP_INFORMATION_CLASS HeapInformationClass,
|
||||
OUT PVOID HeapInformation OPTIONAL,
|
||||
IN SIZE_T HeapInformationLength OPTIONAL,
|
||||
OUT PSIZE_T ReturnLength OPTIONAL
|
||||
);
|
||||
|
||||
//
|
||||
// Multiple alloc-free APIS
|
||||
//
|
||||
|
||||
DWORD
|
||||
NTAPI
|
||||
RtlMultipleAllocateHeap (
|
||||
IN PVOID HeapHandle,
|
||||
IN DWORD Flags,
|
||||
IN SIZE_T Size,
|
||||
IN DWORD Count,
|
||||
OUT PVOID * Array
|
||||
);
|
||||
|
||||
DWORD
|
||||
NTAPI
|
||||
RtlMultipleFreeHeap (
|
||||
IN PVOID HeapHandle,
|
||||
IN DWORD Flags,
|
||||
IN DWORD Count,
|
||||
OUT PVOID * Array
|
||||
);
|
||||
|
||||
typedef enum _PROCESSOR_CACHE_TYPE {
|
||||
CacheUnified,
|
||||
CacheInstruction,
|
||||
|
|
|
@ -1934,7 +1934,7 @@ RtlProtectHeap(IN PVOID HeapHandle,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
DWORD
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
RtlSetHeapInformation(IN HANDLE HeapHandle OPTIONAL,
|
||||
IN HEAP_INFORMATION_CLASS HeapInformationClass,
|
||||
|
@ -1945,7 +1945,7 @@ RtlSetHeapInformation(IN HANDLE HeapHandle OPTIONAL,
|
|||
return 0;
|
||||
}
|
||||
|
||||
DWORD
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
RtlQueryHeapInformation(HANDLE HeapHandle,
|
||||
HEAP_INFORMATION_CLASS HeapInformationClass,
|
||||
|
@ -1979,7 +1979,7 @@ RtlQueryHeapInformation(HANDLE HeapHandle,
|
|||
}
|
||||
}
|
||||
|
||||
DWORD
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
RtlMultipleAllocateHeap(IN PVOID HeapHandle,
|
||||
IN DWORD Flags,
|
||||
|
@ -1991,7 +1991,7 @@ RtlMultipleAllocateHeap(IN PVOID HeapHandle,
|
|||
return 0;
|
||||
}
|
||||
|
||||
DWORD
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
RtlMultipleFreeHeap(IN PVOID HeapHandle,
|
||||
IN DWORD Flags,
|
||||
|
|
|
@ -955,32 +955,32 @@ RtlpRemoveHeapFromProcessList(PHEAP Heap)
|
|||
Use classic, lernt from university times algorithm for removing an entry
|
||||
from a static array */
|
||||
|
||||
Current = (PHEAP *)&Peb->ProcessHeaps[Heap->ProcessHeapsListIndex - 1];
|
||||
Next = Current + 1;
|
||||
Current = (PHEAP *)&Peb->ProcessHeaps[Heap->ProcessHeapsListIndex - 1];
|
||||
Next = Current + 1;
|
||||
|
||||
/* How many items we need to shift to the left */
|
||||
Count = Peb->NumberOfHeaps - (Heap->ProcessHeapsListIndex - 1);
|
||||
/* How many items we need to shift to the left */
|
||||
Count = Peb->NumberOfHeaps - (Heap->ProcessHeapsListIndex - 1);
|
||||
|
||||
/* Move them all in a loop */
|
||||
while (--Count)
|
||||
{
|
||||
/* Copy it and advance next pointer */
|
||||
*Current = *Next;
|
||||
/* Move them all in a loop */
|
||||
while (--Count)
|
||||
{
|
||||
/* Copy it and advance next pointer */
|
||||
*Current = *Next;
|
||||
|
||||
/* Update its index */
|
||||
(*Current)->ProcessHeapsListIndex -= 1;
|
||||
/* Update its index */
|
||||
(*Current)->ProcessHeapsListIndex -= 1;
|
||||
|
||||
/* Advance pointers */
|
||||
Current++;
|
||||
Next++;
|
||||
}
|
||||
/* Advance pointers */
|
||||
Current++;
|
||||
Next++;
|
||||
}
|
||||
|
||||
/* Decrease total number of heaps */
|
||||
Peb->NumberOfHeaps--;
|
||||
/* Decrease total number of heaps */
|
||||
Peb->NumberOfHeaps--;
|
||||
|
||||
/* Zero last unused item */
|
||||
Peb->ProcessHeaps[Peb->NumberOfHeaps] = NULL;
|
||||
Heap->ProcessHeapsListIndex = 0;
|
||||
/* Zero last unused item */
|
||||
Peb->ProcessHeaps[Peb->NumberOfHeaps] = NULL;
|
||||
Heap->ProcessHeapsListIndex = 0;
|
||||
|
||||
/* Release the lock */
|
||||
RtlLeaveHeapLock(&RtlpProcessHeapsListLock);
|
||||
|
@ -1977,6 +1977,8 @@ RtlpAllocateNonDedicated(PHEAP Heap,
|
|||
|
||||
/* Release the lock */
|
||||
if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
|
||||
DPRINT1("HEAP: Allocation failed!\n");
|
||||
DPRINT1("Flags %x\n", Heap->Flags);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -2016,7 +2018,8 @@ RtlAllocateHeap(IN PVOID HeapPtr,
|
|||
if (Size >= 0x80000000)
|
||||
{
|
||||
RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY);
|
||||
return FALSE;
|
||||
DPRINT1("HEAP: Allocation failed!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (Flags & (
|
||||
|
@ -2167,6 +2170,7 @@ RtlAllocateHeap(IN PVOID HeapPtr,
|
|||
// Set STATUS!
|
||||
/* Release the lock */
|
||||
if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
|
||||
DPRINT1("HEAP: Allocation failed!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -2202,6 +2206,7 @@ RtlAllocateHeap(IN PVOID HeapPtr,
|
|||
|
||||
/* Release the lock */
|
||||
if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
|
||||
DPRINT1("HEAP: Allocation failed!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -2277,7 +2282,8 @@ BOOLEAN NTAPI RtlFreeHeap(
|
|||
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
DPRINT1("Failed releasing memory with Status 0x%08X\n", Status);
|
||||
DPRINT1("HEAP: Failed releasing memory with Status 0x%08X. Heap %p, ptr %p, base address %p\n",
|
||||
Status, Heap, Ptr, VirtualEntry);
|
||||
RtlSetLastWin32ErrorAndNtStatusFromNtStatus(Status);
|
||||
}
|
||||
}
|
||||
|
@ -2351,8 +2357,225 @@ RtlpGrowBlockInPlace (IN PHEAP Heap,
|
|||
IN SIZE_T Size,
|
||||
IN SIZE_T Index)
|
||||
{
|
||||
/* We always fail growing in place now */
|
||||
return FALSE;
|
||||
UCHAR EntryFlags, RememberFlags;
|
||||
PHEAP_FREE_ENTRY FreeEntry, UnusedEntry, FollowingEntry;
|
||||
SIZE_T FreeSize, PrevSize, TailPart, AddedSize = 0;
|
||||
PHEAP_ENTRY_EXTRA OldExtra, NewExtra;
|
||||
|
||||
/* We can't grow beyond specified threshold */
|
||||
if (Index > Heap->VirtualMemoryThreshold)
|
||||
return FALSE;
|
||||
|
||||
/* Get entry flags */
|
||||
EntryFlags = InUseEntry->Flags;
|
||||
|
||||
/* Get the next free entry */
|
||||
FreeEntry = (PHEAP_FREE_ENTRY)(InUseEntry + InUseEntry->Size);
|
||||
|
||||
if (EntryFlags & HEAP_ENTRY_LAST_ENTRY)
|
||||
{
|
||||
/* There is no next block, just uncommitted space. Calculate how much is needed */
|
||||
FreeSize = (Index - InUseEntry->Size) << HEAP_ENTRY_SHIFT;
|
||||
FreeSize = ROUND_UP(FreeSize, PAGE_SIZE);
|
||||
|
||||
/* Find and commit those pages */
|
||||
FreeEntry = RtlpFindAndCommitPages(Heap,
|
||||
Heap->Segments[InUseEntry->SegmentOffset],
|
||||
&FreeSize,
|
||||
FreeEntry);
|
||||
|
||||
/* Fail if it failed... */
|
||||
if (!FreeEntry) return FALSE;
|
||||
|
||||
/* It was successful, perform coalescing */
|
||||
FreeSize = FreeSize >> HEAP_ENTRY_SHIFT;
|
||||
FreeEntry = RtlpCoalesceFreeBlocks(Heap, FreeEntry, &FreeSize, FALSE);
|
||||
|
||||
/* Check if it's enough */
|
||||
if (FreeSize + InUseEntry->Size < Index)
|
||||
{
|
||||
/* Still not enough */
|
||||
RtlpInsertFreeBlock(Heap, FreeEntry, FreeSize);
|
||||
Heap->TotalFreeSize += FreeSize;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Remember flags of this free entry */
|
||||
RememberFlags = FreeEntry->Flags;
|
||||
|
||||
/* Sum up sizes */
|
||||
FreeSize += InUseEntry->Size;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The next block indeed exists. Check if it's free or in use */
|
||||
if (FreeEntry->Flags & HEAP_ENTRY_BUSY) return FALSE;
|
||||
|
||||
/* Next entry is free, check if it can fit the block we need */
|
||||
FreeSize = InUseEntry->Size + FreeEntry->Size;
|
||||
if (FreeSize < Index) return FALSE;
|
||||
|
||||
/* Remember flags of this free entry */
|
||||
RememberFlags = FreeEntry->Flags;
|
||||
|
||||
/* Remove this block from the free list */
|
||||
RtlpRemoveFreeBlock(Heap, FreeEntry, FALSE, FALSE);
|
||||
Heap->TotalFreeSize -= FreeEntry->Size;
|
||||
}
|
||||
|
||||
PrevSize = (InUseEntry->Size << HEAP_ENTRY_SHIFT) - InUseEntry->UnusedBytes;
|
||||
FreeSize -= Index;
|
||||
|
||||
/* Don't produce too small blocks */
|
||||
if (FreeSize <= 2)
|
||||
{
|
||||
Index += FreeSize;
|
||||
FreeSize = 0;
|
||||
}
|
||||
|
||||
/* Process extra stuff */
|
||||
if (RememberFlags & HEAP_ENTRY_EXTRA_PRESENT)
|
||||
{
|
||||
/* Calculate pointers */
|
||||
OldExtra = (PHEAP_ENTRY_EXTRA)(InUseEntry + InUseEntry->Size - 1);
|
||||
NewExtra = (PHEAP_ENTRY_EXTRA)(InUseEntry + Index - 1);
|
||||
|
||||
/* Copy contents */
|
||||
*NewExtra = *OldExtra;
|
||||
|
||||
// FIXME Tagging
|
||||
}
|
||||
|
||||
/* Update sizes */
|
||||
InUseEntry->Size = Index;
|
||||
InUseEntry->UnusedBytes = ((Index << HEAP_ENTRY_SHIFT) - Size);
|
||||
|
||||
/* Check if there is a free space remaining after merging those blocks */
|
||||
if (!FreeSize)
|
||||
{
|
||||
/* Update flags and sizes */
|
||||
InUseEntry->Flags |= RememberFlags & HEAP_ENTRY_LAST_ENTRY;
|
||||
|
||||
/* Either update previous size of the next entry or mark it as a last
|
||||
entry in the segment*/
|
||||
if (RememberFlags & HEAP_ENTRY_LAST_ENTRY)
|
||||
Heap->Segments[InUseEntry->SegmentOffset]->LastEntryInSegment = InUseEntry;
|
||||
else
|
||||
(InUseEntry + InUseEntry->Size)->PreviousSize = InUseEntry->Size;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Complex case, we need to split the block to give unused free space
|
||||
back to the heap */
|
||||
UnusedEntry = (PHEAP_FREE_ENTRY)(InUseEntry + Index);
|
||||
UnusedEntry->PreviousSize = Index;
|
||||
UnusedEntry->SegmentOffset = InUseEntry->SegmentOffset;
|
||||
|
||||
/* Update the following block or set the last entry in the segment */
|
||||
if (RememberFlags & HEAP_ENTRY_LAST_ENTRY)
|
||||
{
|
||||
/* Set last entry and set flags and size */
|
||||
Heap->Segments[InUseEntry->SegmentOffset]->LastEntryInSegment = InUseEntry;
|
||||
UnusedEntry->Flags = RememberFlags;
|
||||
UnusedEntry->Size = FreeSize;
|
||||
|
||||
/* Insert it to the heap and update total size */
|
||||
RtlpInsertFreeBlockHelper(Heap, UnusedEntry, FreeSize, FALSE);
|
||||
Heap->TotalFreeSize += FreeSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* There is a block after this one */
|
||||
FollowingEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)UnusedEntry + FreeSize);
|
||||
|
||||
if (FollowingEntry->Flags & HEAP_ENTRY_BUSY)
|
||||
{
|
||||
/* Update flags and set size of the unused space entry */
|
||||
UnusedEntry->Flags = RememberFlags & (~HEAP_ENTRY_LAST_ENTRY);
|
||||
UnusedEntry->Size = FreeSize;
|
||||
|
||||
/* Update previous size of the following entry */
|
||||
FollowingEntry->PreviousSize = FreeSize;
|
||||
|
||||
/* Insert it to the heap and update total free size */
|
||||
RtlpInsertFreeBlockHelper(Heap, UnusedEntry, FreeSize, FALSE);
|
||||
Heap->TotalFreeSize += FreeSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* That following entry is also free, what a fortune! */
|
||||
RememberFlags = FollowingEntry->Flags;
|
||||
|
||||
/* Remove it */
|
||||
RtlpRemoveFreeBlock(Heap, FollowingEntry, FALSE, FALSE);
|
||||
Heap->TotalFreeSize -= FollowingEntry->Size;
|
||||
|
||||
/* And make up a new combined block */
|
||||
FreeSize += FollowingEntry->Size;
|
||||
UnusedEntry->Flags = RememberFlags;
|
||||
|
||||
/* Check where to put it */
|
||||
if (FreeSize <= HEAP_MAX_BLOCK_SIZE)
|
||||
{
|
||||
/* Fine for a dedicated list */
|
||||
UnusedEntry->Size = FreeSize;
|
||||
|
||||
if (RememberFlags & HEAP_ENTRY_LAST_ENTRY)
|
||||
Heap->Segments[UnusedEntry->SegmentOffset]->LastEntryInSegment = (PHEAP_ENTRY)UnusedEntry;
|
||||
else
|
||||
((PHEAP_ENTRY)UnusedEntry + FreeSize)->PreviousSize = FreeSize;
|
||||
|
||||
/* Insert it back and update total size */
|
||||
RtlpInsertFreeBlockHelper(Heap, UnusedEntry, FreeSize, FALSE);
|
||||
Heap->TotalFreeSize += FreeSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The block is very large, leave all the hassle to the insertion routine */
|
||||
RtlpInsertFreeBlock(Heap, UnusedEntry, FreeSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy user settable flags */
|
||||
InUseEntry->Flags &= ~HEAP_ENTRY_SETTABLE_FLAGS;
|
||||
InUseEntry->Flags |= ((Flags & HEAP_SETTABLE_USER_FLAGS) >> 4);
|
||||
|
||||
/* Properly "zero out" (and fill!) the space */
|
||||
if (Flags & HEAP_ZERO_MEMORY)
|
||||
{
|
||||
RtlZeroMemory((PCHAR)(InUseEntry + 1) + PrevSize, Size - PrevSize);
|
||||
}
|
||||
else if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED)
|
||||
{
|
||||
/* Calculate tail part which we need to fill */
|
||||
TailPart = PrevSize & (sizeof(ULONG) - 1);
|
||||
|
||||
/* "Invert" it as usual */
|
||||
if (TailPart) TailPart = 4 - TailPart;
|
||||
|
||||
if (Size > (PrevSize + TailPart))
|
||||
AddedSize = (Size - (PrevSize + TailPart)) & ~(sizeof(ULONG) - 1);
|
||||
|
||||
if (AddedSize)
|
||||
{
|
||||
RtlFillMemoryUlong((PCHAR)(InUseEntry + 1) + PrevSize + TailPart,
|
||||
AddedSize,
|
||||
ARENA_INUSE_FILLER);
|
||||
}
|
||||
}
|
||||
|
||||
/* Fill the new tail */
|
||||
if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED)
|
||||
{
|
||||
RtlFillMemory((PCHAR)(InUseEntry + 1) + Size,
|
||||
HEAP_ENTRY_SIZE,
|
||||
HEAP_TAIL_FILL);
|
||||
}
|
||||
|
||||
/* Return success */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
PHEAP_ENTRY_EXTRA NTAPI
|
||||
|
@ -3270,18 +3493,34 @@ RtlProtectHeap(IN PVOID HeapHandle,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
DWORD
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
RtlSetHeapInformation(IN HANDLE HeapHandle OPTIONAL,
|
||||
IN HEAP_INFORMATION_CLASS HeapInformationClass,
|
||||
IN PVOID HeapInformation,
|
||||
IN SIZE_T HeapInformationLength)
|
||||
{
|
||||
UNIMPLEMENTED;
|
||||
return 0;
|
||||
/* Setting heap information is not really supported except for enabling LFH */
|
||||
if (HeapInformationClass == 0) return STATUS_SUCCESS;
|
||||
|
||||
/* Check buffer length */
|
||||
if (HeapInformationLength < sizeof(ULONG))
|
||||
{
|
||||
/* The provided buffer is too small */
|
||||
return STATUS_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
/* Check for a special magic value for enabling LFH */
|
||||
if (*(PULONG)HeapInformation == 2)
|
||||
{
|
||||
DPRINT1("RtlSetHeapInformation() needs to enable LFH\n");
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
DWORD
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
RtlQueryHeapInformation(HANDLE HeapHandle,
|
||||
HEAP_INFORMATION_CLASS HeapInformationClass,
|
||||
|
@ -3289,11 +3528,29 @@ RtlQueryHeapInformation(HANDLE HeapHandle,
|
|||
SIZE_T HeapInformationLength OPTIONAL,
|
||||
PSIZE_T ReturnLength OPTIONAL)
|
||||
{
|
||||
UNIMPLEMENTED;
|
||||
return 0;
|
||||
PHEAP Heap = (PHEAP)HeapHandle;
|
||||
|
||||
/* Only HeapCompatibilityInformation is supported */
|
||||
if (HeapInformationClass != HeapCompatibilityInformation)
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
|
||||
/* Set result length */
|
||||
if (ReturnLength) *ReturnLength = sizeof(ULONG);
|
||||
|
||||
/* Check buffer length */
|
||||
if (HeapInformationLength < sizeof(ULONG))
|
||||
{
|
||||
/* It's too small, return needed length */
|
||||
return STATUS_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
/* Return front end heap type */
|
||||
*(PULONG)HeapInformation = Heap->FrontEndHeapType;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
DWORD
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
RtlMultipleAllocateHeap(IN PVOID HeapHandle,
|
||||
IN DWORD Flags,
|
||||
|
@ -3305,7 +3562,7 @@ RtlMultipleAllocateHeap(IN PVOID HeapHandle,
|
|||
return 0;
|
||||
}
|
||||
|
||||
DWORD
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
RtlMultipleFreeHeap(IN PVOID HeapHandle,
|
||||
IN DWORD Flags,
|
||||
|
|
Loading…
Reference in a new issue