CLIPBRD_E_CANT_OPEN error al configurar el portapapeles desde .NET

¿Por qué el siguiente código a veces causa una excepción con el contenido “CLIPBRD_E_CANT_OPEN”:

Clipboard.SetText(str); 

Esto generalmente ocurre la primera vez que se utiliza el Portapapeles en la aplicación y no después de eso.

En realidad, creo que esto es culpa de la API de Win32 .

Para establecer datos en el portapapeles, primero debe abrirlos . Solo un proceso puede tener el portapapeles abierto a la vez. Por lo tanto, cuando compruebe si otro proceso tiene el portapapeles abierto por algún motivo , su bash de abrirlo fallará.

Da la casualidad de que Terminal Services realiza un seguimiento del portapapeles, y en versiones anteriores de Windows (previas a la Vista), debe abrir el portapapeles para ver qué hay dentro … que termina bloqueándole. La única solución es esperar hasta que Terminal Services cierre el portapapeles y vuelva a intentarlo.

Sin embargo, es importante darse cuenta de que esto no es específico de los Servicios de Terminal Server: puede suceder con cualquier cosa. Trabajar con el portapapeles en Win32 es una condición de carrera gigante. Pero, dado que por diseño solo se supone que debes usar el portapapeles en respuesta a la entrada del usuario, esto generalmente no presenta ningún problema.

Esto es causado por un error / característica en el portapapeles de Servicios de Terminal Server (y otras cosas posibles) y la implementación de .NET del portapapeles. Un retraso en la apertura del portapapeles provoca el error, que generalmente pasa dentro de unos pocos milisegundos.

La solución es intentar varias veces dentro de un bucle y dormir en el medio.

 for (int i = 0; i < 10; i++) { try { Clipboard.SetText(str); return; } catch { } System.Threading.Thread.Sleep(10); } 

Sé que esta pregunta es antigua, pero el problema todavía existe. Como se mencionó anteriormente, esta excepción se produce cuando el portapapeles del sistema está bloqueado por otro proceso. Desafortunadamente, hay muchas herramientas de recorte, progtwigs para capturas de pantalla y herramientas de copia de archivos que pueden bloquear el portapapeles de Windows. Por lo tanto, obtendrá la excepción cada vez que intente usar Clipboard.SetText(str) cuando dicha herramienta esté instalada en su PC.

Solución:

nunca usar

 Clipboard.SetText(str); 

usar en su lugar

 Clipboard.SetDataObject(str); 

De hecho, podría haber otro problema entre manos. La llamada al framework (tanto el WPF como los sabores de winform) a algo como esto (el código es del reflector):

 private static void SetDataInternal(string format, object data) { bool flag; if (IsDataFormatAutoConvert(format)) { flag = true; } else { flag = false; } IDataObject obj2 = new DataObject(); obj2.SetData(format, data, flag); SetDataObject(obj2, true); } 

Tenga en cuenta que SetDataObject siempre se llama con true en este caso.

Internamente, esto desencadena dos llamadas a la API de Win32, una para establecer los datos y otra para eliminarla de su aplicación para que esté disponible después de que se cierre la aplicación.

He visto varias aplicaciones (algunos complementos de Chrome y un administrador de descargas) que escuchan el evento del portapapeles. Tan pronto como llegue la primera llamada, la aplicación abrirá el portapapeles para examinar los datos, y fallará la segunda llamada para vaciar.

No he encontrado una buena solución, excepto para escribir mi propia clase de portapapeles que usa la API win32 directa o para llamar directamente a setDataObject con false para mantener los datos una vez que se cierra la aplicación.

Resolví este problema para mi propia aplicación usando funciones nativas de Win32: OpenClipboard (), CloseClipboard () y SetClipboardData ().

Debajo de la clase de envoltura que hice. ¿Alguien podría por favor revisarlo y decir si es correcto o no ? Especialmente cuando el código administrado se ejecuta como una aplicación x64 (uso cualquier CPU en las opciones del proyecto). ¿Qué sucede cuando me enlace a las bibliotecas x86 desde la aplicación x64?

¡Gracias!

Aquí está el código:

 public static class ClipboardNative { [DllImport("user32.dll")] private static extern bool OpenClipboard(IntPtr hWndNewOwner); [DllImport("user32.dll")] private static extern bool CloseClipboard(); [DllImport("user32.dll")] private static extern bool SetClipboardData(uint uFormat, IntPtr data); private const uint CF_UNICODETEXT = 13; public static bool CopyTextToClipboard(string text) { if (!OpenClipboard(IntPtr.Zero)){ return false; } var global = Marshal.StringToHGlobalUni(text); SetClipboardData(CF_UNICODETEXT, global); CloseClipboard(); //------------------------------------------- // Not sure, but it looks like we do not need // to free HGLOBAL because Clipboard is now // responsible for the copied data. (?) // // Otherwise the second call will crash // the app with a Win32 exception // inside OpenClipboard() function //------------------------------------------- // Marshal.FreeHGlobal(global); return true; } } 

Esto me sucede en mi aplicación WPF. Tengo OpenClipboard Failed (Excepción de HRESULT: 0x800401D0 (CLIPBRD_E_CANT_OPEN)).

yo suelo

 ApplicationCommands.Copy.Execute(null, myDataGrid); 

La solución es borrar primero el portapapeles

 Clipboard.Clear(); ApplicationCommands.Copy.Execute(null, myDataGrid);