我想阅读一些有关如何在其中使用Windows剪贴板的文章或书籍,但我找不到至少一篇比标准API参考或使用示例更深入的文章。
我正在使用Windows剪贴板上的标准winapi,我获得了一些奇怪的结果。
案例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:我向剪贴板写了一些字符串,关闭剪贴板,更改字符串(在非托管内存中)并再次打开剪贴板并读取此字符串。令我惊讶的是,我获得了SAME字符串地址和我的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执行后立即释放我的非托管内存字符串?
我有很多问题,我认为有些文献会说清楚
更新:
对雷蒙德·陈,乔纳森·波特,埃里克·布朗说:感谢您的回答,但我编辑了第二次测试,它会更正确,现在显示如下: 我更改源字符串BEFORE剪贴板关闭,我可能认为它是有效的操作,它删除了剪贴板关闭后我做的答案。然后我得到这个字符串,结果显示已更改,然后通过调用GetClipboardData得到此字符串,结果显示字符串已更改且指针相同。然后我关闭剪贴板再次打开它并再次读取字符串。我现在得到了什么?字符串地址与源字符串的地址相同,但字符串是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);
//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
答案 0 :(得分:1)
SetClipboardData()
的文档非常明确地表示它不复制您提供的数据 - 相反,剪贴板拥有数据句柄,尽管您仍然可以从剪贴板读取直到剪贴板关闭后,SetClipboardData()
调用成功后,您不得写入或释放数据。
关闭剪贴板后,您不再拥有剪贴板,数据对象根本不可安全使用,即使是读取也是如此,因为另一个进程可能已经更改了剪贴板内容。关闭剪贴板后修改数据的测试运气好,不是因为他们应该这样做。
如果SetClipboardData成功,则系统拥有标识的对象 hMem参数。应用程序可能无法写入或释放数据 一旦所有权转移到系统,但它可以锁定和 从数据中读取,直到调用CloseClipboard函数。 (该 在剪贴板关闭之前必须解锁内存。)
编辑:因为您似乎在资源所有权和未定义行为的概念上遇到了麻烦,所以这个类比可能会有所帮助。
// allocate 1 byte of memory
char* ptr = malloc(sizeof(char));
// set the byte to the letter A
*ptr = 'A';
// free the memory
free(ptr);
// set the byte to B
*ptr = 'B';
// verify that the byte is set to B
printf("byte contains %c\n", *ptr);
// allocate another byte of memory
char* ptr2 = malloc(sizeof(char));
// are they the same byte? maybe
printf("byte contains %c - first was %lx, second is %lx\n", *ptr2, ptr, ptr2);
我希望您会看到此代码完全错误。我们分配内存,写入它,释放它,然后我们写入它并再次读取它。然而,如果您编译并运行此代码,它很有可能会工作。第二次分配也很有可能返回与第一次分配相同的地址。发生了什么事?
这称为未定义的行为。该语言没有定义在这种情况下发生的事情。当你释放内存时,你不再拥有它,你不能写它,也不能读它。如果它工作,或似乎工作,这是巧合,仅此而已。无法保证它始终有效。没有必要继续执行测试以试图证明它确实起作用 - 没有任何改变行为未定义的事实。它可能会起作用,也可能不起作用。不要这样做。
答案 1 :(得分:-1)
SetClipboardData
复制给定全局句柄中的数据。剪贴板关闭后,应释放全局句柄(而不是之前)。 GetClipboardData
返回(内部)内存句柄;您应该将此句柄视为只读缓冲区。