我正在编写一个测试程序,以了解与使用malloc
创建的LPWSTR相比,BSTR的工作原理。
我决定在BSTR和LPWSTR上调用HeapSize来查看它们是否在进程堆上。
BSTR不在进程堆上,但我发现LPWSTR的HeapSize远远大于我复制到其中的Hello World字符串。值非常大,GetLastError
返回0表示没有错误。它似乎为我使用malloc
创建的每个LPWSTR提供了相同的大,所以我做了一个测试,看它是否只是从同一个c运行时堆中分配的。
以下是上述代码:
#include <stdio.h>
#include <Windows.h>
#include <Windowsx.h>
int main(int argc, char **argv)
{
void *testalloc1;
void *testalloc2;
LPWSTR w;
BSTR b;
testalloc1 = malloc(1024);
testalloc2 = calloc(512, 2);
w = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, sizeof(L"Hello, World!"));
wcsncpy(w, L"Hello, World!", sizeof(L"Hello, World!") / sizeof(WCHAR));
b = SysAllocString(w);
_putws(w);
printf("%d.\n", *(DWORD *)(((LONG_PTR)b) - 4));
_putws(b);
printf("w -> %d.\n", HeapSize(GetProcessHeap(), 0, w));
printf("GetLastError -> 0x%d.\n", GetLastError());
printf("testalloc1 -> %d.\n", HeapSize(GetProcessHeap(), 0, testalloc1));
printf("GetLastError -> 0x%d.\n", GetLastError());
printf("testalloc2 -> %d.\n", HeapSize(GetProcessHeap(), 0, testalloc2));
printf("GetLastError -> 0x%d.\n", GetLastError());
printf("b -> %d.\n", HeapSize(
GetProcessHeap(),
0,
(LPVOID)((LONG_PTR)b - sizeof(LONG_PTR))
));
printf("GetLastError -> 0x%d.\n", GetLastError());
return 0;
}
导致:
Hello, World!
26.
Hello, World!
w -> 28.
GetLastError -> 0x0.
testalloc1 -> 47120.
GetLastError -> 0x0.
testalloc2 -> 47120.
GetLastError -> 0x0.
b -> 32.
GetLastError -> 0x0.
我写了另一个测试,试着了解发生了什么;我使用不同的堆类分配函数(HeapAlloc,malloc,calloc)分配了多个内存块并得到了这个输出:
malloc(1024) -> 215432.
calloc(512, 2) -> 215432
calloc(2, 512) -> 215432
new BYTE[1024] -> 215432
new BYTE[large] -> 1024.
malloc(large) -> 1024.
HeapAlloc(1024) -> 1024.
这是上面输出的代码:
#include <Windows.h>
#include <stdio.h>
int main(int argc, char **argv)
{
HANDLE heap;
void *mem[7];
LONG_PTR limit;
heap = GetProcessHeap();
mem[0] = malloc(1024);
mem[1] = calloc(512, 2);
mem[2] = calloc(2, 512);
mem[3] = (void *)new BYTE[1024];
limit = HeapSize(heap, 0, mem[0]);
mem[4] = (void *)new BYTE[limit];
mem[5] = malloc(limit);
mem[6] = HeapAlloc(heap, 0, 1024);
printf("malloc(1024) -> %d.\n", HeapSize(heap, 0, mem[0]));
printf("calloc(512, 2) -> %d.\n", HeapSize(heap, 0, mem[1]));
printf("calloc(2, 512) -> %d.\n", HeapSize(heap, 0, mem[2]));
printf("new BYTE[1024] -> %d.\n", HeapSize(heap, 0, mem[3]));
printf("new BYTE[large] -> %d.\n", HeapSize(heap, 0, mem[4]));
printf("malloc(large) -> %d.\n", HeapSize(heap, 0, mem[5]));
printf("HeapAlloc(1024) -> %d.\n", HeapSize(heap, 0, mem[6]));
return 0;
}
这段代码是否合理地证明malloc,calloc,new都使用内存池作为分配的首选,否则HeapAlloc(在我的系统上),或者我错过了什么?
答案 0 :(得分:3)
对于某些内存指针的调用HeapSize
,我们必须首先知道内存块所在的堆的句柄,然后我们必须确保这个内存指针正好在分配块的开头 - 否则HeapSize
生成异常,如果我们在调试器下,并且没有返回正确的值。
但是可能由给定的指针确定 - 它是属于某个堆(在进程中可以是几个堆)而不仅是块大小,而是从块开始的偏移量(比如SysAllocString
返回不是指向已分配块的指针,但是从块开始偏移8)。这可以在GetProcessHeaps
和HeapWalk
的帮助下完成(在HeapLock和HeapUnlock
内)
void TestHeapPointer(PVOID pv)
{
static volatile UCHAR guz;
ULONG NumberOfHeaps = 0, n = 8;
union {
PVOID buf;
PHANDLE ProcessHeaps;
};
PVOID stack = alloca(guz);
BOOL bFound = FALSE;
do
{
NumberOfHeaps = RtlPointerToOffset(buf = alloca((n - NumberOfHeaps) * sizeof(HANDLE)), stack) / sizeof(HANDLE);
if (NumberOfHeaps >= (n = GetProcessHeaps(NumberOfHeaps, ProcessHeaps)))
{
do
{
HANDLE hHeap = *ProcessHeaps++;
if (HeapLock(hHeap))
{
PROCESS_HEAP_ENTRY phe = {};
while (HeapWalk(hHeap, &phe))
{
if (phe.wFlags & PROCESS_HEAP_ENTRY_BUSY)
{
ULONG_PTR ofs = (ULONG_PTR)pv - (ULONG_PTR)phe.lpData;
if (ofs < phe.cbData)
{
DbgPrint("%p in %p block %x size ofs=%x\n", pv, phe.lpData, phe.cbData, ofs);
bFound = TRUE;
break;
}
}
}
HeapUnlock(hHeap);
}
} while (!bFound && --NumberOfHeaps);
break;
}
} while (NumberOfHeaps < n);
if (!bFound) DbgPrint("%p not in heaps", pv);
}
和测试一样
static const WCHAR cstr[] = L"Hello, World!";
if (PVOID pv = malloc(sizeof(cstr)))
{
TestHeapPointer(pv);
free(pv);
}
if (BSTR b = SysAllocString(cstr))
{
TestHeapPointer(b);
SysFreeString(b);
}