Подтвердить что ты не робот

Как обрабатывать заблокированный буфер обмена и другие странности

В течение последних двух часов я отслеживал довольно определенную ошибку, так как другое приложение открыло буфер обмена. По существу, поскольку буфер обмена является общим ресурсом (согласно "Почему мой общий буфер не работает?" и вы пытаетесь выполнить

Clipboard.SetText(string)

или

Clipboard.Clear().

Вызывается следующее исключение:

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)

Моим первоначальным решением было повторить попытку после короткой паузы, пока я не понял, что Clipboard.SetDataObject имеет поля для количества раз и длины задержки. Поведение .NET по умолчанию - попробовать 10 раз с задержкой в ​​100 мс.

Есть одна заключительная вещь, которая была отмечена конечным пользователем, несмотря на то, что исключение вызывает операцию копирования в буфер обмена, но я не смог найти никакой дополнительной информации о том, почему это может быть.

Мое текущее решение проблемы - просто молча игнорировать исключение... это действительно лучший способ?

4b9b3361

Ответ 1

Поскольку буфер обмена используется всеми приложениями UI, вы будете сталкиваться с этим время от времени, очевидно, что вы не хотите, чтобы ваше приложение терпело крах, если ему не удалось записать в буфер обмена, поэтому изящно обрабатывать ExternalException разумно, я предложит пользователю представление и ошибку, если вызов setobjectdata для записи в буфер обмена не удастся.

Было предложено использовать (через p/invoke) user32!GetOpenClipboardWindow, чтобы увидеть, открыто ли другое приложение для открытия буфера обмена, оно вернется HWND окна, в котором открыт буфер обмена, или IntPtr.Zero, если приложение не было открыто, вы можете вращать значение до тех пор, пока оно не будет IntPtr.Zero в течение определенного периода времени.

Ответ 2

Мне жаль воскресить старый вопрос, но другим обходным решением было бы использовать Clipboard.SetDataObject вместо Clipboard.SetText.

В соответствии с этой статьей msdn этот метод имеет два параметра: retryTimes и retryDelay - что вы можете использовать вот так:

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

Ответ 3

Сегодня я столкнулся с этой ошибкой. Я решил обработать это, сообщив пользователю о потенциально ошибочном приложении. Для этого вы можете сделать что-то вроде этого:

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

Для меня заголовок окна проблемы был "skype_plugin_core_proxy_window". Я искал информацию об этом и был удивлен, что это дало только один удар, и это было на русском языке. Поэтому я добавляю этот ответ, чтобы дать еще один удар для этой строки и предоставить дополнительную помощь, чтобы принести потенциально опасные приложения для освещения.

Ответ 4

Выполнение Clipboard.Clear() перед Clipboard.SetDataObject(pasteString, true) делает трюк.

Предыдущее предложение настройки retryTimes и retryDelay не работало для меня, и в любом случае значения по умолчанию retryTimes = 10 и retryDelay = 100ms

Ответ 5

Это немного дерьмовый, но он решил мою проблему.

Повторите попытку очистки() после задержки.

Подробнее @этот блог.

Ответ 6

Просто позвоните сначала:

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

Я заметил, что если вы находитесь в середине операции вставки (сообщение WM_PASTE), в том числе во время события TextChanged, буфер обмена остается заблокированным окном (TextBox), принимающим событие. Поэтому, если вы просто вызываете этот метод "CloseClipboard" внутри обработчика события, вы можете вызвать управляемые методы Clipboard.Clear и Clipboard.SetText без каких-либо проблем или задержек.

Ответ 7

Используя код Джеффа (Jeff 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
}

вы можете обрабатывать ошибку довольно удобным способом.

Мне удалось уменьшить частоту ошибки, используя System.Windows.Forms.Clipboard Вместо System.Windows.Clipboard.

Я подчеркиваю, что это не устраняет проблему, но это уменьшило возникновение моего приложения.

Ответ 8

Итак, я действительно придумал свое решение. Я знаю, что я немного опаздываю, ОДНАКО, похоже, это работает для меня.

// 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);
        }
    }

Это, очевидно, С#, однако эти методы доступны для всех Visual Studios. Я, очевидно, создал функцию looping, а также попытался принудительно вставить ее в буфер обмена с помощью попыток.

По существу здесь поток. Допустим, вы хотите поместить слово Clipboard в буфер обмена в любом месте вашего кода (при условии, что эта функция определена).

  • Функция вызова ClipboardPaste ( "Буфер обмена" );
  • Затем очистит буфер обмена
  • Затем он "попытается" поместить вашу строку в буфер обмена.
  • Сначала он ждет полсекунды (500 миллисекунд).
  • Сбрасывает буфер обмена снова
  • Затем он пытается поместить строку в буфер обмена с помощью SetDataObject
  • SetDataObject, если он терпит неудачу, повторит попытку до 5 раз с задержкой в ​​250 миллисекунд между каждой попыткой.
  • Если начальная попытка не удалась, она обнаруживает исключение, аварийное завершение, затем пытается все заново.

Да, у этого есть недостаток, если вы знаете, что ваш буфер обмена всегда будет иметь исключение независимо от того, что (бесконечный цикл). Однако я еще не сталкивался с бесконечным циклом с этим методом. Другой недостаток заключается в том, что он может занять пару секунд (существенно замедляя работу ваших приложений), прежде чем он будет работать, в то время как при попытке его может заморозить ваше приложение, как только оно сработает, приложение все равно продолжит работу.