Delphi “我怎样才能解决问题?”;无法打开剪贴板:访问被拒绝";错误?

Delphi “我怎样才能解决问题?”;无法打开剪贴板:访问被拒绝";错误?,delphi,delphi-2010,clipboard,copy-paste,Delphi,Delphi 2010,Clipboard,Copy Paste,我正在使用以下代码将文本复制到剪贴板: Clipboard.Open; try Clipboard.AsText := GenerateClipboardText; finally Clipboard.Close; end; 似乎是随机出现“无法打开剪贴板:访问被拒绝”错误。我猜这些错误是由其他应用程序锁定剪贴板引起的,但我似乎从未对其他应用程序进行过任何应该导致锁定的操作 奇怪的是,我的用户报告的Vista和Windows7错误似乎比XP多 在尝试访问剪贴板之

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

  Clipboard.Open;
  try
    Clipboard.AsText := GenerateClipboardText;
  finally
    Clipboard.Close;
  end;
似乎是随机出现“无法打开剪贴板:访问被拒绝”错误。我猜这些错误是由其他应用程序锁定剪贴板引起的,但我似乎从未对其他应用程序进行过任何应该导致锁定的操作

奇怪的是,我的用户报告的Vista和Windows7错误似乎比XP多


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

没有方法检查某个内容,然后根据结果执行其他操作,期望它不会失败,因为除非检查和操作是一个原子操作,否则另一个进程或线程总是有可能并行执行相同的操作


这适用于您是否尝试打开剪贴板、打开文件、创建或删除目录-您只需尝试这样做,可能会在循环中多次尝试,并优雅地处理错误。

尝试检查GetClipboardOwner,如果它不为null且不是您的应用程序。handle,您无法打开以修改其内容。
即使看起来很好去,但当你真的去做的时候,它可能不再是了。

因此,除了在循环中添加一个try,直到您得到它或很好地放弃为止(例如通知用户)。

这不是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;
    
    虽然不成功,但要
    尝试
    //设置剪贴板
    成功:=正确;
    除了
    例外情况
    开始
    公司(RetryCount);
    如果RetryCount<3,则
    睡眠(RetryCount*100)
    其他的
    引发MyException.Create('无法设置剪贴板');
    结束;
    结束;
    
    首先,请注意,这可能不是应用程序中的问题。其他应用程序锁定了剪贴板或打乱了通知链,现在您的应用程序无法访问它。当我遇到这样的问题时,我重启电脑,它们神奇地消失了。。。好。。。至少在我再次运行产生问题的应用程序之前

    此代码(未在Delphi中检查)可能会对您有所帮助。它不会解决通知链断开的问题(除了重新启动PC之外,没有任何东西可以解决问题),但如果应用程序锁定剪贴板一段时间,它会解决问题。如果讨厌的应用程序将剪贴板锁定很长时间(秒),请增加MaxRetries:

    此外,删除“raise”并将其转换为函数,然后像这样使用它可能是一个好主意:

    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.');
    
    奇怪的是,我的用户似乎 使用报告更多错误 Vista和Windows 7与XP相比更具优势

    这可能与Vista/Win7如何处理剪贴板查看器通知有关。尽管它们仍然支持XP“剪贴板查看器链”,它会发送一条通知消息,必须依次重新发送给每个侦听器(如果一个应用程序未能做到这一点,则不会通知其他应用程序)。从Vista开始,应用程序将被直接通知。没有什么可以阻止他们一次访问剪贴板

    类比:我有三个孩子。我有一个蛋糕。根据XP规则,我告诉最大的孩子吃一些蛋糕,然后告诉下一个最大的孩子吃一片。她得到她的那一片,告诉她的兄弟,他得到了他的,并告诉他的兄弟,谁得到了他的,一切都在有序地进行。
    问题:中间的孩子把蛋糕拿到他的房间,没有告诉最小的,最小的错过了


    在Vista/Windows7中,该系统仍然存在。但新的应用程序可以要求我在蛋糕一到厨房就立即通知。我喊着“蛋糕准备好了!”他们都同时出现并试图抓一些。但是只有一把餐刀,所以他们必须不断地伸手去拿餐刀,但拿不到,并等待下一个机会

    我猜您正在Win 8或更高版本上运行应用程序


    只需右键单击App.exe文件,转到“兼容性”选项卡,并在Windows XP或更低版本上更改兼容性模式。保证会成功的

    这也不是Win32的问题——在为并发系统编程时,这是一个简单的事实。为什么它们不等待全局锁呢?这绝对是个德尔福问题。你的睡眠时间不够长。15毫秒没什么用。在5x15ms之后,任何打开剪贴板的东西都可能仍然有它。你可能是对的,但另一方面,如果你使用长时间延迟,你可能会因为剪贴板正在使用而让应用程序挂起几秒钟。也许每个程序员都应该决定多少“冻结”是可以接受的。更好的是:延迟甚至可以用作过程的参数。--我刚刚更新了代码。依我看,延迟时间越长越好。这些事情本质上是不可预测的。你不知道什么应用打开了剪贴板,或者为什么。你不知道剪贴板通知链中还有多少其他应用程序。您可能需要等待几秒钟才能成功以独占方式访问剪贴板。如果250毫秒还不够,下次试试500,然后1000,然后2000。因此,乘以(iDelayMs*RetryCount)可以得到您想要的结果。请注意Delphi文档中的这段代码:“Clipboard.Open->打开剪贴板,防止其他应用程序
    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.');