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

问题描述 投票:63回答:8

我有一个使用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()

这真的很烦人。

我已经看到了一些对这个问题的引用here以及网络上的各个位置,没有真正的解决方案。

我可以验证在Visual Studio中引发此异常时剪贴板是否已锁定,因为我无法复制粘贴消息(必须将其写入文件)。此外,在复制过程开始之前未锁定剪贴板。

如何解决这个问题呢?

c# wpf datagrid clipboard copy-paste
8个回答
85
投票

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

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

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

string data = "Copy This"

直到现在我使用以下方法

Clipboard.SetText(data);

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

Clipboard.SetDataObject(data);

它工作:)。我再也没遇到过这个问题。


70
投票

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

将此属性添加到App.xaml中的Application元素

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

6
投票

我也在应用程序中遇到问题,我将信息复制到剪贴板中,因为用户仔细阅读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),因为后者有一个4参数的SetDataObject()方法,其中包括重试计数和重试延迟(以毫秒为单位)。这至少从我的代码中删除了一些重试噪声。

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


5
投票

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

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

Clipboard.SetText("my string");

Clipboard.SetDataObject("my string");

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

此外,如果循环超过10次或更多,您可以在while中有一个计数器,异常发生。我测试它的最大计数器是一次性和一次性循环剪贴板工作。


0
投票

这个确切的目的有一个DataGrid事件/方法签名CopyingRowClipboardContentobject senderDataGridRowClipboardEventArgs e),并且比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;
}

-1
投票

这对我也有用。

更改班级:

Clipboard.SetText("my string");

至:

Clipboard.SetDataObject("my string");
© www.soinside.com 2019 - 2024. All rights reserved.