内部で Windows のクリップボードがどのように機能するかについての記事や本を読みたいのですが、標準の API リファレンスや の使用例よりも詳しい記事が少なくとも 1 つ見つかりません。
標準の 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 さんへ: 回答ありがとうございます。しかし、2 番目のテストを編集して、より正確に修正したところ、次のように表示されました。クリップボードを閉じた後に作成した回答が削除されます。次に、この文字列を取得すると、結果が変更されたことが示されます。次に、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.) メモリ内に 1 つのソース オブジェクトの複数のコピーがあります 2.) クリップボードを閉じる前に SetClipboardData 呼び出しに指定されたソース メモリを変更すると、クリップボードを再度開いた後、ソース アドレスでもソース オブジェクトが復元されます。
そうじゃない?これらの条項が真実かどうか、誰か説明できますか?
更新 2: OK.. C++ で 3 番目のテストを書き直していました。それはここにあります:
#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