OpenClipboard failed when copy pasting data from WPF DataGrid
Asked Answered
V

13

91

I've got a WPF application using datagrid. The application worked fine until I installed Visual Studio 2012 and Blend+SketchFlow preview. Now, when I'm trying to copy the data from the grid into the clipboard with Ctrl + C (in any application), I'm getting the following exception:

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()

This is really annoying.

I've seen some references to this problem here and on various locations on the web, with no real solution.

I can verify that the clipboard is locked when this exception is raised in Visual Studio, as I couldn't copy paste the message (had to write it to a file). Also, the clipboard wasn't locked before the copy process started.

How to solve this problem?

Vantassel answered 7/10, 2012 at 13:29 Comment(1)
The problem actually manifests itself in Visual Studio 2019 when you try and copy the sln file at the point where you see recently opened projects when you start up the IDE.Birkett
B
117

We are using .NET 4.0. We had the same problem, but after logging off the system, code used to work fine for some time.

Finally we found the alternative.

If you want to copy a string to the clipboard,

string data = "Copy This"

Till now I was using the following method

Clipboard.SetText(data);

It was failing again and again. Then I looked at other methods available to set text in the clipboard in Clipboard Class and tried the following:

Clipboard.SetDataObject(data);

And it worked :). I never had the issue again.

Bono answered 16/7, 2013 at 13:57 Comment(4)
Very good. I use .NET 4.5 and the issue is still there. It seems that if I reselect an item in the ListView then this error does occur but I have no idea why.Chug
I use WPF and .NET 4.5 (not 4.5.1) and the issue is gone with this solution! :-)Lukas
SetDataObject worked well here. SetText throws an exception, but the other does not. Thanks!Wardroom
i found that this call works fine with multiple clipboard copy of datagrids.Kohler
T
85

It is a bug in the WPF Clipboard handler. You need to handle the unhandled exception in the Application.DispatcherUnhandledException event.

Add this attribute to the Application element in your App.xaml

DispatcherUnhandledException="Application_DispatcherUnhandledException"

Add this code to your App.xaml.cs file

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;
}
Torgerson answered 23/11, 2012 at 4:39 Comment(7)
Microsoft have fixed this in .NET 4.5, the code is now similar to that found in the Windows Forms assemblies. The clipboard routines have a retry count and delay time instead of just failing as soon as the clipboard can't be accessed.Torgerson
I'm using .NET 4.0 and facing the same issue. I dont' want to touch the App.xaml files. Is there any other solution for this problem???Bono
Why don't you want to touch the app.xaml files? You can subscribe to the Application.DispatcherUnhandledException event from elsewhere but you should do it in your App class when your app loads. More info hereTorgerson
@Bono yes that solution is fine when you are in control of the method that sets the clipboard data but it doesn't help when the method is not in your code. For example WPF controls and third party controls. If you don't implement the bug fix in your code somewhere you will still be affected by it on .NET 4.0.Torgerson
SetText is NOT fixed in 4.5, but using SetDataObject seems to fix it - HOWEVER: My program copies a folder path to the clipboard. It works OK, but if I run the app in the VS debugger and turn .NET exception catching on something weird happens. If I paste into, say, Notepad it works fine, but if I paste into the location box of Windows Explorer my app throws a COM exception "Invalid FORMATETC structure (Exception from HRESULT: 0x80040064 (DV_E_FORMATETC))". This only happens when debugging with exception catching on, so the exception must be caught and handled somewhere, but it's still strange.Eustazio
Even in .net 4.7.1, and even using SetDataObject, I can trigger this exception at will by rapidly and repeatedly clicking on a Copy button. I will be including this handler as a backstop in all my WPF applications (I don't like magic numbers, though, so I'll also include const int CLIPBRD_E_CANT_OPEN = unchecked((int) 0x800401D0)).Autostrada
For me the exception is caught by AppDomain.CurrentDomain.UnhandledException. The program will force closed.Hanseatic
B
12

I, too, have been having a problem in an application where I copy information into the clipboard as users peruse a ListBox. The information that's copied is related to the selected item, and it permits them to paste it (said info) into other applications for convenience. Occasionally I get the CLIPBRD_E_CANT_OPEN on some user's systems, but not on others.

While I still haven't been able to fix the contention, I was able to create some code to find the application causing the contention. I'd like to at least share this code in the hope it helps someone. I will add the using statement, attributes, and method I created to find the Process object of the culprit. From the Process item you can obtain the process' name, PID, main window title (if it has one), and other potentially useful data. Here's the lines of code I added without the code that calls it. (NOTE: Below the code snippet I have one more tidbit to share):

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;
    }

OTHER NOTE: One other thing I changed which simplified my code a bit was to convert from using System.Windows.Clipboard to System.Windows.Forms.Clipboard (see System.Windows.Forms.Clipboard Class)because the latter has a 4-parameter SetDataObject() method which includes a retry count and a retry delay in milliseconds. This at least removed some of the retry noise from my code.

Your mileage may vary...plus there may be side effects in this which I've not yet stumbled upon, so if anyone knows of them please comment. In any event, I hope this proves useful to someone.

Bitt answered 23/1, 2014 at 14:40 Comment(1)
+1 for using System.Windows.Forms.Clipboard instead of System.Windows.Clipboard. thanksTwopiece
A
8

I also had this issue in WPF 4.0 and 4.5 since I installed TeraCopy (Windows 7, 64-bit). Every Clipboard.SetText() failed with a System.Runtime.InteropServices.COMException.

My first solution was to uninstall TeraCopy - it worked, but I love this application, so I had to search for another solution to resolve that issue. The solution was to replace

Clipboard.SetText("my string");

with

Clipboard.SetDataObject("my string");
Aesthetic answered 12/11, 2014 at 1:22 Comment(2)
The downside is, the data will be deleted from system Clipboard when the application exits.Taxpayer
Not if you set the second parameter true to remain data after the application exits. Check this out: learn.microsoft.com/de-de/dotnet/api/…Aesthetic
S
2

I had the same problem with RichTextBox. The following code crashed randomly:

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

It seems it's preferred to use System.Windows.Controls.RichTextBox.Copy

Signal answered 5/7, 2014 at 6:31 Comment(0)
P
2

I had a problem to retrieve XAML data from the clipboard with .NET 4.6.1.

Error message:

OpenClipboard Failed (Exception from HRESULT: 0x800401D0 (CLIPBRD_E_CANT_OPEN)))

I solved it as follows:

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 { }
}
Pudding answered 4/5, 2016 at 11:47 Comment(0)
F
2

I have finaly found a solution to use the default copy mode implemented by DataGrid.

The previous answers didn't work for me:

  • Using Clipboard.SetDataObject(data); insteed of Clipboard.SetText(data) --> This solution was not what i expected, i didn't want to implement myself the copy feature.
  • Handling DispatcherUnhandledException : i do not know why but it didn't work for me. The method attached to this event was not called.

I finally found a new way to handle this problem. You just need to clear the clipboard before you press "Ctrl + C".

So, i made a new style in the MainWindows.xaml file resources:

<Window.Resources>
    <Style TargetType="DataGrid">
        <EventSetter Event="PreviewKeyDown" Handler="DataGrid_PreviewKeyDown"/>
    </Style>
</Window.Resources>

This style is made to handle the "previewKeyDown" in all datagrids of my application. The method called is the following:

private void DataGrid_PreviewKeyDown(object sender, KeyEventArgs e)
{
    if (e.Key == Key.C && (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
    {
        System.Windows.Forms.Clipboard.Clear();
    }
}

After that, the problem was solved.

Filagree answered 1/8, 2019 at 14:53 Comment(1)
This works for me. But instead of using "System.Windows.Forms.Clipboard.Clear()", I have used "System.Windows.Clipboard.Clear()" for wpf.Differentia
M
1

I had the same problem in copying Excel cells to the clipboard and getting data from the clipboard as an HTML string.

You can use (while-try-catch) like in the below code.

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;
    }
}

Also, you can have a counter in the while if the loop is more than 10 times or more, exception occur. I test its maximum counter is one and in one time loop clipboard work.

Mestee answered 12/11, 2014 at 5:20 Comment(0)
R
1

Code app.xaml

<Application.Resources>
        <Style TargetType="DataGrid">
            <EventSetter Event="PreviewKeyDown" Handler="DataGrid_PreviewKeyDown"/>
        </Style>
    </Application.Resources>

code file app.xaml.cs

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;

namespace WpfApp1
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        private void DataGrid_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.C && (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
            {
                System.Windows.Forms.Clipboard.Clear();
            }
        }
    }
}

I have dealt with this code.

Ranger answered 17/9, 2019 at 12:44 Comment(1)
Please improve your answer by editing it in a better way than currently.Sandysandye
I
0

There's a DataGrid event/method signature for this exact purpose CopyingRowClipboardContent(object sender, DataGridRowClipboardEventArgs e) and is more reliable than Clipboard.SetDataObject(data) or Clipboard.SetText(data).

Here's how to use it.

Set "FullRow" at the SelectionUnit mode for dataGrid called myDataGrid

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

We have a method, myDataGrid_CopyingRowClipboardContent, that gets called for each row in the dataGrid to copy its contents to the clipboard. For example,for a datagrid with seven rows this is called seven times.

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;
}
Idiosyncrasy answered 16/9, 2017 at 14:44 Comment(0)
G
0

I write a extension method for WPF Datagrid Export to Excel(CSV):

if "MyDatagrid" is the name of your datagrid, use one line code to call on own user control.

MyDatagrid.ExportToExcel(this);

and add this method to your extension static class

#region DataGrid Extentions

public static void ExportToExcel(this DataGrid dg, UserControl owner, string filename = "")
{
    try
    {
        dg.SelectionMode = DataGridSelectionMode.Extended;
        dg.SelectAllCells();

        Clipboard.Clear();
        ApplicationCommands.Copy.Execute(null, dg);

        var saveFileDialog = new SaveFileDialog
        {
            FileName = filename != "" ? filename : "gpmfca-exportedDocument",
            DefaultExt = ".csv", 
            Filter = "Common Seprated Documents (.csv)|*.csv"
        };

        if (saveFileDialog.ShowDialog() == true)
        {
            var clip2 = Clipboard.GetText();
            File.WriteAllText(saveFileDialog.FileName, clip2.Replace('\t', ','), Encoding.UTF8);
            Process.Start(saveFileDialog.FileName);
        }    
   
        dg.UnselectAllCells();
        dg.SelectionMode = DataGridSelectionMode.Single;
    }
    catch (Exception ex)
    {
        owner.ShowMessageBox(ex.Message);
        Clipboard.Clear();
    }
}
#endregion

finally don't forget to

using Microsoft.Win32;

on extension class and set

ClipboardCopyMode="IncludeHeader"

for your datagrid.

Gormand answered 29/11, 2020 at 17:6 Comment(0)
J
0

Indeed the same here as dlf; the copy seems to succeed but I get an exception nevertheless. Here is some example code:

string s = "";
try
{
   s="xyz";
   // Use SetDataObject() and 'true' to let it survive an application exit
   Clipboard.SetDataObject(s, true);
}
catch (Exception ex)
{
  const int CLIPBRD_E_CANT_OPEN = unchecked((int)0x800401D0);
  if (ex.HResult == CLIPBRD_E_CANT_OPEN)
  {
    // Known bug with .NET 4.5
  }
  else
    MessageBox.Show($"Failed to copy to clipboard: {ex.ToString()}", "Error");
}
Jeanettajeanette answered 2/10, 2023 at 9:14 Comment(0)
B
0

This isn't solution, but an application you can use to test your solution. Insert this into new Console app and run it, it will acquire access to Clipboard, so basically any other app will not get access to it.

You can tweak the miliseconds of free access to clipboard and/or miliseconds of blocked access.

You can run this app in background and then test your main app's solution for this problem.

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool OpenClipboard(IntPtr hWndNewOwner);

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseClipboard();

[STAThread]
public static void Main()
{
    ulong a = 0;
    const int blockMs = 50;
    const int freeMs = 0;

    while (true)
    {
        // Clear all after 1000 cycles
        if (a % 1000 == 0)
        {
            Console.Clear();
        }

        Console.Write("Opening clipboard... ");
        var result = OpenClipboard(IntPtr.Zero);
        Console.WriteLine(result ? "Success" : "Fail");
        Thread.Sleep(blockMs);
        Console.WriteLine($"Slept {blockMs} ms");
        if (result)
        {
            CloseClipboard();
        }

        if (freeMs > 0)
        {
            Thread.Sleep(freeMs);
        }

        a++;
    }
}
Bower answered 20/2 at 9:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.