Как я могу исправить ошибки "Can not open clipboard: Access Denied"?

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

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

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

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

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

Ответы

Ответ 1

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

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

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

Я бы порекомендовал второе решение, потому что это будет более похожий на 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;

Ответ 2

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

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

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

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

Ответ 3

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

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

Ответ 4

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

Этот код (не проверенный в Delphi) может вам помочь. Это не решит проблему, так как цепочка уведомлений сломана (ничего, кроме перезагрузки ПК, никогда не будет исправлено), но это устранит проблему, если какое-то время приложение блокирует буфер обмена. Увеличьте MaxRetries, если это надоедливое приложение держит буфер обмена заблокированным для A REALLY LONG time (seconds):

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.');

Ответ 5

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

Ответ 6

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

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