Pregunta

¿Por qué el siguiente código a veces provoca una excepción con el contenido "CLIPBRD_E_CANT_OPEN":

Clipboard.SetText(str);

Esto suele ocurrir la primera vez que se utiliza el Portapapeles en la aplicación y no después.

¿Fue útil?

Solución

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

Para configurar datos en el portapapeles, debe abrelo primero.Sólo un proceso puede tener el portapapeles abierto a la vez.Entonces, cuando verificas, si otro proceso tiene el portapapeles abierto por alguna razon, su intento de abrirlo fallará.

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

Sin embargo, es importante darse cuenta de que esto no es específico de Terminal Services:puede pasar 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 jugar con el portapapeles en respuesta a la entrada del usuario, esto generalmente no presenta un problema.

Otros consejos

Esto se debe a un error/función en el portapapeles de Terminal Services (y posiblemente otras cosas) y la implementación .NET del portapapeles.Un retraso en la apertura del portapapeles provoca el error, que suele pasar en unos pocos milisegundos.

La solución es intentarlo 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 aún existe.Como se mencionó anteriormente, esta excepción ocurre cuando otro proceso bloquea el portapapeles del sistema.Desafortunadamente, existen muchas herramientas de recorte, programas para capturas de pantalla y herramientas de copia de archivos que pueden bloquear el portapapeles de Windows.Entonces obtendrás la excepción cada vez que intentes 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);

En realidad, podría haber otro problema entre manos.La llamada del marco (tanto WPF como 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 en este caso siempre se llama a SetDataObject con verdadero.

Internamente, eso desencadena dos llamadas a la API de Win32, una para configurar los datos y otra para eliminarlos de su aplicación para que estén disponibles 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 buscar los datos y la segunda llamada para vaciar fallará.

No he encontrado una buena solución, excepto escribir mi propia clase de portapapeles que usa la API win32 directa o llamar a setDataObject directamente con false para conservar los datos después de 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 contenedora 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 vinculo a 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.Obtuve un error en OpenClipboard (excepción de HRESULT:0x800401D0 (CLIPBRD_E_CANT_OPEN)).

yo suelo

ApplicationCommands.Copy.Execute(null, myDataGrid);

La solución es borrar el portapapeles primero.

Clipboard.Clear();
ApplicationCommands.Copy.Execute(null, myDataGrid);
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top