2008-09-16 3 views
32

Почему следующий код иногда вызывает исключение с содержанием «CLIPBRD_E_CANT_OPEN»:CLIPBRD_E_CANT_OPEN ошибки при установке Clipboard из .NET

Clipboard.SetText(str); 

Это обычно происходит в первый раз, когда буфер используется в приложении, а не после этого.

+0

Это довольно kludgey решение - это действительно единственный способ? – Blorgbeard 2008-09-16 02:07:20

+0

Это похоже на то, как MS реализовала его в Формах. Этот вопрос касался WPF (хотя я не понимал, что это важно). – 2010-01-18 08:22:03

ответ

29

На самом деле, я думаю, что это fault of the Win32 API.

Чтобы установить данные в буфер обмена, сначала необходимо указать open it. Только один процесс может открывать буфер обмена одновременно. Итак, когда вы проверяете, если другой процесс имеет буфер обмена по любой причине, ваша попытка открыть его не удастся.

Как раз так происходит, что службы терминалов отслеживают буфер обмена, а в старых версиях Windows (pre-Vista) вам нужно открыть буфер обмена, чтобы увидеть, что внутри ..., что в конечном итоге блокирует вас. Единственное решение - дождаться, когда Terminal Services закроет буфер обмена и повторит попытку.

Важно понимать, что это не относится к службам терминалов, однако: это может произойти с чем угодно. Работа с буфером обмена в Win32 - это гигантское состояние гонки. Но, поскольку по дизайну вы должны только гадать с буфером обмена в ответ на ввод пользователя, это обычно не представляет проблемы.

30

Это связано с ошибкой/функцией в буфере обмена служб терминалов (и, возможно, другими) и внедрением .NET в буфер обмена. Задержка при открытии буфера обмена вызывает ошибку, которая обычно проходит в течение нескольких миллисекунд.

Решение состоит в том, чтобы попробовать несколько раз в цикле и поспать между ними.

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

Если вы посмотрите на внутренности Clipboard.SetText, по крайней мере на .NET 2.0 SP1, вы увидите, что у него уже есть цикл повтора/ожидания. Повторяется до 10 раз с задержкой в ​​100 мс. – 2008-09-26 16:07:59

+12

@Mike: System.Windows.Forms.Clipboard имеет повторную попытку, но System.Windows.Clipboard из WPF этого не делает. – 2009-09-18 00:46:00

5

На самом деле может быть еще одна проблема. Структура вызова (как WPF и WinForm ароматизаторов) на что-то вроде этого (код от рефлектора):

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); 
} 

Обратите внимание, что SetDataObject всегда вызывается с правдой в этом случае.

Внутренне, что вызывает два вызова win32 api, один для установки данных и один для его очистки от вашего приложения, чтобы он был доступен после закрытия приложения.

Я видел несколько приложений (некоторые хром-плагины и диспетчер загрузки), которые прослушивают событие буфера обмена. Как только первый звонок ударит, приложение откроет буфер обмена, чтобы просмотреть данные, а второй вызов флеша не удастся.

Не нашли подходящего решения, кроме как написать собственный класс буфера обмена, который использует прямой API win32 или для вызова setDataObject напрямую с помощью false для хранения данных после закрытия приложения.

4

Я решил эту проблему для своего приложения, используя собственные функции Win32: OpenClipboard(), CloseClipboard() и SetClipboardData().

Ниже класс обертки, который я сделал. Может кто-нибудь , пожалуйста, просмотрите его и сообщите, если он правильно или нет. Особенно, когда управляемый код работает как приложение x64 (я использую любой CPU в вариантах проекта). Что происходит, когда я ссылаюсь на библиотеки x86 из приложения x64?

Спасибо!

Вот код:

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; 
    } 
} 
9

Я знаю, этот вопрос старый, но проблема все еще существует. Как упоминалось ранее, это исключение возникает, когда системный буфер обмена блокируется другим процессом. К сожалению, многие инструменты для съёмки, программы для скриншотов и инструменты копирования файлов, которые могут блокировать буфер обмена Windows. Таким образом, вы получите исключение каждый раз, когда пытаетесь использовать Clipboard.SetText(str), когда такой инструмент установлен на вашем ПК.

Решение:

никогда не использовать

Clipboard.SetText(str); 

использовать вместо

Clipboard.SetDataObject(str); 
0

Это случилось со мной в моем WPF приложения. Я получил OpenClipboard Failed (Исключение из HRESULT: 0x800401D0 (CLIPBRD_E_CANT_OPEN)).

я использую

ApplicationCommands.Copy.Execute(null, myDataGrid); 

раствор для очистки буфера обмена первого

Clipboard.Clear(); 
ApplicationCommands.Copy.Execute(null, myDataGrid); 
Смежные вопросы