Почему следующий код иногда вызывает исключение с содержимым «CLIPBRD_E_CANT_OPEN»:
Clipboard.SetText(str);
Обычно это происходит при первом использовании буфера обмена в приложении, а не после этого.
Почему следующий код иногда вызывает исключение с содержимым «CLIPBRD_E_CANT_OPEN»:
Clipboard.SetText(str);
Обычно это происходит при первом использовании буфера обмена в приложении, а не после этого.
На самом деле, я думаю, это ошибка Win32 API.
Чтобы поместить данные в буфер обмена, вы должны открыть его а> во-первых. Буфер обмена может быть открыт только для одного процесса. Итак, когда вы проверяете, открыт ли буфер обмена у другого процесса по какой-либо причине, ваша попытка открыть его потерпит неудачу.
Так уж получилось, что службы терминалов отслеживают буфер обмена, и в более старых версиях Windows (до Vista) вам нужно открыть буфер обмена, чтобы увидеть, что внутри ... что в конечном итоге блокирует вас. Единственное решение - дождаться, пока службы терминалов закроют буфер обмена, и повторить попытку.
Однако важно понимать, что это не относится к службам терминалов: это может случиться с чем угодно. Работа с буфером обмена в Win32 - это состояние гигантской гонки. Но поскольку по задумке вы должны возиться с буфером обмена только в ответ на ввод данных пользователем, это обычно не представляет проблемы.
Это вызвано ошибкой / функцией в буфере обмена служб терминалов (и, возможно, другими вещами) и реализацией буфера обмена .NET. Задержка открытия буфера обмена вызывает ошибку, которая обычно проходит в течение нескольких миллисекунд.
Решение состоит в том, чтобы попробовать несколько раз в цикле и засыпать между ними.
for (int i = 0; i < 10; i++)
{
try
{
Clipboard.SetText(str);
return;
}
catch { }
System.Threading.Thread.Sleep(10);
}
catch {}
- плохая практика. Заменить на catch (COMException ex) { const uint CLIPBRD_E_CANT_OPEN = 0x800401D0; if ((uint)ex.ErrorCode != CLIPBRD_E_CANT_OPEN) throw; }
- person Maxence; 26.10.2017
Я знаю, что это старый вопрос, но проблема все еще существует. Как упоминалось ранее, это исключение возникает, когда системный буфер обмена заблокирован другим процессом. К сожалению, существует множество инструментов для вырезания, программ для создания снимков экрана и инструментов для копирования файлов, которые могут блокировать буфер обмена Windows. Таким образом, вы будете получать исключение каждый раз, когда попытаетесь использовать Clipboard.SetText(str)
, когда такой инструмент установлен на вашем ПК.
Решение:
никогда не использовать
Clipboard.SetText(str);
использовать вместо
Clipboard.SetDataObject(str);
Clipboard.SetDataObject(str, true);
, чтобы данные буфера обмена были доступны вне приложения.
- person deadlydog; 26.11.2020
На самом деле может возникнуть другая проблема. Вызов фреймворка (варианты 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 всегда вызывается со значением true.
Внутренне это вызывает два вызова win32 api: один для установки данных, а другой для их удаления из вашего приложения, чтобы он был доступен после закрытия приложения.
Я видел несколько приложений (несколько плагинов для Chrome и диспетчер загрузок), которые прослушивают событие буфера обмена. Как только сработает первый вызов, приложение откроет буфер обмена для просмотра данных, а второй вызов сброса завершится ошибкой.
Не нашел хорошего решения, кроме как написать свой собственный класс буфера обмена, который использует прямой Win32 API или вызвать setDataObject напрямую с false для хранения данных после закрытия приложения.
Я решил эту проблему для своего собственного приложения, используя собственные функции Win32: OpenClipboard (), CloseClipboard () и SetClipboardData ().
Ниже созданного мной класса-оболочки. Может ли кто-нибудь пожалуйста просмотреть его и сказать, правильное оно или нет. Особенно, когда управляемый код работает как приложение x64 (я использую Any 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;
}
}
Clipboard.SetText()
перед вызовом собственной функции (т.е. использовал собственный способ, только если управляемый не работал). Но если управляемая версия терпит неудачу, она блокирует буфер обмена, а после этой собственной версии также не удается открыть буфер обмена.
- person Mar; 11.05.2015
Это случилось со мной в моем приложении WPF. У меня OpenClipboard Failed (исключение из HRESULT: 0x800401D0 (CLIPBRD_E_CANT_OPEN)).
я использую
ApplicationCommands.Copy.Execute(null, myDataGrid);
решение - сначала очистить буфер обмена
Clipboard.Clear();
ApplicationCommands.Copy.Execute(null, myDataGrid);
Beyond Clipboard
запущен - Clipboard.Clear()
вызовет точно такое же исключение. протестировано с Beyond Compare
версией 4.2.3.22587
- person itsho; 11.04.2018
Используйте версию WinForms (да, использование WinForms в приложениях WPF не вредно), она обрабатывает все, что вам нужно:
System.Windows.Forms.SetDataObject(yourText, true, 10, 100);
Это попытается скопировать ваш текст в буфер обмена, он останется после того, как ваше приложение существует, будет пытаться до 10 раз и будет ждать 100 мс между каждой попыткой.