2009-05-30 7 views
45

En el transcurso de las últimas dos horas he estado rastreando un error bastante específico porque eso ocurre porque otra aplicación tiene el portapapeles abierto. Esencialmente como el portapapeles es un recurso compartido (según "Why does my shared clipboard not work?") y se intenta ejecutarCómo manejar portapapeles bloqueados y otras rarezas

Clipboard.SetText(string) 

o

Clipboard.Clear(). 

La excepción siguiente:

 
System.Runtime.InteropServices.ExternalException: Requested Clipboard operation did not succeed. 
    at System.Windows.Forms.Clipboard.ThrowIfFailed(Int32 hr) 
    at System.Windows.Forms.Clipboard.SetDataObject(Object data, Boolean copy, Int32 retryTimes, Int32 retryDelay) 
    at System.Windows.Forms.Clipboard.SetText(String text, TextDataFormat format) 
    at System.Windows.Forms.Clipboard.SetText(String text) 

Mi solución inicial fue para volver a intentar después de una breve pausa, hasta que me di cuenta de que Clipboard.SetDataObject tiene campos para el número de veces y la duración del retraso, el comportamiento predeterminado de .NET es intentar 10 veces con retraso de 100 ms.

Hay una última cosa que el usuario final ha observado, es decir, a pesar de que la operación de copiar al portapapeles aún funciona, no he podido encontrar más información sobre por qué puede ser así.

Mi solución actual al problema es simplemente ignorar silenciosamente la excepción ... ¿es esta la mejor manera?

Respuesta

23

Como el portapapeles es compartido por todas las aplicaciones de interfaz de usuario, se encontrará con esto de vez en cuando, obviamente no desea que la aplicación se bloquee si no puede escribir en el portapapeles, por lo que manejar ExcepciónExcelente es razonable, Sugeriría presentar y error al usuario si falla la llamada a los datos de setobject para escribir en el portapapeles.

Una sugerencia sería utilizar (a través de P/Invoke) user32!GetOpenClipboardWindow para ver si otra aplicación tiene el portapapeles abierto, se devolverá el HWND de la ventana que tiene el portapapeles abierta, o IntPtr.Zero si no hay ninguna aplicación tenía abierta, podría girar el valor hasta que sea IntPtr.Zero durante un período de tiempo específico.

+0

He leído acerca de GetOpenClipboardWindow, parece que esa es la mejor solución para los problemas de acceso al portapapeles. Gracias por su respuesta. –

+0

Cómo obtener el proceso bloqueando el Portapapeles - ver: http://stackoverflow.com/questions/6583642/determine-which-process-is-locking-the-clipboard – toong

+1

Simplemente cierre el portapapeles primero. Ver mi respuesta – Triynko

38

Lamento resucitar una pregunta anterior, pero otra solución sería usar Clipboard.SetDataObject en lugar de Clipboard.SetText.

Según this msdn article este método tiene dos parámetros - retryTimes y retryDelay - que se puede utilizar de esta manera:

Clipboard.SetDataObject(
    "some text", //text to store in clipboard 
    false,  //do not keep after our app exits 
    5,   //retry 5 times 
    200);  //200ms delay between retries 
+0

No, en absoluto, buen lugar. –

+0

El problema que estoy enfrentando es que incluso con números altos para retryTimes y retryDelay (20/500), tener Firefox abierto todavía hace que esto falle. Tan extraño ... – aaaidan

+0

buena respuesta, me ayudó mucho – apomene

0

Esto es poco cutre .. pero resolvió mi problema.

Vuelva a intentar la limpieza() después de un retraso.

Más información @this blog.

10

Me encontré con este error hoy. Decidí manejarlo al decirle al usuario sobre la aplicación potencialmente mal. Para ello, se puede hacer algo como esto:

[System.Runtime.InteropServices.DllImport("user32.dll")] 
static extern IntPtr GetOpenClipboardWindow(); 

[System.Runtime.InteropServices.DllImport("user32.dll")] 
static extern int GetWindowText(int hwnd, StringBuilder text, int count); 

private void btnCopy_Click(object sender, EventArgs e) 
{ 
    try 
    { 
     Clipboard.Clear(); 
     Clipboard.SetText(textBox1.Text); 
    } 
    catch (Exception ex) 
    { 
     string msg = ex.Message; 
     msg += Environment.NewLine; 
     msg += Environment.NewLine; 
     msg += "The problem:"; 
     msg += Environment.NewLine; 
     msg += getOpenClipboardWindowText(); 
     MessageBox.Show(msg); 
    } 
} 

private string getOpenClipboardWindowText() 
{ 
    IntPtr hwnd = GetOpenClipboardWindow(); 
    StringBuilder sb = new StringBuilder(501); 
    GetWindowText(hwnd.ToInt32(), sb, 500); 
    return sb.ToString(); 
    // example: 
    // skype_plugin_core_proxy_window: 02490E80 
} 

Para mí, el título de la ventana era un problema "skype_plugin_core_proxy_window". Busqué información sobre eso, y me sorprendió que solo produjera un golpe, y eso fue en ruso. Así que estoy agregando esta respuesta, tanto para dar otro golpe para esa cadena, como para proporcionar más ayuda para sacar a la luz las aplicaciones potencialmente malas.

+0

Se mencionó en otros temas similares que existen versiones de Skype que se comportan bastante mal cuando se trata del Portapapeles (compartido) – cacau

-3

Así que de hecho he encontrado mi propia solución. Sé que estoy un poco tarde, SIN EMBARGO, esto parece estar funcionando para mí.

// This allows the clipboard to have something copied to it. 
    public static void ClipboardPaste(String pasteString) 
    { 
     // This clears the clipboard 
     Clipboard.Clear(); 

     // This is a "Try" of the statement inside {}, if it fails it goes to "Catch" 
     // If it "Catches" an exception. Then the function will retry itself. 
     try 
     { 
      // This, per some suggestions I found is a half second second wait before another 
      // clipboard clear and before setting the clipboard 
      System.Threading.Thread.Sleep(500); 
      Clipboard.Clear(); 
      // This is, instead of using SetText another method to put something into 
      // the clipboard, it includes a retry/fail set up with a delay 
      // It retries 5 times with 250 milliseconds (0.25 second) between each retry. 
      Clipboard.SetDataObject(pasteString, false, 5, 250); 
     } 
     catch (Exception) 
     { 
      ClipboardPaste(pasteString); 
     } 
    } 

Esto es obviamente C#, sin embargo, estos métodos están expuestos a todos los estudios visuales. Obviamente, he creado una función de bucle e intenté forzarla en el portapapeles con reintentos.

Esencialmente aquí está el flujo. Digamos que quiere colocar la palabra Portapapeles en el portapapeles, en cualquier parte de su código (suponiendo que se define esta función).

  1. Función de llamada ClipboardPaste ("Portapapeles");
  2. Luego borrará el portapapeles
  3. Luego "intentará" poner su cadena en el portapapeles.
  4. Primero espera medio segundo (500 milisegundos)
  5. Borra el portapapeles de nuevo
  6. Entonces se trata de poner la cadena en el portapapeles utilizando SetDataObject
  7. SetDataObject si no lo volverá a intentar hasta 5 veces, con un retraso de 250 milisegundos entre cada reintento.
  8. Si el intento inicial falla, atrapa la excepción, el bloqueo, luego lo intenta todo de nuevo.

Sí, esto tiene un defecto si sabes que tu portapapeles siempre tendrá una excepción sin importar qué (bucle infinito). Sin embargo, todavía no he encontrado un ciclo infinito con este método. El otro defecto es que puede tomar un par de segundos (esencialmente ralentizando sus aplicaciones) antes de que funcione, mientras que está intentando congelar su aplicación, una vez que tenga éxito la aplicación continuará de todos modos.

+3

Si la configuración del portapapeles siempre causa una excepción, no obtendrá un ciclo infinito, sino un excepción de desbordamiento de pila, porque estás recurriendo. – dsolimano

1

Haciendo un Clipboard.Clear() antes Clipboard.SetDataObject(pasteString, true) parece hacer el truco.

La sugerencia anterior de establecer retryTimes y retryDelay no funcionó para mí y en todo caso los valores por defecto son retryTimes = 10 y retryDelay = 100ms

+1

Clipboard.Clear provoca el error también. – Triynko

0

Sólo tiene que llamar primero esto,

[System.Runtime.InteropServices.DllImport("user32.dll")] 
static extern IntPtr CloseClipboard(); 

me di cuenta de que si' re en el medio de una operación de pegar (mensaje WM_PASTE), incluso durante el evento TextChanged, el portapapeles permanece bloqueado por la ventana (el TextBox) que recibe el evento. Entonces, si llama a ese método "CloseClipboard" dentro del controlador de eventos, puede llamar a los métodos de Clipboard.Clear y Clipboard.SetText administrados sin problemas o retrasos.

0

Al hacer uso de código de Jeff Roe (Jeff's Code)

[System.Runtime.InteropServices.DllImport("user32.dll")] 
static extern IntPtr GetOpenClipboardWindow(); 

[System.Runtime.InteropServices.DllImport("user32.dll")] 
static extern int GetWindowText(int hwnd, StringBuilder text, int count); 

private void btnCopy_Click(object sender, EventArgs e) 
{ 
    try 
    { 
     Clipboard.Clear(); 
     Clipboard.SetText(textBox1.Text); 
    } 
    catch (Exception ex) 
    { 
     string msg = ex.Message; 
     msg += Environment.NewLine; 
     msg += Environment.NewLine; 
     msg += "The problem:"; 
     msg += Environment.NewLine; 
     msg += getOpenClipboardWindowText(); 
     MessageBox.Show(msg); 
    } 
} 

private string getOpenClipboardWindowText() 
{ 
    IntPtr hwnd = GetOpenClipboardWindow(); 
    StringBuilder sb = new StringBuilder(501); 
    GetWindowText(hwnd.ToInt32(), sb, 500); 
    return sb.ToString(); 
    // example: 
    // skype_plugin_core_proxy_window: 02490E80 
} 

que son capaces de manejar el error de una manera bastante práctico.

He logrado reducir la frecuencia del error haciendo uso de System.Windows.Forms.Clipboard en lugar de System.Windows.Clipboard.

Insisto en que esto no soluciona el problema, pero ha reducido la incidencia de mi aplicación.

Cuestiones relacionadas