c# - una - Cómo manejar el portapapeles bloqueado y otras rarezas.
recuperar portapapeles windows 7 (8)
En el transcurso de las últimas dos horas he estado rastreando un error bastante específico que se produce porque otra aplicación tiene el portapapeles abierto. Esencialmente, ya que el Portapapeles es un recurso compartido (según "¿Por qué no funciona mi portapapeles compartido?" ) Y usted intenta ejecutar
Clipboard.SetText(string)
o
Clipboard.Clear().
Se lanza la siguiente excepción:
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 volver a intentarlo 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 un retraso de 100 ms.
Hay una cosa final que ha señalado el usuario final, que 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é esto puede ser.
Mi solución actual al problema es ignorar silenciosamente la excepción ... ¿es esta realmente la mejor manera?
Así que en realidad he encontrado mi propia solución. Sé que llego 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, y también he intentado forzarla en el portapapeles con reintentos.
Esencialmente aquí está el flujo. Digamos que desea colocar la palabra Portapapeles en el portapapeles, en cualquier parte de su código (asumiendo que esta función está definida).
- Función de llamada ClipboardPaste ("Portapapeles");
- A continuación, se borrará el portapapeles.
- Entonces "intentará" poner tu cadena en el portapapeles.
- Primero espera medio segundo (500 milisegundos).
- Despeja el portapapeles de nuevo
- Luego trata de poner la cadena en el portapapeles usando SetDataObject
- SetDataObject si falla volverá a intentarlo hasta 5 veces, con un retraso de 250 milisegundos entre cada reintento.
- Si el intento inicial falla, detecta la excepción, el bloqueo y luego lo intenta de nuevo.
Sí, esto tiene una falla si sabe que su portapapeles siempre tendrá una excepción sin importar qué (bucle infinito). Sin embargo, todavía no he encontrado un bucle infinito con este método. La otra falla es que puede tardar un par de segundos (esencialmente ralentizando sus aplicaciones) antes de que funcione, mientras que intentarlo puede congelar su aplicación, una vez que tenga éxito, la aplicación continuará de todos modos.
Como el portapapeles es compartido por todas las aplicaciones de la interfaz de usuario, se encontrará con esto de vez en cuando, obviamente, no desea que su aplicación se bloquee si no se escribe en el portapapeles, por lo que el manejo adecuado de ExternalException es razonable. y error al usuario si falla la llamada a setobjectdata para escribir en el portapapeles.
Una sugerencia sería usar (a través de p / invocar) user32!GetOpenClipboardWindow
para ver si otra aplicación tiene el portapapeles abierto, devolverá el HWND de la ventana que tiene el portapapeles abierto, o IntPtr.Zero
si ninguna aplicación lo tiene abierto, podría girar en el valor hasta que sea IntPtr.Zero
durante un período de tiempo específico.
Esto es un poco horrible ... pero resolvió mi problema.
Vuelva a intentar borrar () después de un retraso.
Más información en this blog.
Hacer un Clipboard.Clear()
antes de que Clipboard.SetDataObject(pasteString, true)
parece hacer el truco.
La sugerencia anterior de establecer retryTimes
y retryDelay
no funcionó para mí y, en cualquier caso, los valores predeterminados son retryTimes = 10
y retryDelay = 100ms
Haciendo uso del código de Jeff Roe (Código de Jeff )
[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
}
Eres capaz de manejar el error de una manera bastante práctica.
He logrado reducir la frecuencia del error haciendo uso de System.Windows.Forms.Clipboard
lugar de System.Windows.Clipboard
.
Hago hincapié en que esto no soluciona el problema pero ha reducido la ocurrencia de mi aplicación.
Lamento haber resucitado una pregunta anterior, pero otra solución sería utilizar el Clipboard.SetDataObject
lugar del Clipboard.SetText
.
De acuerdo con este artículo de msdn, este método tiene dos parámetros, retryTimes y retryDelay , que puede usar de la siguiente 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
Me encontré con este error hoy. Decidí manejarlo informándole al usuario sobre la aplicación que podría comportarse mal. Para ello, puedes 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 del problema era "skype_plugin_core_proxy_window". Busqué información sobre eso, y me sorprendió que produjera solo un golpe, y eso fue en ruso. Así que estoy agregando esta respuesta, tanto para dar otro golpe a esa cadena, como para proporcionar más ayuda para sacar a la luz las aplicaciones que potencialmente pueden comportarse mal.
Solo llama a esto primero
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern IntPtr CloseClipboard();
Noté que si estás en medio de una operación de pegado (mensaje WM_PASTE), incluso durante el evento TextChanged, el portapapeles permanece bloqueado por la ventana (el TextBox) que recibe el evento. Entonces, si solo llama a ese método "CloseClipboard" dentro del controlador de eventos, puede llamar a los métodos administrados Clipboard.Clear y Clipboard.SetText sin ningún problema o retraso.