从WPF DataGrid复制粘贴数据时,OpenClipboard失败


77

我有一个使用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中引发此异常时验证剪贴板是否已锁定,因为我无法复制粘贴消息(必须将其写入文件)。另外,在复制过程开始之前剪贴板没有被锁定。

如何解决这个问题呢?


当您尝试在启动IDE时看到最近打开的项目的位置复制sln文件时,该问题实际上在Visual Studio 2019中显现出来。
JGFMK

Answers:


98

我们正在使用.NET 4.0。我们遇到了同样的问题,但是在注销系统后,代码可以正常工作一段时间。

最后,我们找到了替代方案。

如果要将字符串复制到剪贴板,

string data = "Copy This"

直到现在我正在使用以下方法

Clipboard.SetText(data);

它一次又一次失败。然后,我查看了可用于在剪贴板类的剪贴板中设置文本的其他方法,并尝试了以下方法:

Clipboard.SetDataObject(data);

它的工作:)。我再也没有这个问题。


5
很好。我使用.NET 4.5,问题仍然存在。看来,如果我在ListView中重新选择一个项目,则会发生此错误,但我不知道为什么。
Alois Kraus 2014年

2
我使用WPF和.NET 4.5(不是4.5.1),此解决方案不再解决问题!:-)
马塞尔(Marcel)

1
SetDataObject在这里运作良好。SetText会引发异常,而其他则不会。谢谢!
法比亚诺

我发现此调用可以与DataGrid的多个剪贴板副本一起正常工作。
sailfish009 '18

77

这是WPF剪贴板处理程序中的错误。您需要在Application.DispatcherUnhandledException事件中处理未处理的异常。

将此属性添加到ApplicationApp.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;
}

5
Microsoft已在.NET 4.5中修复此问题,该代码现在与Windows Forms程序集中的代码相似。剪贴板例程具有重试计数和延迟时间,而不是一旦无法访问剪贴板便立即失败。
亚历克斯·威斯

2
我正在使用.NET 4.0并面临相同的问题。我不想触摸App.xaml文件。这个问题还有其他解决方案吗???
kushdilip

2
您为什么不想触摸app.xaml文件?您可以Application.DispatcherUnhandledException从其他地方订阅事件,但是在加载应用程序时应在App类中进行订阅。更多信息在这里
Alex Wiese

6
@kushdilip是的,当您控制设置剪贴板数据的方法时,该解决方案很好,但是当该方法不在的方法中时,该解决方案将无济于事代码中。例如WPF控件和第三方控件。如果您未在代码中的某个地方实现错误修复,那么您在.NET 4.0上仍然会受到它的影响。
Alex Wiese 2013年

6
SetText在4.5中未修复,但是使用SetDataObject似乎可以修复它-但是:我的程序将文件夹路径复制到剪贴板。它可以正常工作,但是如果我在VS调试器中运行该应用程序并打开.NET异常捕获功能,则会发生奇怪的事情。如果我将其粘贴到记事本中,则可以正常工作,但是如果我将其粘贴到Windows资源管理器的位置框中,则我的应用程序将引发COM异常“无效的FORMATETC结构(HRESULT的异常:0x80040064(DV_E_FORMATETC))”。这仅在调试时捕获到异常时才发生,因此必须在某个地方捕获并处理异常,但这仍然很奇怪。
戴夫

7

我也遇到了一个应用程序中的问题,在该应用程序中,随着用户细读ListBox,我将信息复制到剪贴板中。复制的信息与所选项目相关,为了方便起见,允许他们将其粘贴(其他信息)到其他应用程序中。有时我在某些用户的系统上会得到CLIPBRD_E_CANT_OPEN,而在其他用户的系统上却不会。

虽然我仍然无法解决争用,但是我仍然可以创建一些代码来查找引起争用的应用程序。我希望至少共享此代码,以希望对他人有所帮助。我将添加为找到罪魁祸首的Process对象而创建的using语句,属性和方法。您可以从“流程”项中获取流程的名称,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类),因为后者具有4参数SetDataObject()方法,其中包括重试计数和重试延迟(以毫秒为单位)。这至少消除了我的代码中的一些重试噪音

您的里程可能会有所不同...再加上可能还没有发现的副作用,因此,如果有人知道它们,请发表评论。无论如何,我希望这对某人有用。


2
+1用于使用System.Windows.Forms.Clipboard而不是System.Windows.Clipboard。谢谢
Dmase05

6

由于我安装了TeraCopy(Windows 7,64位),因此在WPF 4.0和4.5中也存在此问题。每个Clipboard.SetText()都失败,并出现System.Runtime.InteropServices.COMException。

我的第一个解决方案是卸载TeraCopy-它可以工作,但是我喜欢这个应用程序,因此我不得不寻找另一种解决方案来解决该问题。解决的办法是更换

Clipboard.SetText("my string");

Clipboard.SetDataObject("my string");

缺点是,当应用程序退出时,数据将从系统剪贴板中删除。
希林

1
如果将第二个参数设置为true,则在应用程序退出后仍保留数据,则不会。检查一下:docs.microsoft.com/de-de/dotnet/api/…–
pr0gg3r

2

我对RichTextBox有同样的问题。以下代码随机崩溃:

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

似乎最好使用System.Windows.Controls.RichTextBox.Copy


2

我在使用.NET 4.6.1从剪贴板检索XAML数据时遇到问题。

错误信息:

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

1

在将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

我终于找到了使用DataGrid实现的默认复制模式的解决方案。

先前的答案对我不起作用:

  • 使用Clipboard.SetDataObject(data); 委托Clipboard.SetText(data)->此解决方案不是我期望的,我不想自己实现复制功能。
  • 处理DispatcherUnhandledException:我不知道为什么,但是对我不起作用。未调用此事件附带的方法。

我终于找到了解决这个问题的新方法。您只需要清除剪贴板,然后再按“ Ctrl + C”即可。

因此,我在MainWindows.xaml文件资源中创建了一种新样式:

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

这种样式用于处理我的应用程序的所有数据网格中的“ previewKeyDown”。调用的方法如下:

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

之后,问题解决了。


0

为此目的有一个DataGrid事件/方法签名CopyingRowClipboardContentobject senderDataGridRowClipboardEventArgse),它比Clipboard.SetDataObject(data)或更可靠Clipboard.SetText(data)

这是使用方法。

在SelectionUnit模式下为名为myDataGrid的dataGrid设置“ FullRow”

<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

代码app.xaml

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

代码文件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();
            }
        }
    }
}

我已经处理了这段代码。


请通过比当前更好的方式来编辑它来改善您的答案。
kwoxer
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.