c# - OpenClipboard falló al copiar los datos pegados de WPF DataGrid
copy-paste (9)
Agregando mi respuesta de la pregunta SO mencionada para referencia -
Encontré esto de Andrew Smith en http://blogs.infragistics.com/forums/t/35379.aspx -
Técnicamente, solo 1 proceso puede abrir el portapapeles, por lo que si se abre otro proceso, las solicitudes subsiguientes fallarán hasta que el primero libere el portapapeles. Esto se manejó en la clase del Portapapeles de WinForms donde volvería a intentar el conjunto con un retraso entre cada intento, pero la clase del portapapeles de WPF no hace esto, por lo que si falla en el primer espectáculo, se produce la excepción. Incluso entonces probablemente deberíamos detectar la excepción y aumentar el error de operación del portapapeles si aún falla.
Se explica lo mismo y se mencionan algunas formas de solucionarlo en este blog italiano:
WPF DataGrid Clipboard BUG (?) & Work (Traducción en caché) ( Enlace Orignal )
Seguir el hilo del foro de MSDN sugiere que esto podría ser un problema específico de la máquina:
Excepción CLIPBRD_E_CANT_OPEN al copiar al portapapeles desde un DataGrid
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 intento copiar los datos de la grilla en el portapapeles con Ctrl + C (en cualquier aplicación), recibo la siguiente excepción:
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 realmente molesto
He visto algunas referencias a este problema here y en varios lugares 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 resolver este problema?
Es un error en el controlador del portapapeles WPF. Debe controlar la excepción no controlada en el evento Application.DispatcherUnhandledException.
Agregue este atributo al elemento Application
en su App.xaml
DispatcherUnhandledException="Application_DispatcherUnhandledException"
Agregue este código a su archivo 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;
}
Estamos usando .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 quieres copiar una cadena al portapapeles,
string data = "Copy This"
Hasta ahora estaba usando el siguiente método
Clipboard.SetText(data);
Estaba fallando una y otra vez. Luego miré otros métodos disponibles para configurar texto en el portapapeles en Clipboard Class y probé lo siguiente:
Clipboard.SetDataObject(data);
Y funcionó :). Nunca tuve el problema de nuevo.
Hay una firma de evento / método DataGrid para este propósito CopyingRowClipboardContent (remitente de objetos, DataGridRowClipboardEventArgs e) y es más confiable que Clipboard.SetDataObject (data) o Clipboard.SetText (data).
He aquí cómo usarlo.
Establezca "FullRow" en el modo SelectionUnit para dataGrid llamado myDataGrid
<DataGrid x:Name="myDataGrid" SelectionUnit="FullRow"></DataGrid>
Tenemos un método, myDataGrid_CopyingRowClipboardContent, que recibe una llamada para cada fila en el dataGrid para copiar su contenido al 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;
}
También tuve este problema en WPF 4.0 y 4.5 desde que instalé TeraCopy (Windows 7, 64 bits). Cada Clipboard.SetText () falló con System.Runtime.InteropServices.COMException.
Mi primera solución fue desinstalar TeraCopy, funcionó, pero me encanta esta aplicación, así que tuve que buscar otra solución para resolver ese problema. La solución fue reemplazar
Clipboard.SetText("my string");
con
Clipboard.SetDataObject("my string");
Tuve el mismo problema al copiar las celdas de Excel al portapapeles y obtener datos del portapapeles como una cadena HTML.
Puedes usar (while-try-catch) como en el siguiente código.
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, puede tener un contador en el while
si el ciclo es más de 10 veces o más, se produce una excepción. Probé que su contador máximo es uno y en un trabajo de portapapeles de bucle de tiempo.
Tuve el mismo problema con RichTextBox. El siguiente código se colgó al azar:
TextRange tr = new TextRange(rich.Document.ContentStart, rich.Document.ContentEnd);
System.Windows.Clipboard.SetDataObject(tr.Text);
Parece que es preferible usar System.Windows.Controls.RichTextBox.Copy
Tuve un problema para recuperar datos XAML del portapapeles con .NET 4.6.1.
Mensaje de error:
Fallo en OpenClipboard (Excepción de HRESULT: 0x800401D0 (CLIPBRD_E_CANT_OPEN)))
Lo resolví 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 { }
}
Yo también he tenido un problema en una aplicación donde copio información en el portapapeles a medida que 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.
Si bien aún no he podido solucionar la disputa, pude crear algún código 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. Agregaré la declaración de uso , los atributos y el método que creé para encontrar el objeto de proceso del culpable. Desde el elemento 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: debajo del fragmento de código tengo un tidbit 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 simplifiqué un poco mi código fue convertir de System.Windows.Clipboard a System.Windows.Forms.Clipboard (ver System.Windows.Forms.Clipboard Class ) porque este último tiene un valor de 4. parámetro del método SetDataObject () que incluye un recuento de reintento y un retraso de reintento en milisegundos. Esto al menos eliminó parte del ruido de reintento de mi código.
Su millaje puede variar ... además, puede haber efectos secundarios que aún no me he encontrado, así que si alguien los conoce, por favor coméntelos. En cualquier caso, espero que esto sea útil para alguien.