it-swarm.com.ru

Сбой OpenClipboard при копировании данных из WPF DataGrid

У меня есть приложение WPF, использующее сетку данных. Приложение работало нормально, пока я не установил 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, поскольку я не смог скопировать, вставить сообщение (пришлось записать его в файл). Кроме того, буфер обмена не был заблокирован до начала процесса копирования.

Как решить эту проблему?

63
Arsen Zahray

Мы используем .NET 4.0. У нас была та же проблема, но после выхода из системы код работал нормально в течение некоторого времени.

Наконец мы нашли альтернативу.

Если вы хотите скопировать строку в буфер обмена,

string data = "Copy This"

До сих пор я использовал следующий метод

Clipboard.SetText(data);

Это терпело неудачу снова и снова. Затем я посмотрел на другие методы, доступные для установки текста в буфере обмена в Класс буфера обмена, и попробовал следующее:

Clipboard.SetDataObject(data);

И это сработало :). У меня никогда не было проблемы снова.

79
kushdilip

Это ошибка в обработчике буфера обмена 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;
}
68
Alex Wiese

У меня тоже была проблема в приложении, где я копирую информацию в буфер обмена, когда пользователи просматривают ListBox. Скопированная информация относится к выбранному элементу и позволяет им для удобства вставить ее (упомянутую информацию) в другие приложения. Иногда я получаю CLIPBRD_E_CANT_OPEN на одних пользовательских системах, но не на других.

Хотя я до сих пор не смог исправить разногласия, я смог создать некоторый код, чтобы найти приложение, вызывающее разногласия. Я хотел бы хотя бы поделиться этим кодом в надежде, что он кому-нибудь поможет. Я добавлю оператор using, атрибуты и метод, который я создал, чтобы найти объект преступника Process. Из элемента Process вы можете получить имя процесса, 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's 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 ), поскольку последний имеет метод с четырьмя параметрами SetDataObject (), который включает в себя количество повторов и задержку повторения в миллисекундах. Это, по крайней мере, удалило часть повторных попыток noise из моего кода. 

Ваш пробег может варьироваться ... плюс могут быть побочные эффекты, на которые я еще не наткнулся, поэтому, если кто-то знает о них, пожалуйста, прокомментируйте. В любом случае, я надеюсь, что это окажется полезным для кого-то.

6
John

У меня также была эта проблема в WPF 4.0 и 4.5, так как я установил TeraCopy (Windows 7, 64-bit). Каждый Clipboard.SetText () завершился ошибкой с исключением System.Runtime.InteropServices.COME.

Моим первым решением было удалить TeraCopy - оно работало, но мне нравится это приложение, поэтому мне пришлось искать другое решение для решения этой проблемы. Решение было заменить

Clipboard.SetText("my string");

с

Clipboard.SetDataObject("my string");
5
pr0gg3r

У меня была такая же проблема с RichTextBox. Следующий код случайно вышел из строя:

TextRange tr = new TextRange(rich.Document.ContentStart, rich.Document.ContentEnd);
System.Windows.Clipboard.SetDataObject(tr.Text);

Кажется, предпочтительнее использовать System.Windows.Controls.RichTextBox.Copy

2
AVEbrahimi

У меня была проблема с получением данных 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 { }
}
2
Pollitzer

У меня была такая же проблема при копировании ячеек 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 раз или более, возникает исключение. Я проверяю его максимальный счетчик за один и тот же цикл работы буфера обмена.

1
Majid gharaei

Добавление моего ответа из упомянутого SO вопроса для справки -

Нашел это у Эндрю Смита по адресу http://blogs.infragistics.com/forums/t/35379.aspx -

Технически только 1 процесс может открыть буфер обмена, так что если другой процесс открыл его, последующие запросы не будут выполняться до первый релиз буфера обмена. Это как бы обрабатывалось в WinForms Класс буфера обмена, где он будет повторять набор с задержкой между каждая попытка, но класс буфера обмена WPF не делает этого, поэтому в случае сбоя первое шоу исключение происходит. Даже тогда мы должны вероятно перехватить исключение и вызвать ошибку операции буфера обмена, если оно по-прежнему не удается.

То же самое объясняется, и некоторые способы исправить это упоминаются в этом итальянском блоге -

Буфер обмена WPF DataGrid BUG (?) & Работа

Гугл-перевод

Следующая ветка форума MSDN предполагает, что это может быть проблема, связанная с конкретной машиной, можете ли вы воспроизвести ее на других машинах? -

CLIPBRD_E_CANT_OPEN исключение при копировании в буфер обмена из DataGrid

Обновление:

Ссылка на блог не работает, но по этой ссылке можно получить доступ к кэшированной версии -

Буфер обмена WPF DataGrid BUG (?) & Work (Кэшированный перевод)

1
akjoshi

Для этой конкретной цели есть сигнатура события/метода DataGrid CopyingRowClipboardContent (отправитель объекта, DataGridRowClipboardEventArgs e), которая более надежна, чем Clipboard.SetDataObject (data) или Clipboard.SetText (data).

Вот как это использовать.

Установите «FullRow» в режиме SelectionUnit для dataGrid с именем myDataGrid

<DataGrid x:Name="myDataGrid" SelectionUnit="FullRow"></DataGrid>

У нас есть метод 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;
}
0
Markus