Ошибка CLIPBRD_E_CANT_OPEN при настройке буфера обмена из .NET

StackOverflow https://stackoverflow.com/questions/68666

  •  09-06-2019
  •  | 
  •  

Вопрос

Почему следующий код иногда вызывает исключение с содержимым "CLIPBRD_E_CANT_OPEN":

Clipboard.SetText(str);

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

Это было полезно?

Решение

На самом деле, я думаю, что это ошибка Win32 API.

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

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

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

Другие советы

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

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

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

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

Решение:

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

Clipboard.SetText(str);

используйте вместо этого

Clipboard.SetDataObject(str);

На самом деле может возникнуть и другая проблема.Вызов фреймворка (как WPF, так и winform) для чего-то подобного (код взят из reflector):

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 в этом случае всегда вызывается с значением true .

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

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

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

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

Ниже приведен класс-оболочка, который я создал.Мог бы кто-нибудь пожалуйста просмотрите его и скажите, правильно это или нет.Особенно, когда управляемый код запущен как приложение x64 (я использую любой процессор в параметрах проекта). Что происходит, когда я ссылаюсь на библиотеки 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;
    }
}

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

я использую

ApplicationCommands.Copy.Execute(null, myDataGrid);

решение состоит в том, чтобы сначала очистить буфер обмена

Clipboard.Clear();
ApplicationCommands.Copy.Execute(null, myDataGrid);
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top