85

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

如何解决这个问题呢?

4

11 回答 11

109

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

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

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

string data = "Copy This"

到目前为止,我一直在使用以下方法

Clipboard.SetText(data);

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

Clipboard.SetDataObject(data);

它奏效了:)。我再也没有遇到过这个问题。

于 2013-07-16T13:57:18.887 回答
81

这是 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;
}
于 2012-11-23T04:39:58.927 回答
8

我也一直在应用程序中遇到问题,我在用户细读 ListBox 时将信息复制到剪贴板。复制的信息与所选项目相关,并且允许他们将其(所述信息)粘贴到其他应用程序中以方便使用。有时我会在某些用户的系统上获得 CLIPBRD_E_CANT_OPEN,但在其他系统上却没有。

虽然我仍然无法修复争用,但我能够创建一些代码来查找导致争用的应用程序。我想至少分享这段代码,希望它对某人有所帮助。我将添加我创建的using语句、属性和方法来查找罪魁祸首的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 类),因为后者有 4-参数SetDataObject()方法,其中包括重试计数和以毫秒为单位的重试延迟。这至少从我的代码中消除了一些重试噪音。

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

于 2014-01-23T14:40:37.930 回答
7

自从我安装了TeraCopy(Windows 7、64 位)以来,我在 WPF 4.0 和 4.5 中也遇到了这个问题。每个 Clipboard.SetText() 都因 System.Runtime.InteropServices.COMException 而失败。

我的第一个解决方案是卸载 TeraCopy - 它有效,但我喜欢这个应用程序,所以我不得不寻找另一个解决方案来解决这个问题。解决方案是更换

Clipboard.SetText("my string");

Clipboard.SetDataObject("my string");
于 2014-11-12T01:22:49.530 回答
2

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

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

似乎首选使用System.Windows.Controls.RichTextBox.Copy

于 2014-07-05T06:31:39.560 回答
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 { }
}
于 2016-05-04T11:47:47.283 回答
2

我终于找到了使用 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();
    }
}

之后,问题就解决了。

于 2019-08-01T14:53:49.793 回答
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 次或更多,您可以有一个计数器,发生异常。我测试它的最大计数器是一次性循环剪贴板工作。

于 2014-11-12T05:20:06.150 回答
1

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

我已经处理了这段代码。

于 2019-09-17T12:44:46.950 回答
0

有一个用于这个确切目的的 DataGrid 事件/方法签名CopyingRowClipboardContent( object sender, DataGridRowClipboardEventArgse) 并且比Clipboard.SetDataObject(data)or更可靠Clipboard.SetText(data)

以下是如何使用它。

在名为 myDataGrid 的 dataGrid 的 SelectionUnit 模式下设置“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;
}
于 2017-09-16T14:44:34.080 回答
0

我为 WPF Datagrid Export to Excel(CSV) 编写了一个扩展方法:

如果“MyDatagrid”是您的数据网格的名称,请使用一行代码调用自己的用户控件。

MyDatagrid.ExportToExcel(this);

并将此方法添加到您的扩展静态类

#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

最后别忘了

using Microsoft.Win32;

在扩展类和设置

ClipboardCopyMode="IncludeHeader"

为您的数据网格。

于 2020-11-29T17:06:02.040 回答