web-dev-qa-db-fra.com

Comment gérer un presse-papiers bloqué et autres bizarreries

Au cours des dernières heures, j'ai détecté un bogue assez spécifique qui se produit car une autre application a le presse-papiers ouvert. Essentiellement, car le presse-papiers est une ressource partagée (selon "Pourquoi mon presse-papiers partagé ne fonctionne pas?" ) et vous essayez d'exécuter

Clipboard.SetText(string)

ou

Clipboard.Clear().

L'exception suivante est levée:

 System.Runtime.InteropServices.ExternalException: l'opération de Presse-papiers demandée n'a pas réussi. 
 sur System.Windows.Forms.Clipboard.ThrowIfFailed (Int32 hr) 
 sur System.Windows.Forms.Clipboard.SetDataObject (Object data, Boolean copy, Int32 retryTimes, Int32 retryDelay) 
 sur System.Windows.Forms.Clipboard.SetText (texte de chaîne, format TextDataFormat) 
 sur System.Windows.Forms.Clipboard.SetText (texte de chaîne) 

Ma solution initiale a été de réessayer après une courte pause, jusqu'à ce que je réalise que Clipboard.SetDataObject a des champs pour le nombre de fois et la durée du délai. Le comportement par défaut de .NET consiste à essayer 10 fois avec un délai de 100 ms.

Il y a une dernière chose qui a été notée par l'utilisateur final. Autrement dit, malgré l'exception levée, l'opération de copie dans le presse-papiers fonctionne toujours. Je n'ai pas pu trouver plus d'informations sur la raison pour laquelle cela pourrait être.

Ma solution actuelle au problème consiste simplement à ignorer silencieusement l'exception ... est-ce vraiment la meilleure façon?

50
Richard Slater

Comme le presse-papiers est partagé par toutes les applications d'interface utilisateur, vous y rencontrerez de temps en temps. De toute évidence, vous ne voulez pas que votre application plante si elle ne parvient pas à écrire dans le presse-papiers, il est donc raisonnable de gérer gracieusement ExternalException. Je suggère de présenter une erreur à l'utilisateur si l'appel SetObjectData pour écrire dans le presse-papiers échoue.

Une suggestion serait d'utiliser (via P/Invoke ) user32!GetOpenClipboardWindow pour voir si une autre application a le presse-papiers ouvert. Il renverra le HWND de la fenêtre dans laquelle le presse-papiers est ouvert, ou IntPtr.Zero si aucune application ne l'a ouverte. Vous pouvez faire tourner la valeur jusqu'à ce que son IntPtr.Zero pendant une durée spécifiée.

28
Phil Price

Une autre solution consiste à utiliser Clipboard.SetDataObject au lieu de Clipboard.SetText.

Selon cet article MSDN cette méthode a deux paramètres - retryTimes et retryDelay - que vous pouvez utiliser comme ceci:

System.Windows.Forms.Clipboard.SetDataObject(
    "some text", // Text to store in clipboard
    false,       // Do not keep after our application exits
    5,           // Retry 5 times
    200);        // 200 ms delay between retries
41
Alex

J'ai rencontré cette erreur aujourd'hui. J'ai décidé de le gérer en informant l'utilisateur de l'application potentiellement défectueuse. Pour ce faire, vous pouvez faire quelque chose comme ceci:

[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
}

Pour moi, le titre de la fenêtre problématique était "skype_plugin_core_proxy_window". J'ai cherché des informations à ce sujet et j'ai été surpris que cela ne produise qu'un seul coup, et c'était en russe. J'ajoute donc cette réponse, à la fois pour donner un autre coup pour cette chaîne, et pour fournir une aide supplémentaire pour mettre en lumière des applications potentiellement défectueuses.

12
Jeff Roe

En utilisant le code 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
}

vous êtes capable de gérer l'erreur d'une manière assez pratique.

J'ai réussi à réduire la fréquence des erreurs en utilisant System.Windows.Forms.Clipboard Au lieu de System.Windows.Clipboard.

Je souligne que cela ne résout pas le problème, mais cela a réduit l'occurrence de mon application.

3
Strider2009

Appelez simplement ceci en premier:

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

J'ai remarqué que si vous êtes au milieu d'une opération de collage (message WM_PASTE), y compris pendant l'événement TextChanged, le presse-papiers reste verrouillé par la fenêtre (la TextBox) recevant l'événement. Donc, si vous appelez simplement cette méthode "CloseClipboard" dans le gestionnaire d'événements, vous pouvez appeler les méthodes gérées Clipboard.Clear et Clipboard.SetText sans aucun problème ni délai.

3
Triynko

Faire une Clipboard.Clear() avant Clipboard.SetDataObject(pasteString, true) semble faire l'affaire.

La suggestion précédente de définir retryTimes et retryDelay ne fonctionnait pas pour moi et en tout cas les valeurs par défaut sont retryTimes = 10 et retryDelay = 100ms

2
Tony Bennett

C'est un peu merdique ... Mais cela a résolu mon problème.

Réessayez clear () après un délai.

Plus d'informations sont dans le billet de blog Comment gérer un presse-papiers bloqué - erreur Clipboard.Clear ().

0
Patrick Sameera