Почему следующий код иногда вызывает исключение с содержимым "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);
}
Я знаю, что этот вопрос старый, но проблема все еще существует. Как упоминалось ранее, это исключение возникает, когда системный буфер обмена блокируется другим процессом. К сожалению, многие инструменты для съёмки, программы для скриншотов и инструменты копирования файлов, которые могут блокировать буфер обмена Windows. Таким образом, вы получите исключение каждый раз, когда пытаетесь использовать Clipboard.SetText(str)
, когда такой инструмент установлен на вашем ПК.
Решение:
никогда не используйте
Clipboard.SetText(str);
используйте
Clipboard.SetDataObject(str);
На самом деле может быть и другая проблема. Рамочный вызов (как 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 для хранения данных после закрытия приложения.
Я решил эту проблему для своего собственного приложения, используя собственные функции 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;
}
}
Это происходит со мной в моем приложении WPF. У меня возникла ошибка OpenClipboard (исключение из HRESULT: 0x800401D0 (CLIPBRD_E_CANT_OPEN)).
Я использую
ApplicationCommands.Copy.Execute(null, myDataGrid);
решение состоит в том, чтобы сначала очистить буфер обмена
Clipboard.Clear();
ApplicationCommands.Copy.Execute(null, myDataGrid);