Как я могу исправить ошибки «Не удается открыть буфер обмена: доступ запрещен»? - PullRequest
14 голосов
/ 07 декабря 2009

Я использую следующий код для копирования текста в буфер обмена:

  Clipboard.Open;
  try
    Clipboard.AsText := GenerateClipboardText;
  finally
    Clipboard.Close;
  end;

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

Странно, но мои пользователи, похоже, сообщают о большем количестве ошибок в Vista и Windows 7, чем в XP.

Есть ли способ проверить, заблокирован ли буфер обмена, прежде чем пытаться получить к нему доступ?

Ответы [ 6 ]

16 голосов
/ 07 декабря 2009

Это не проблема Delphi. Поскольку буфер обмена может быть заблокирован в любой момент, даже если вы проверите, если буфер обмена в настоящий момент не заблокирован, он может быть заблокирован непосредственно после проверки.

У вас есть две возможности:

  1. Не используйте класс буфера обмена Delphi. Вместо этого используйте необработанные функции API, где у вас есть немного более детальный контроль над возможными ошибочными ситуациями.
  2. Ожидайте сбоя вашего кода, добавив обработчик исключений. Затем добавьте некоторый код повтора, т. Е. Повторите попытку, чтобы установить текст три раза, возможно, с экспоненциальным откатом, прежде чем выдать свою собственную ошибку.

Я бы порекомендовал второе решение, потому что это был бы более Delphi-подобный подход и в конце концов приведет к более чистому коду.

while not Success do
try
  //Set the clipboard
  Success := True;
except
  on Exception do
  begin
    Inc(RetryCount);
    if RetryCount < 3 then 
      Sleep(RetryCount * 100)
    else 
      raise MyException.Create('Cannot set clipboard');
  end;
end;
8 голосов
/ 12 января 2011

Странно, но мои пользователи сообщать больше ошибок с Vista и Windows 7, чем с XP

Возможно, это связано с тем, как Vista / Win7 справляется с уведомлением средства просмотра буфера обмена. Хотя они по-прежнему поддерживают XP «цепочку просмотра буфера обмена», которая отправляет по одному уведомлению, которое необходимо переслать каждому слушателю по очереди (и если одно приложение не может это сделать, другие приложения не уведомляются). Начиная с Vista, приложения уведомляются напрямую. И ничто не мешает им одновременно получить доступ к буферу обмена.

Аналогия: у меня трое детей. У меня есть торт. С помощью правил XP я говорю старшему ребенку, чтобы он съел торт, а затем велел следующему старшему ребенку есть кусок. Она получает свой кусок, говорит своему брату, он получает его, и говорит своему брату, который получает его, и все происходит в порядке.
Проблема: средний ребенок берет торт в свою комнату, не говорит младшему, а самый младший пропускает.

В Vista / Windows7 эта система все еще существует. Но новые приложения могут потребовать от меня немедленного уведомления, как только пирог прибудет на кухню. Я кричу "торт готов!" и все они появляются в одно и то же время и пытаются захватить немного. Но есть только один сервировочный нож, поэтому они должны продолжать тянуться к ножу, не в состоянии достать его и ждать следующей возможности.

1 голос
/ 29 июля 2017

Я полагаю, вы запускаете свое приложение на Win 8 или выше.

Просто щелкните правой кнопкой мыши файл App .exe, перейдите на вкладку «Совместимость» и измените режим совместимости в Windows XP или более ранних версиях. Это будет работать, гарантировано!

1 голос
/ 10 января 2011

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

Этот код (не проверенный в Delphi) может вам помочь. Это не решит проблему, если нарушена цепочка уведомлений (ничего, кроме перезапуска ПК, это не исправит), но это исправит проблему, если какое-то время приложение блокирует буфер обмена. Увеличьте MaxRetries, если это противное приложение удерживает буфер обмена заблокированным в течение ДЕЙСТВИТЕЛЬНО ДОЛГОГО времени (в секундах):

procedure Str2Clipboard(CONST Str: string; iDelayMs: integer);
CONST
   MaxRetries= 5;
VAR RetryCount: Integer;
begin
 RetryCount:= 0;
 for RetryCount:= 1 to MaxRetries DO
  TRY
    inc(RetryCount);
    Clipboard.AsText:= Str;
    Break;
  EXCEPT
    on Exception DO
      if RetryCount = MaxRetries
      then RAISE Exception.Create('Cannot set clipboard')
      else Sleep(iDelayMs)
  END;
end;

Кроме того, было бы неплохо отбросить «рейз» и преобразовать его в функцию и использовать ее следующим образом:

if not Str2Clipboard 
then Log.AddMsg('Dear user, other applications are blocking the clipboard. We have tried. We really did. But it didn''t work. Try again in a few seconds.');
1 голос
/ 07 декабря 2009

Попробуйте проверить GetClipboardOwner, если он не нулевой и не для вашего Application.Handle, вы не можете открыть для изменения его содержимого.
И даже если это кажется удачным, это может быть уже не так, когда вы на самом деле делаете это.
Поэтому добавляйте попытку, за исключением цикла, до тех пор, пока вы его не получите или не сдадитесь (например, уведомив пользователя).

1 голос
/ 07 декабря 2009

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

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

...