Как справиться с заблокированным буфером обмена и другими странностями - PullRequest
50 голосов
/ 30 мая 2009

За последние пару часов я обнаружил довольно специфическую ошибку, которая возникает из-за того, что в другом приложении открыт буфер обмена. По существу, поскольку буфер обмена является общим ресурсом (согласно «Почему мой общий буфер обмена не работает?» ), и вы пытаетесь выполнить

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 мсек.

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

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

Ответы [ 8 ]

41 голосов
/ 05 марта 2011

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

Согласно этой статье MSDN этот метод имеет два параметра - retryTimes и retryDelay - которые вы можете использовать следующим образом:

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
28 голосов
/ 30 мая 2009

Поскольку буфер обмена используется всеми приложениями пользовательского интерфейса, вы будете время от времени сталкиваться с этим. Очевидно, что вы не хотите, чтобы ваше приложение аварийно завершало работу, если оно не записывалось в буфер обмена, поэтому разумно обрабатывать исключение ExternalException. Я бы предложил представить ошибку пользователю, если вызов SetObjectData для записи в буфер обмена не удался.

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

12 голосов
/ 13 октября 2011

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

[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". Я искал информацию об этом, и был удивлен, что это дало только один удар, и это было на русском языке. Поэтому я добавляю этот ответ, чтобы дать еще один удар по этой строке и предоставить дополнительную помощь для выявления потенциально неправильно работающих приложений.

3 голосов
/ 24 июля 2013

Используя код Джеффа Роу ( Код Джеффа )

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

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

3 голосов
/ 10 октября 2012

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

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

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

2 голосов
/ 25 мая 2012

Выполнение Clipboard.Clear() до Clipboard.SetDataObject(pasteString, true), кажется, помогает.

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

0 голосов
/ 05 марта 2012

Я на самом деле придумала собственное решение, и, похоже, оно работает для меня.

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

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

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

  1. Вызов функции ClipboardPaste («Буфер обмена»);
  2. Затем он очистит буфер обмена
  3. Тогда он «попытается» поместить вашу строку в буфер обмена.
  4. Сначала он ждет полсекунды (500 миллисекунд)
  5. Снова очищает буфер обмена
  6. Затем он пытается поместить строку в буфер обмена, используя SetDataObject
  7. SetDataObject в случае сбоя будет повторяться до пяти раз с задержкой 250 миллисекунд между каждой попыткой.
  8. Если первоначальная попытка не удалась, она ловит исключение, сбой, а затем повторяет попытку.

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

0 голосов
/ 17 августа 2011

Это немного глупо ... Но это решило мою проблему.

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

Больше информации в блоге Как обрабатывать заблокированный буфер обмена - ошибка Clipboard.Clear () .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...