Ошибка OpenClipboard при копировании данных из WPF DataGrid
У меня есть приложение WPF, использующее datagrid. Приложение отлично работало, пока я не установил Visual Studio 2012 и Blend + SketchFlow. Теперь, когда я пытаюсь скопировать данные из сетки в буфер обмена с помощью Ctrl + C (в любом приложении), я получаю следующее исключение:
System.Runtime.InteropServices.COMException (0x800401D0): OpenClipboard Failed (Exception from HRESULT: 0x800401D0 (CLIPBRD_E_CANT_OPEN))
at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
at System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(Int32 errorCode, IntPtr errorInfo)
at System.Windows.Clipboard.Flush()
at System.Windows.Clipboard.CriticalSetDataObject(Object data, Boolean copy)
at System.Windows.Controls.DataGrid.OnExecutedCopy(ExecutedRoutedEventArgs args)
at System.Windows.Controls.DataGrid.OnExecutedCopy(Object target, ExecutedRoutedEventArgs args)
at System.Windows.Input.CommandBinding.OnExecuted(Object sender, ExecutedRoutedEventArgs e)
at System.Windows.Input.CommandManager.ExecuteCommandBinding(Object sender, ExecutedRoutedEventArgs e, CommandBinding commandBinding)
at System.Windows.Input.CommandManager.FindCommandBinding(CommandBindingCollection commandBindings, Object sender, RoutedEventArgs e, ICommand command, Boolean execute)
at System.Windows.Input.CommandManager.FindCommandBinding(Object sender, RoutedEventArgs e, ICommand command, Boolean execute)
at System.Windows.Input.CommandManager.OnExecuted(Object sender, ExecutedRoutedEventArgs e)
at System.Windows.UIElement.OnExecutedThunk(Object sender, ExecutedRoutedEventArgs e)
at System.Windows.Input.ExecutedRoutedEventArgs.InvokeEventHandler(Delegate genericHandler, Object target)
at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
at System.Windows.UIElement.RaiseTrustedEvent(RoutedEventArgs args)
at System.Windows.UIElement.RaiseEvent(RoutedEventArgs args, Boolean trusted)
at System.Windows.Input.RoutedCommand.ExecuteImpl(Object parameter, IInputElement target, Boolean userInitiated)
at System.Windows.Input.RoutedCommand.ExecuteCore(Object parameter, IInputElement target, Boolean userInitiated)
at System.Windows.Input.CommandManager.TranslateInput(IInputElement targetElement, InputEventArgs inputEventArgs)
at System.Windows.UIElement.OnKeyDownThunk(Object sender, KeyEventArgs e)
at System.Windows.Input.KeyEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget)
at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
at System.Windows.UIElement.RaiseTrustedEvent(RoutedEventArgs args)
at System.Windows.UIElement.RaiseEvent(RoutedEventArgs args, Boolean trusted)
at System.Windows.Input.InputManager.ProcessStagingArea()
at System.Windows.Input.InputManager.ProcessInput(InputEventArgs input)
at System.Windows.Input.InputProviderSite.ReportInput(InputReport inputReport)
at System.Windows.Interop.HwndKeyboardInputProvider.ReportInput(IntPtr hwnd, InputMode mode, Int32 timestamp, RawKeyboardActions actions, Int32 scanCode, Boolean isExtendedKey, Boolean isSystemKey, Int32 virtualKey)
at System.Windows.Interop.HwndKeyboardInputProvider.ProcessKeyAction(MSG& msg, Boolean& handled)
at System.Windows.Interop.HwndSource.CriticalTranslateAccelerator(MSG& msg, ModifierKeys modifiers)
at System.Windows.Interop.HwndSource.OnPreprocessMessage(Object param)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
at System.Windows.Threading.Dispatcher.Invoke(DispatcherPriority priority, Delegate method, Object arg)
at System.Windows.Interop.HwndSource.OnPreprocessMessageThunk(MSG& msg, Boolean& handled)
at System.Windows.Interop.HwndSource.WeakEventPreprocessMessage.OnPreprocessMessage(MSG& msg, Boolean& handled)
at System.Windows.Interop.ComponentDispatcherThread.RaiseThreadMessage(MSG& msg)
at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
at System.Windows.Threading.Dispatcher.Run()
at System.Windows.Application.RunDispatcher(Object ignore)
at System.Windows.Application.RunInternal(Window window)
at System.Windows.Application.Run(Window window)
at System.Windows.Application.Run()
Это действительно раздражает.
Я видел некоторые ссылки на эту проблему здесь и в разных местах в Интернете без реального решения.
Я могу проверить, заблокирован ли буфер обмена, когда это исключение возникает в Visual Studio, поскольку я не мог скопировать вставку сообщения (должен был записать его в файл). Кроме того, буфер обмена не был заблокирован до начала процесса копирования.
Как решить эту проблему?
Ответы
Ответ 1
Мы используем .NET 4.0. У нас была та же проблема, но после выхода из системы код работал нормально в течение некоторого времени.
Наконец, мы нашли альтернативу.
Если вы хотите скопировать строку в буфер обмена,
string data = "Copy This"
До сих пор я использовал следующий метод
Clipboard.SetText(data);
Он терпел неудачу снова и снова. Затем я просмотрел другие доступные методы, чтобы установить текст в буфер обмена в Clipboard Class и попробовал следующее:
Clipboard.SetDataObject(data);
И это сработало:). У меня больше не было проблемы.
Ответ 2
Это ошибка в обработчике буфера обмена WPF. Вам нужно обработать необработанное исключение в событии Application.DispatcherUnhandledException.
Добавьте этот атрибут в элемент Application
в вашем App.xaml
DispatcherUnhandledException="Application_DispatcherUnhandledException"
Добавьте этот код в файл App.xaml.cs
void Application_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
var comException = e.Exception as System.Runtime.InteropServices.COMException;
if (comException != null && comException.ErrorCode == -2147221040)
e.Handled = true;
}
Ответ 3
У меня тоже возникла проблема в приложении, где я копирую информацию в буфер обмена, когда пользователи просматривают ListBox. Скопированная информация относится к выбранному элементу и позволяет им вставлять его (указанную информацию) в другие приложения для удобства. Иногда я получаю CLIPBRD_E_CANT_OPEN в некоторых пользовательских системах, но не на других.
Пока я до сих пор не смог исправить конфликт, мне удалось создать код, чтобы найти приложение, вызывающее конфликт. Я бы хотел по крайней мере поделиться этим кодом в надежде, что это поможет кому-то. Я добавлю используя инструкцию, атрибуты и метод, которые я создал, чтобы найти объект Process of the Culprit. Из элемента "Процесс" вы можете получить имя процесса, PID, заголовок главного окна (если оно есть) и другие потенциально полезные данные. Здесь строки кода, которые я добавил без кода, который его вызывает. ( ПРИМЕЧАНИЕ: Ниже фрагмента кода у меня есть еще один лакомый кусочек, чтобы поделиться):
using System.Diagnostics; // For Process class
using System.Runtime.InteropServices; // For DllImport's
...
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern IntPtr GetOpenClipboardWindow();
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
...
///-----------------------------------------------------------------------------
/// <summary>
/// Gets the Process that holding the clipboard
/// </summary>
/// <returns>A Process object holding the clipboard, or null</returns>
///-----------------------------------------------------------------------------
public Process ProcessHoldingClipboard()
{
Process theProc = null;
IntPtr hwnd = GetOpenClipboardWindow();
if (hwnd != IntPtr.Zero)
{
uint processId;
uint threadId = GetWindowThreadProcessId(hwnd, out processId);
Process[] procs = Process.GetProcesses();
foreach (Process proc in procs)
{
IntPtr handle = proc.MainWindowHandle;
if (handle == hwnd)
{
theProc = proc;
}
else if (processId == proc.Id)
{
theProc = proc;
}
}
}
return theProc;
}
ДРУГОЕ ПРИМЕЧАНИЕ: Еще одна вещь, которую я изменил, которая упростила мой код, заключалась в том, чтобы конвертировать из System.Windows.Clipboard в System.Windows.Forms.Clipboard(см. System.Windows.Forms.Clipboard Class), поскольку последний имеет 4-параметрический метод SetDataObject(), который включает в себя счетчик повторных попыток и задержку повторения в миллисекундах. Это, по крайней мере, устранило некоторые из повторных шумов из моего кода.
Ваш пробег может варьироваться... плюс могут быть побочные эффекты в этом, на которые я еще не наткнулся, поэтому, если кто-нибудь знает о них, прокомментируйте. В любом случае, надеюсь, это окажется полезным для кого-то.
Ответ 4
У меня также была эта проблема в WPF 4.0 и 4.5, так как я установил TeraCopy (Windows 7, 64-разрядный). У каждого Clipboard.SetText() произошел сбой в System.Runtime.InteropServices.COMException.
Моим первым решением было удалить TeraCopy - это сработало, но мне нравится это приложение, поэтому мне пришлось искать другое решение для решения этой проблемы. Решение заключалось в замене
Clipboard.SetText("my string");
с
Clipboard.SetDataObject("my string");
Ответ 5
У меня была такая же проблема с RichTextBox. Следующий код разбился случайным образом:
TextRange tr = new TextRange(rich.Document.ContentStart, rich.Document.ContentEnd);
System.Windows.Clipboard.SetDataObject(tr.Text);
Кажется предпочтительным использовать System.Windows.Controls.RichTextBox.Copy
Ответ 6
У меня возникла проблема с извлечением данных XAML из буфера обмена с помощью .NET 4.6.1.
Сообщение об ошибке:
Ошибка OpenClipboard (исключение из HRESULT: 0x800401D0 (CLIPBRD_E_CANT_OPEN)))
Я решил это следующим образом:
int counter = 0;
object xamlClipData = null;
while (xamlClipData == null)
{
try
{
if (counter > 10)
{
System.Windows.MessageBox.Show("No access to clipboard xaml data.");
break;
}
counter++;
if (System.Windows.Clipboard.GetDataObject().GetDataPresent(DataFormats.Xaml))
{
xamlClipData = System.Windows.Clipboard.GetData(DataFormats.Xaml);
}
}
catch { }
}
Ответ 7
У меня была та же проблема при копировании ячеек Excel в буфер обмена и получение данных из буфера обмена в виде строки HTML.
Вы можете использовать (while-try-catch), как в приведенном ниже коде.
Excel.Application exap = new Microsoft.Office.Interop.Excel.Application();
Excel.Workbook wb = exap.Workbooks.Open(
sourceFileNameTextBox.Text,
Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing);
Excel.Sheets sh = wb.Worksheets;
bool clip = false;
// Copy Excel cells to clipboard
while (!clip)
{
try
{
ws.Cells.get_Range(cells[0], cells[1]).Copy(Type.Missing);
clip = true;
}
catch
{
clip = false;
}
}
string b = "";
// Get Excel cells data from the clipboard as HTML
clip = false;
while(!clip)
{
try
{
b = Clipboard.GetData(DataFormats.Html) as string;
clip = true;
}
catch
{
clip = false;
}
}
Кроме того, вы можете иметь счетчик в while
, если цикл более 10 раз или более, возникает исключение. Я проверяю его максимальный счетчик - это одно и однократное выполнение буфера обмена.
Ответ 8
Добавление моего ответа из упомянутого ТАК вопроса для справки -
Нашел это у Эндрю Смита по адресу http://blogs.infragistics.com/forums/t/35379.aspx -
Технически только 1 процесс может открыть буфер обмена, поэтому, если другой процесс открыл его, последующие запросы не будут выполнены, пока первый не освободит буфер обмена. Это было как-то обработано в классе буфера обмена WinForms, где он будет повторять набор с задержкой между каждой попыткой, но класс буфера обмена WPF этого не делает, поэтому в случае неудачи при первом показе возникает исключение. Даже тогда мы, вероятно, должны перехватить исключение и вызвать ошибку операции с буфером обмена, если она все еще не работает.
То же самое объясняется, и некоторые способы исправить это упоминаются в этом итальянском блоге -
Буфер обмена WPF DataGrid (?) И работа
Гугл-перевод
Следующая ветка форума MSDN предполагает, что это может быть проблема, связанная с конкретной машиной, можете ли вы воспроизвести ее на других машинах? -
CLIPBRD_E_CANT_OPEN исключение при копировании в буфер обмена из DataGrid
Обновить:
Ссылка на блог не работает, но по этой ссылке можно получить доступ к кэшированной версии -
Буфер обмена WPF DataGrid BUG (?) & Work (Кэшированный перевод)
Ответ 9
Для этой точной цели CopyingRowClipboardContent
сигнатура события/метода DataGrid CopyingRowClipboardContent
(object sender
, DataGridRowClipboardEventArgs
e), которая является более надежной, чем Clipboard.SetDataObject(data)
или Clipboard.SetText(data)
.
Вот как это использовать.
Установите "FullRow" в режиме SelectionUnit для dataGrid с именем myDataGrid
<DataGrid x:Name="myDataGrid" SelectionUnit="FullRow"></DataGrid>
У нас есть метод myDataGrid_CopyingRowClipboardContent
, который myDataGrid_CopyingRowClipboardContent
для каждой строки в dataGrid для копирования его содержимого в буфер обмена. Например, для сетки данных с семью строками это называется семь раз.
public int clipboardcalledcnt { get; set; } // CopyingRowClipboardContent invoked count
private void myDataGrid_CopyingRowClipboardContent(object sender, DataGridRowClipboardEventArgs e)
{
PathInfo cellpath = new PathInfo(); // A custom class to hold path information
string path = string.Empty;
DataGrid dgdataPaths = (DataGrid)sender;
int rowcnt = dgdataPaths.SelectedItems.Count;
cellpath = (PathInfo)e.Item;
path = "Row #" + clipboardcalledcnt + " Len=" + cellpath.Length.ToString() + ", path=" + cellpath.Path;
e.ClipboardRowContent.Clear();
if (clipboardcalledcnt == 0) // Add header to clipboard paste
e.ClipboardRowContent.Add(new DataGridClipboardCellContent("", null, "--- Clipboard Paste ---\t\t\n")); // \t cell divider, repeat (number of cells - 1)
clipboardcalledcnt++;
e.ClipboardRowContent.Add(new DataGridClipboardCellContent(path, null, path));
if (clipboardcalledcnt == rowcnt)
clipboardcalledcnt = 0;
}
Ответ 10
Это сработало и для меня.
Изменить класс с:
Clipboard.SetText("my string");
чтобы:
Clipboard.SetDataObject("my string");