我想阅读一些关于 windows 剪贴板内部工作原理的文章或书籍,但我找不到至少一篇比标准 API 参考或使用 .
我正在使用标准 winapi 上的 windows 剪贴板,我得到了一些奇怪的结果。
案例 1:我将一些 unicode 字符串写入剪贴板并记住该字符串的地址。然后我关闭剪贴板,并重复以下过程:打开剪贴板,获取我的 unicode 字符串的地址,关闭剪贴板。
我认为我必须收到剪贴板内容的相同地址,但事实并非如此。为什么?
//1.) Copying string to clipboard
if (WinAPI.OpenClipboard(owner))
{
WinAPI.EmptyClipboard();
IntPtr exampleStringPtr = Marshal.StringToHGlobalUni("Example");
Console.WriteLine("setting address: {0}", exampleStringPtr.ToInt32());
WinAPI.SetClipboardData(WinAPI.CF_UNICODETEXT, exampleStringPtr);
WinAPI.CloseClipboard();
}
//2.) Getting string from clipboard
for (int i = 0; i < 100; i++ )
if (WinAPI.OpenClipboard(owner))
{
IntPtr exampleStringPtr = WinAPI.GetClipboardData(WinAPI.CF_UNICODETEXT);
Console.WriteLine("getting address: {0}", exampleStringPtr.ToInt32());
WinAPI.GlobalLock(exampleStringPtr);
var s = Marshal.PtrToStringUni(exampleStringPtr);
WinAPI.GlobalUnlock(exampleStringPtr);
WinAPI.CloseClipboard();
}
案例 2:我向剪贴板写入一些字符串,关闭剪贴板,更改字符串(在非托管内存中),然后再次打开剪贴板并读取该字符串。令我惊讶的是,我获得了相同的字符串地址和我的 UNCHANGED 字符串。
//1.) Copying string to clipboard
if (WinAPI.OpenClipboard(owner))
{
WinAPI.EmptyClipboard();
IntPtr exampleStringPtr = Marshal.StringToHGlobalUni("Loooooooooooonng String Example");
Console.WriteLine("setting address: {0}", exampleStringPtr.ToInt32());
WinAPI.SetClipboardData(WinAPI.CF_UNICODETEXT, exampleStringPtr);
WinAPI.CloseClipboard();
//2.) Change string - replace first 10 characters on one any symbol
for (int i = 0; i < 10; i++)
{
Marshal.WriteByte(exampleStringPtr + i, 50);
}
//3.) Obtain string and make sure that string was changed
Console.WriteLine("changed string: {0}", Marshal.PtrToStringUni(exampleStringPtr));
}
//2.) Getting string from clipboard
for (int i = 0; i < 100; i++)
if (WinAPI.OpenClipboard(owner))
{
IntPtr exampleStringPtr = WinAPI.GetClipboardData(WinAPI.CF_UNICODETEXT);
Console.WriteLine("getting address: {0}", exampleStringPtr.ToInt32());
WinAPI.GlobalLock(exampleStringPtr);
var s = Marshal.PtrToStringUni(exampleStringPtr);
Console.WriteLine("obtained string: {0}", s);
WinAPI.CloseClipboard();
}
现在我在想剪贴板将 SetClipboardData 中的所有内存块复制到其他内存,并且可以多次复制源块。我不明白,为什么我不能在 SetClipboardData 执行后立即为字符串释放非托管内存?
我有很多问题,我认为一些文献会说清楚
更新:
致 Raymond Chen、Jonathan Potter、Eric Brown:感谢您的回答,但我编辑了我的第二个测试,它会更正确,现在显示如下:我在剪贴板关闭之前更改源字符串,我可能认为这是有效的操作和它删除了我在剪贴板关闭后做出的答案。然后我得到这个字符串,结果显示它已更改,然后我通过调用 GetClipboardData 得到此字符串,结果显示字符串已更改且指针相同。然后我关闭剪贴板再次打开它并再次读取字符串。我现在得到什么?字符串地址与源字符串的地址相同,但字符串未更改。这是这段代码:
//1.) Copying string to clipboard
if (WinAPI.OpenClipboard(owner))
{
WinAPI.EmptyClipboard();
IntPtr exampleStringPtr = Marshal.StringToHGlobalUni("Loooooooooooonng String Example");
Console.WriteLine("setting address: {0}", exampleStringPtr.ToInt32());
WinAPI.SetClipboardData(WinAPI.CF_UNICODETEXT, exampleStringPtr);
//2.) Change string while clipboard isn't closed - replace first 10 characters on one any symbol
for (int i = 0; i < 10; i++)
{
Marshal.WriteByte(exampleStringPtr + i, 50);
}
//3.) Obtain string and make sure that string was changed
Console.WriteLine("changed string: {0}", Marshal.PtrToStringUni(exampleStringPtr));
//4.) Get this string from clipboard and make sure that clipboard was changed
exampleStringPtr = WinAPI.GetClipboardData(WinAPI.CF_UNICODETEXT);
Console.WriteLine("getting address of changed string: {0}", exampleStringPtr.ToInt32());
var lockedPtr = WinAPI.GlobalLock(exampleStringPtr);
var s = Marshal.PtrToStringUni(exampleStringPtr);
WinAPI.GlobalUnlock(lockedPtr);
Console.WriteLine("obtained string: {0}", s);
WinAPI.CloseClipboard();
}
Console.WriteLine("\n-------Close and open clipboard------------------\n");
//5.) Getting string from clipboard
for (int i = 0; i < 100; i++)
if (WinAPI.OpenClipboard(owner))
{
IntPtr exampleStringPtr = WinAPI.GetClipboardData(WinAPI.CF_UNICODETEXT);
Console.WriteLine("getting address: {0}", exampleStringPtr.ToInt32());
var lockedPtr = WinAPI.GlobalLock(exampleStringPtr);
var s = Marshal.PtrToStringUni(lockedPtr);
WinAPI.GlobalUnlock(lockedPtr);
Console.WriteLine("obtained string: {0}", s);
WinAPI.CloseClipboard();
}
我运行程序,暂停它并通过 WinDbg 分析内存。然后我将结果截图并提供给您。http://postimg.org/image/are6um7yv/所以,我的测试和屏幕截图显示:
1.)我们在内存中有一个源对象的多个副本 2.)如果我在关闭剪贴板之前更改给 SetClipboardData 调用的源内存,在再次打开剪贴板后,即使在源地址上也可以恢复源对象。
不是吗?谁能解释一下,这些条款是否正确?
更新 2:好的.. 我正在重写我的第三个 C++ 测试。它在这里:
#include "stdafx.h"
#include "windows.h"
#include "conio.h"
int main()
{
HWND owner = GetConsoleWindow();
//1.) Copying string to clipboard
if (OpenClipboard(owner))
{
EmptyClipboard();
//WCHAR *str = L"Loooong string example";
char *str = "Loooooooong string Example";
int cch = strlen(str);
char* strptr = (char*)GlobalAlloc(GMEM_MOVEABLE, (cch + 1));
printf("setting (segment??) address: %X \n", strptr);
LPVOID lockedPtr = GlobalLock(strptr);
printf("locked setting address: %X \n", lockedPtr);
// copy
memcpy(lockedPtr, str, cch);
GlobalUnlock(strptr);
// set to clipboard
SetClipboardData(CF_TEXT, strptr);
//2.) Change string while clipboard isn't closed - replace first 10 characters on one any symbol
lockedPtr = GlobalLock(strptr);
for (int i = 0; i < 10; i++)
{
((char*)lockedPtr)[i] = 50;
}
GlobalUnlock(strptr);
//3.) Obtain string and make sure that string was changed
lockedPtr = GlobalLock(strptr);
printf("changed string: %s \n", lockedPtr);
GlobalUnlock(strptr);
//4.) Get this string from clipboard and make sure that clipboard was changed
strptr = (char*)GetClipboardData(CF_TEXT);
printf("getting address: %X \n", strptr);
lockedPtr = GlobalLock(strptr);
printf("locked getting address: %X \n", lockedPtr);
printf("obtained string: %s \n", (char*)lockedPtr );
GlobalUnlock(strptr);
CloseClipboard();
}
printf("\n-------Close and open clipboard------------------\n");
//5.) Getting string from clipboard
for (int i = 0; i < 10; i++)
{
//Sleep(1000);
if (OpenClipboard(owner))
{
HANDLE exampleStringPtr = GetClipboardData(CF_TEXT);
printf("getting address: %X \n", exampleStringPtr);
char* lockedPtr = (char*)GlobalLock(exampleStringPtr);
printf("locked getting address: %X \n", lockedPtr);
printf("obtained string: %s \n", lockedPtr);
GlobalUnlock(exampleStringPtr);
CloseClipboard();
}
}
getch();
return 0;
}
真的,现在当我调用 GetClipboardData 时,我总是获得指向数据的相同指针。但有时它与我放入剪贴板的第一个字符串的锁定指针不同。
但是虽然我是在 C++ 上编写这个测试,但我仍然有我早期编写的相同效果。
如果我在调用 SetClipboardData 后更改源内存块,然后尝试调用 GetClipboardData,我将获得更改后的内存块。但是当我关闭此剪贴板然后再次打开它时,我更改的内存块被一些信息覆盖,我不知道,当我调用 GetClipboardData 时,该内存块恢复到初始状态,就好像我没有更改它一样.
如果剪贴板没有副本并且我更改了源块,剪贴板从哪里“知道”如何恢复该块?
我录制了一小段截屏视频,显示记忆恢复的时刻http://screencast.com/t/5t3wc9LS