如何修复“无法打开剪贴板:拒绝访问”错误?

时间:2009-12-07 10:21:09

标签: delphi delphi-2010 clipboard copy-paste

我使用以下代码将文本复制到剪贴板:

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

看似随意我得到“无法打开剪贴板:拒绝访问”错误。我猜这些错误是由其他应用程序锁定剪贴板引起的,但我似乎从来没有对应该导致锁定的其他应用程序做任何事情。

奇怪的是,我的用户似乎报告了Vista和Windows 7的错误,而不是XP。

在尝试访问剪贴板之前,有没有办法检查剪贴板是否已锁定?

6 个答案:

答案 0 :(得分:16)

这不是德尔福问题。由于剪贴板可以随时锁定,即使您检查,如果剪贴板当前未锁定,它可能会在检查后直接锁定。

这里有两种可能性:

  1. 不要使用Delphi剪贴板类。而是使用原始API函数,您可以对可能的错误情况进行更细粒度的控制。
  2. 通过添加异常处理程序,预计代码会失败。然后添加一些重试代码,即重新设置文本三次,可能是指数退避,然后再抛出你自己的错误。
  3. 我建议使用第二种解决方案,因为它更像是类似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;
    

答案 1 :(得分:8)

  

奇怪的是我的用户似乎是   报告更多的错误   Vista和Windows 7比使用XP

这可能与Vista / Win7如何处理剪贴板查看器通知有关。虽然他们仍然支持XP“剪贴板查看器链”,它会发送一条必须依次重新发送给每个侦听器的通知消息(如果一个应用程序无法执行此操作,则不会通知其他应用程序)。从Vista开始,应用程序会直接通知。没有什么可以阻止他们一次性尝试访问剪贴板。

打个比方:我有3个孩子。我有一个蛋糕。根据XP规则,我告诉最大的孩子要有一些蛋糕,然后告诉下一个最大的孩子有一个切片。她得到她的切片,告诉她的兄弟,他得到他的,并告诉他的兄弟,谁得到他的,一切都有序地进行。
问题:中间孩子将蛋糕带到他的房间,没有告诉最小的,最年轻的人错过了。

使用Vista / Windows7,该系统仍然存在。但是,一旦蛋糕到达厨房,新的应用程序可以立即请求立即通知我。我喊“蛋糕准备好了!”他们都出现在同一时间并尝试抓住一些。但是只有一把服务刀,所以他们必须继续伸手去拿刀,不能拿到它,等待下一次机会。

答案 2 :(得分:1)

没有办法检查某些东西,然后根据结果做一些其他的东西,期望它不会失败,因为除非检查和动作是一个原子操作,总是有可能是另一个进程或线程同时做同样的事。

这可以判断您是否尝试打开剪贴板,打开文件,创建或删除目录 - 您应该只是尝试这样做,可能在循环中多次,并优雅地处理错误。

答案 3 :(得分:1)

尝试检查GetClipboardOwner,如果它不是null而不是您的Application.Handle,则无法打开以修改其内容。
即使它似乎很好,但实际上它可能不再存在了 所以在循环中添加一个try,直到你得到它或放弃(例如通知用户)。

答案 4 :(得分:1)

首先请注意,这可能不是您的应用程序中的问题。其他应用程序锁定了剪贴板或弄乱了通知链,现在您的应用程序无法访问它。当我遇到这样的问题时,我重新启动计算机并且他们神奇地离开......好吧......至少在我再次运行产生问题的应用程序之前。

此代码(未在Delphi中检查)可能对您有所帮助。它不会解决问题是通知链被破坏(除了PC重启之外什么都不会修复它)但如果应用程序锁定剪贴板一段时间它将解决问题。如果那个讨厌的应用程序将剪贴板锁定了很长时间(秒),则增加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.');

答案 5 :(得分:1)

我猜你在Win 8或更高版本上运行你的应用程序。

只需右键单击您的App .exe文件,转到兼容性选项卡,然后在Windows XP或更低版本上更改兼容模式。它会起作用,保证!