2008-09-16 5 views
32

Warum manchmal ist der folgende Code eine Ausnahme mit dem Inhalt „CLIPBRD_E_CANT_OPEN“ Ursachen:CLIPBRD_E_CANT_OPEN Fehler bei der Einstellung die Zwischenablage von .NET

Clipboard.SetText(str); 

Dies geschieht in der Regel das erste Mal in der Zwischenablage in der Anwendung verwendet wird und nicht danach.

+0

Das ist eine ziemlich kludgey Lösung ist klar - es ist wirklich der einzige Weg? – Blorgbeard

+0

Dies sieht so aus, wie MS es in Forms implementiert hat. Diese Frage war über WPF (obwohl ich nicht wusste, dass es wichtig war). –

Antwort

29

Eigentlich denke ich, das ist die fault of the Win32 API.

Um Daten in die Zwischenablage zu setzen, müssen Sie zunächst open it. Nur ein Prozess kann die Zwischenablage gleichzeitig geöffnet haben. Also, wenn Sie überprüfen, ob ein anderer Prozess die Zwischenablage aus irgendeinem Grund geöffnet hat, wird Ihr Versuch, es zu öffnen, fehlschlagen.

Es passiert einfach, dass die Terminaldienste die Zwischenablage verfolgen, und bei älteren Versionen von Windows (vor Vista) müssen Sie die Zwischenablage öffnen, um zu sehen, was sich darin befindet ..., was Sie blockiert. Die einzige Lösung besteht darin, zu warten, bis die Terminaldienste die Zwischenablage schließen und es erneut versuchen.

Es ist wichtig zu wissen, dass dies nicht speziell für Terminaldienste gilt: Es kann mit allem passieren. Die Arbeit mit der Zwischenablage in Win32 ist eine gigantische Wettlaufbedingung. Aber da Sie eigentlich nur als Reaktion auf Benutzereingaben mit der Zwischenablage herumspielen sollten, ist das normalerweise kein Problem.

30

Dies wird durch einen Fehler/eine Funktion in der Terminaldienste-Zwischenablage (und möglichen anderen Dingen) und der .NET-Implementierung der Zwischenablage verursacht. Eine Verzögerung beim Öffnen der Zwischenablage verursacht den Fehler, der normalerweise innerhalb weniger Millisekunden vergeht.

Die Lösung ist es, mehrmals innerhalb einer Schleife zu versuchen und dazwischen zu schlafen.

for (int i = 0; i < 10; i++) 
{ 
    try 
    { 
     Clipboard.SetText(str); 
     return; 
    } 
    catch { } 
    System.Threading.Thread.Sleep(10); 
} 
+4

Wenn Sie sich die Interna von Clipboard.SetText ansehen, sehen Sie zumindest auf .NET 2.0 SP1, dass es bereits eine Wiederholungs-/Warteschleife gibt. Wiederholt bis zu 10 Mal mit einer Verzögerung von 100 ms. –

+12

@Mike: System.Windows.Forms.Clipboard hat eine Wiederholung, aber System.Windows.Clipboard von WPF nicht. –

+0

Das hat den Trick für unsere WPF-Anwendung, danke! –

5

Eigentlich könnte es ein anderes Problem zur Hand sein. Das Framework Aufruf (sowohl die WPF und winform Aromen), um so etwas wie dieses (Code aus Reflektor):

private static void SetDataInternal(string format, object data) 
{ 
    bool flag; 
    if (IsDataFormatAutoConvert(format)) 
    { 
     flag = true; 
    } 
    else 
    { 
     flag = false; 
    } 
    IDataObject obj2 = new DataObject(); 
    obj2.SetData(format, data, flag); 
    SetDataObject(obj2, true); 
} 

Beachten Sie, dass SetDataObject immer wahr in diesem Fall aufgerufen wird.

Intern löst dies zwei Aufrufe an die Win32 API aus, eine zum Festlegen der Daten und eine zum Löschen der Daten aus Ihrer App, damit sie nach dem Schließen der App verfügbar ist.

Ich habe mehrere Apps gesehen (einige Chrome-Plugins und einen Download-Manager), die das Zwischenablage-Ereignis anhören. Sobald der erste Anruf eintrifft, öffnet die App die Zwischenablage, um in die Daten zu schauen, und der zweite Anruf zum Löschen wird fehlschlagen.

Haben keine gute Lösung gefunden, außer meine eigene Zwischenablage-Klasse zu schreiben, die direkte Win32-API verwendet oder setDataObject direkt mit false aufrufen, um Daten zu speichern, nachdem die App geschlossen wird.

4

Ich löste dieses Problem für meine eigene App mit nativen Win32-Funktionen: OpenClipboard(), CloseClipboard() und SetClipboardData().

Unter der Wrapper Klasse habe ich gemacht. Könnte jemand bitte überprüfen Sie es und sagen, ob es korrekt ist oder nicht. Vor allem, wenn der gesteuerte Code als x64-App läuft (ich verwende Any CPU in den Projektoptionen). Was passiert, wenn ich eine Verbindung zu x86-Bibliotheken von der x64-App herstelle?

Vielen Dank!

Hier ist der Code:

public static class ClipboardNative 
{ 
    [DllImport("user32.dll")] 
    private static extern bool OpenClipboard(IntPtr hWndNewOwner); 

    [DllImport("user32.dll")] 
    private static extern bool CloseClipboard(); 

    [DllImport("user32.dll")] 
    private static extern bool SetClipboardData(uint uFormat, IntPtr data); 

    private const uint CF_UNICODETEXT = 13; 

    public static bool CopyTextToClipboard(string text) 
    { 
     if (!OpenClipboard(IntPtr.Zero)){ 
      return false; 
     } 

     var global = Marshal.StringToHGlobalUni(text); 

     SetClipboardData(CF_UNICODETEXT, global); 
     CloseClipboard(); 

     //------------------------------------------- 
     // Not sure, but it looks like we do not need 
     // to free HGLOBAL because Clipboard is now 
     // responsible for the copied data. (?) 
     // 
     // Otherwise the second call will crash 
     // the app with a Win32 exception 
     // inside OpenClipboard() function 
     //------------------------------------------- 
     // Marshal.FreeHGlobal(global); 

     return true; 
    } 
} 
+0

PS: Ich habe auch versucht, Managed' Clipboard.SetText() 'Aufruf vor dem Aufruf der" native "-Funktion (d. H. Nur nativ verwendet, wenn die verwalteten nicht funktionierte). Wenn die verwaltete Version fehlschlägt, wird die Zwischenablage gesperrt, und nach dieser systemeigenen Version kann die Zwischenablage nicht mehr geöffnet werden. – Mar

9

Ich weiß, diese Frage ist alt, aber das Problem weiterhin besteht. Wie zuvor erwähnt, tritt diese Ausnahme auf, wenn die Systemzwischenablage von einem anderen Prozess blockiert wird. Leider gibt es viele Schnipsel, Programme für Screenshots und Dateikopierwerkzeuge, die die Windows-Zwischenablage blockieren können. Sie erhalten also die Ausnahme, wenn Sie versuchen,zu verwenden, wenn ein solches Tool auf Ihrem PC installiert ist.

Lösung:

nie

Clipboard.SetText(str); 

Verwendung anstelle

Clipboard.SetDataObject(str); 
+2

Das ist großartig. Warum funktioniert SetDataObject dann? – Carol

+0

@K_Rol: Sieht aus wie Yishai Galatzers Antwort erklärt es. – Cameron

0

verwenden diese in meinem WPF-Anwendung mit mir geschehen. Ich habe OpenClipboard fehlgeschlagen (Ausnahme von HRESULT: 0x800401D0 (CLIPBRD_E_CANT_OPEN)).

i verwenden

ApplicationCommands.Copy.Execute(null, myDataGrid); 

Lösung wird die Zwischenablage erste

Clipboard.Clear(); 
ApplicationCommands.Copy.Execute(null, myDataGrid); 
Verwandte Themen