2017-05-21 5 views
1

Wenn ich eine riesige Menge Text in der Systemzwischenablage habe (zB 150MB Textdatei), würde ich gerne in der Lage sein, die Systemzwischenablage aus einem Stream als Unicode-Text zu lesen um eine OutOfMemoryException zu vermeiden. Ist das überhaupt möglich, indem Sie das Pinvoke-Beispiel unten optimieren?System Zwischenablage lesen als Stream anstelle von String

Bei diesen sehr großen Zwischenablagen gibt Clipboard.GetText (TextDataFormat.UnicodeText) eine leere Zeichenfolge zurück, ohne eine Ausnahme auszulösen.

Alternativ kann, wenn ich von hier aus wie das Beispiel verwenden pinvoke, werde ich eine 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; 
    } 
+0

Haben Sie die Datei ausgewählt und kopieren oder öffnen, dann den gesamten Text auswählen und dann kopieren? –

+0

als Test Ich öffne eine große Datei in Notepad ++ und wählen Sie alle und dann kopieren, aber in Wirklichkeit könnte die Zwischenablage von woanders kommen wie zum Beispiel SQL Server Management Studio. – tjsmith

+1

150Mb (in ANSI oder UTF8) als Unicode ~ => 300Mb im Speicher. Sie erhalten also 300M (Zwischenablage) + 300M (Ihre .NET-Zeichenkette) => 600M im Speicher des Prozesses, streamen oder streamen nicht. Bei so großen Informationen in der Zwischenablage kann nicht genügend Speicher vorhanden sein. Es sei denn, Sie möchten etwas anderes mit dem Zwischenspeicherinhalt machen, wie es als eine Datei direkt speichern, ohne es als eine Zeichenkette zu realisieren. Diese Vorgehensweise führt zu möglichen Speicherproblemen. –

Antwort

5

bekommen Dies wird die System-Zwischenablage in eine Textdatei schreiben, ohne sie zuerst in einen String umzuwandeln, so dass sehr große Zwischenablagen, die ausgeschrieben werden müssen, ohne auf eine OutOfMemoryException zu stoßen. Es erfordert, dass das Visual Studio-Projekt mit der /unsicheren-Flag erstellt wird.

[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(lpwcstr); 
     } 
    } 
    catch 
    { 
     return false; 
    } 
    finally 
    { 
     try 
     { 
      CloseClipboard(); 
     } 
     catch 
     { 
      //ignore 
     } 
    } 
    return true; 
}