如果系统剪贴板上有大量文本(例如 150MB 文本文件),我希望能够从流中读取系统剪贴板作为 Unicode 文本,以避免 OutOfMemoryException。通过调整下面的 pinvoke 示例是否可以实现这一点?
对于这些非常大的剪贴板,Clipboard.GetText(TextDataFormat.UnicodeText) 将返回空字符串而不引发异常。
或者,如果我像这里的示例一样使用 pinvoke,我将得到 OutOfMemoryException http://komalmangal.blogspot.ca/2016/04/how-to-get-clipboard-data-and-its-size.html
[DllImport("user32.dll")]
static extern IntPtr GetClipboardData(uint uFormat);
[DllImport("user32.dll")]
static extern bool IsClipboardFormatAvailable(uint format);
[DllImport("user32.dll", SetLastError = true)]
static extern bool OpenClipboard(IntPtr hWndNewOwner);
[DllImport("user32.dll", SetLastError = true)]
static extern bool CloseClipboard();
[DllImport("kernel32.dll")]
static extern IntPtr GlobalLock(IntPtr hMem);
[DllImport("kernel32.dll")]
static extern bool GlobalUnlock(IntPtr hMem);
const uint CF_UNICODETEXT = 13;
public static string GetText()
{
if (!IsClipboardFormatAvailable(CF_UNICODETEXT))
return null;
if (!OpenClipboard(IntPtr.Zero))
return null;
string data = null;
var hGlobal = GetClipboardData(CF_UNICODETEXT);
if (hGlobal != IntPtr.Zero)
{
var lpwcstr = GlobalLock(hGlobal);
if (lpwcstr != IntPtr.Zero)
{
data = Marshal.PtrToStringUni(lpwcstr);
GlobalUnlock(lpwcstr);
}
}
CloseClipboard();
return data;
}
这会将系统剪贴板写出到文本文件,而无需先将其转换为字符串,从而允许写出非常大的剪贴板而不会遇到
OutOfMemoryException
。它要求使用 /unsafe 标志构建 Visual Studio 项目。
[DllImport("user32.dll")]
private static extern IntPtr GetClipboardData(uint uFormat);
[DllImport("user32.dll")]
private static extern bool IsClipboardFormatAvailable(uint format);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool OpenClipboard(IntPtr hWndNewOwner);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool CloseClipboard();
[DllImport("kernel32.dll")]
private static extern IntPtr GlobalLock(IntPtr hMem);
[DllImport("kernel32.dll")]
private static extern bool GlobalUnlock(IntPtr hMem);
[DllImport("kernel32.dll")]
private static extern UIntPtr GlobalSize(IntPtr hMem);
private const uint CF_UNICODETEXT = 13;
//Write the clipboard to a text file without having to first convert it to a string.
//This avoids OutOfMemoryException for large clipboards and is faster than other methods
public static bool WriteClipboardTextToFile(string filename)
{
try
{
if (!IsClipboardFormatAvailable(CF_UNICODETEXT) || !OpenClipboard(IntPtr.Zero))
return false;
}
catch
{
return false;
}
try
{
var hGlobal = GetClipboardData(CF_UNICODETEXT);
if (hGlobal == IntPtr.Zero)
return false;
var lpwcstr = GlobalLock(hGlobal);
if (lpwcstr == IntPtr.Zero)
return false;
try
{
long length = (long)GlobalSize(lpwcstr);
Stream stream;
unsafe
{
stream = new UnmanagedMemoryStream((byte*)lpwcstr, length);
}
const int bufSize = 4096;
var buffer = new char[bufSize];
using (var sw = new StreamWriter(new FileStream(filename, FileMode.Create, FileAccess.Write), Encoding.UTF8))
{
//Clipboard text is in Encoding.Unicode == UTF-16LE
using (var sr = new StreamReader(stream, Encoding.Unicode))
{
int charCount;
while (!sr.EndOfStream && (charCount = sr.ReadBlock(buffer, 0, bufSize)) > 0)
{
if (sr.EndOfStream && buffer[charCount - 1] == '\0')
sw.Write(buffer, 0, charCount - 1); //don't write out null terminator
else
sw.Write(buffer, 0, charCount);
}
}
}
}
finally
{
GlobalUnlock(hGlobal);
}
}
catch
{
return false;
}
finally
{
try
{
CloseClipboard();
}
catch
{
//ignore
}
}
return true;
}