#include <Windows.h>
int main()
{
HANDLE h = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 4); //new char[4]{ "QWE" };
if (!h) return 1;
strcpy_s((char*)h, 4, "QWE");
OpenClipboard(0);
EmptyClipboard();
SetClipboardData(CF_TEXT, h);
CloseClipboard();
HeapFree(h, 0, 0);
return 0;
}
如果我使用
new char[4]{ "QWE" };
而不是 HeapAlloc()
,我会在 SetClipboardData()
执行时收到错误。为什么会这样? h
的内存检查器在两种情况下显示相同的结果。
HeapAlloc()
返回 LPVOID
(void*
) 指针,而 new char[]
返回 char*
指针。它们不是同一类型,并且它们都不是内存对象的有效 Win32 HANDLE
。
SetClipboardData()
想要一个有效的HANDLE
指向内存对象,但不仅仅是HANDLE
的任何类型。根据
SetClipboardData()
文档:
如果
成功,则系统拥有由SetClipboardData
参数标识的对象。 一旦所有权转移到系统,应用程序就不能写入或释放数据,但可以锁定和读取数据直到调用hMem
函数。 (必须在关闭剪贴板之前解锁内存。) 如果CloseClipboard
参数标识内存对象,则该对象必须已使用带hMem
标志的 [GlobalAlloc
] 函数进行分配。GMEM_MOVEABLE
HeapAlloc()
和new[]
都不满足这些要求。您必须使用 GlobalAlloc()
来代替,以便剪贴板可以正确获取内存对象的所有权,例如:
#include <Windows.h>
int main()
{
HANDLE h = GlobalAlloc(GMEM_MOVEABLE, 4);
if (!h) return 1;
char *pmem = (char*) GlobalLock(h);
if (!pmem) {
GlobalFree(h);
return 1;
}
strcpy_s(pmem, 4, "QWE");
GlobalUnlock(h);
if (OpenClipboard(NULL)) {
EmptyClipboard();
if (SetClipboardData(CF_TEXT, h))
h = NULL; // clipboard now owns it, don't free it!
CloseClipboard();
}
if (h) GlobalFree(h);
return 0;
}
在编译时,
HANDLE
只是void*
的别名,并且char*
指针可以隐式转换为void*
,因此编译器将允许HeapAlloc
'ed LPVOID
或将 new[]
'ed char*
传递给 SetClipboardData()
而不会抱怨。 但是在运行时,OS期望传递的HANDLE
指向有效的可移动全局内存对象,其他任何东西都会调用未定义的行为。