Ошибка 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");