2012-10-07 271 views
54

Tengo una aplicación WPF que usa datagrid. La aplicación funcionó bien hasta que instalé Visual   Studio   2012 y Blend + SketchFlow preview. Ahora, cuando estoy tratando de copiar los datos de la red en el portapapeles con Ctrl +C (en cualquier aplicación), estoy recibiendo la siguiente excepción:OpenClipboard falló al copiar datos pegados de WPF DataGrid

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

Esto es muy molesto .

He visto algunas referencias a este problema here y en varias ubicaciones en la web, sin una solución real.

Puedo verificar que el portapapeles esté bloqueado cuando se produce esta excepción en Visual Studio, ya que no pude copiar y pegar el mensaje (tuve que escribirlo en un archivo). Además, el portapapeles no se bloqueó antes de que comenzara el proceso de copia.

¿Cómo solucionar este problema?

Respuesta

63

Es un error en el controlador del Portapapeles WPF. Debe controlar la excepción no controlada en el evento Application.DispatcherUnhandledException.

Añadir este atributo al elemento Application en su App.xaml

DispatcherUnhandledException="Application_DispatcherUnhandledException" 

Añadir este código a sus App.xaml.cs presentar

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 ha corregido esto en .NET 4.5, ahora el código es similar al que se encuentra en los ensamblados de Windows Forms. Las rutinas del portapapeles tienen una cuenta de reintento y un tiempo de demora en lugar de solo fallar tan pronto como no se pueda acceder al portapapeles. –

+2

Estoy usando .NET 4.0 y estoy enfrentando el mismo problema. No quiero tocar los archivos de App.xaml. ¿Hay alguna otra solución para este problema? – kushdilip

+2

¿Por qué no quieres tocar los archivos app.xaml? Puede suscribirse al evento 'Application.DispatcherUnhandledException' desde otro lugar, pero debe hacerlo en su clase de aplicación cuando se cargue su aplicación. Más información [aquí] (http://msdn.microsoft.com/en-us/library/system.windows.application.dispatcherunhandledexception.aspx) –

65

Estamos utilizando .NET 4.0. Tuvimos el mismo problema, pero después de cerrar la sesión del sistema, el código solía funcionar bien durante un tiempo.

Finalmente encontramos la alternativa.

Si desea copiar una cadena en el portapapeles,

string data = "Copy This" 

Hasta ahora yo estaba usando el siguiente método

Clipboard.SetText(data); 

Fue fallar una y otra vez. Entonces miré a otros métodos disponibles para configurar el texto en el portapapeles en Clipboard Class y trató lo siguiente:

Clipboard.SetDataObject(data); 

y funcionó :). Nunca tuve el problema de nuevo.

+3

Muy bien. Uso .NET 4.5 y el problema sigue ahí. Parece que si vuelvo a seleccionar un elemento en el ListView, este error ocurre pero no tengo idea de por qué. –

+2

Utilizo WPF y .NET 4.5 (no 4.5.1) y el problema desapareció con esta solución. :-) – Marcel

+1

SetDataObject funcionó bien aquí. SetText arroja una excepción, pero el otro no. ¡Gracias! – Fabiano

5

Yo también he tenido un problema en una aplicación donde copio información en el portapapeles cuando los usuarios examinan detenidamente un ListBox. La información que se copia está relacionada con el elemento seleccionado y les permite pegarla (dicha información) en otras aplicaciones para su comodidad. Ocasionalmente obtengo CLIPBRD_E_CANT_OPEN en algunos sistemas de usuario, pero no en otros.

Aunque todavía no he podido corregir la contención, pude crear algunos códigos para encontrar la aplicación que causa la contención. Me gustaría al menos compartir este código con la esperanza de que ayude a alguien.Voy a añadir el usando comunicado, atributos, y el método que he creado para encontrar el Proceso objeto del culpable. Desde el artículo Proceso, puede obtener el nombre del proceso, el PID, el título de la ventana principal (si tiene uno) y otros datos potencialmente útiles. Aquí están las líneas de código que agregué sin el código que lo llama. (NOTA: A continuación el fragmento de código que tengo Un dato más para compartir):

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

OTRA NOTA: Otra cosa que cambié y que simplificó un poco mi código fue convertir el uso de System.Windows.Clipboard-System.Windows.Forms.Clipboard (ver System.Windows.Forms.Clipboard Class) ya que este último tiene una de 4 parámetros SetDataObject() método que incluye un número de reintentos y un retraso de reintento en milisegundos. Esto al menos eliminó algunos de los ruido de mi código.

su kilometraje puede variar ... además de que puede haber efectos secundarios en este que todavía no ha tropezado, así que si alguien sabe de ellos por favor comentar. En cualquier caso, espero que esto sea útil para alguien.

+1

+1 para usar System.Windows.Forms.Clipboard en lugar de System.Windows.Clipboard. Gracias – Dmase05

2

Tuve el mismo problema con RichTextBox. El siguiente código se estrelló al azar:

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

Parece que es preferible utilizar System.Windows.Controls.RichTextBox.Copy

5

También tuve este problema en WPF 4.0 y 4.5 desde que he instalado TeraCopy (Windows   7, 64 bits). Cada Clipboard.SetText() falló con System.Runtime.InteropServices.COMException.

Mi primera solución era desinstalar TeraCopy - funcionó, pero me encanta esta aplicación, así que tuve que buscar otra solución para resolver esta cuestión. La solución fue reemplazar

Clipboard.SetText("my string"); 

con

Clipboard.SetDataObject("my string"); 
1

que tenía el mismo problema en la copia de celdas de Excel en el portapapeles y la obtención de datos desde el portapapeles como una cadena HTML.

Puede utilizar (mientras-try-catch) como en el código de abajo.

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

Además, se puede tener un contador en el while si el bucle es más de 10 veces o más, se producen una excepción. Probé que su contador máximo es uno y en un trabajo de portapapeles de bucle de tiempo.

2

que tenía un problema para recuperar datos XAML desde el portapapeles con .NET 4.6.1.

mensaje

error:

OpenClipboard Error (Excepción de HRESULT: 0x800401D0 (CLIPBRD_E_CANT_OPEN)))

Lo resuelto de la siguiente manera:

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

Hay un evento DataGrid/la firma del método para este propósito exacto CopyingRowClipboardContent (remitente del objeto, DataGridRowClipboardEventArgs e) y es más confiable que Clipboard.SetDat aObjeto (datos) o Clipboard.SetText (datos).

Así es como usarlo.

Set "FullRow" en el modo de SelectionUnit para dataGrid llama myDataGrid

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

Tenemos un método, myDataGrid_CopyingRowClipboardContent, que es llamada para cada fila en la cuadrícula de datos para copiar su contenido en el portapapeles. Por ejemplo, para una cuadrícula de datos con siete filas esto se llama siete veces.

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

Adición de mi respuesta desde mencionada pregunta SO de referencia -

Encontramos este de Andrew Smith en http://blogs.infragistics.com/forums/t/35379.aspx -

Técnicamente sólo el 1 proceso puede abrir el portapapeles de modo que si otro proceso lo tiene abrió las solicitudes posteriores fallarán hasta que el primero libere el portapapeles. Esta fue una especie de manejarse en los WinForms clase Clipboard donde podría volver a intentar el set con un retraso de entre cada uno de tratar, pero la clase portapapeles WPF no hace esto por lo que si se produce un error en el primer show se produce la excepción. Incluso entonces probablemente deberíamos detectar la excepción y elevar el error de operación portapapeles si todavía falla.

Lo mismo se explica y algunas maneras de solucionarlo se mencionan en este italiano del blog -

WPF DataGrid Clipboard BUG (?) & Work (Cached translation) (Orignal Link)

Siguiendo hilo del foro de MSDN sugiere que esto podría ser la máquina problema específico -

CLIPBRD_E_CANT_OPEN exception when copying to clipboard from a DataGrid