【问题标题】:How can I fix "Cannot open clipboard: Access Denied" errors?如何修复“无法打开剪贴板:拒绝访问”错误?
【发布时间】:2009-12-07 10:21:09
【问题描述】:

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

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

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

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

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

【问题讨论】:

  • 请注意 Delphi 文档中的这个 sn-p:" Clipboard.Open -> 打开剪贴板,防止其他应用程序更改其内容,直到剪贴板关闭。在添加一系列项目之前调用 Open剪贴板。这可以防止其他应用程序覆盖剪贴板,直到它关闭。(向剪贴板添加单个项目时,无需调用 Open。​​)"

标签: delphi delphi-2010 clipboard copy-paste


【解决方案1】:

这不是 Delphi 问题。因为剪贴板随时都可以被锁定,所以即使你勾选了,如果剪贴板当前没有被锁定,也有可能在勾选后直接被锁定。

你有两种可能:

  1. 不要使用 Delphi 剪贴板类。而是使用原始 API 函数,您可以在其中对可能的错误情况进行更细粒度的控制。
  2. 通过添加异常处理程序预计您的代码会失败。然后添加一些重试代码,即在抛出您自己的错误之前重试设置文本 3 次,可能使用指数退避。

我推荐第二种解决方案,因为它更像是 Delphi 方法,最终会产生更简洁的代码。

var
  Success : boolean;
  RetryCount : integer;
begin
  RetryCount := 0;
  Success := false;
  while not Success do
    try
      //
      // Set the clipboard here
      //
      Success := True;
    except
      on E: EClipboardException do
      begin
        Inc(RetryCount);
        if RetryCount < 3 then
          Sleep(RetryCount * 100)
        else
          raise Exception.Create('Cannot set clipboard after three attempts');
      end else
        raise;  // if not a clipboard problem then re-raise 
    end;
end;

【讨论】:

  • 这也不是 Win32 问题 - 在为并发系统编程时这是一个简单的事实。
  • 他们为什么不等待全局锁呢?这绝对是一个delphi问题。
【解决方案2】:

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

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

类比:我有 3 个孩子。我有一个蛋糕。使用 XP 规则,我告诉最大的孩子吃一些蛋糕,然后告诉下一个最大的孩子吃一片。她得到了她的切片,告诉她的兄弟,他得到了他的,并告诉他的兄弟,他得到了他的,一切都在有条不紊地进行。
问题:中间的孩子把蛋糕带到他的房间,没有告诉最小的,最小的错过了。

对于 Vista/Windows7,该系统仍然存在。但是更新的应用程序可以要求我在蛋糕到达厨房后立即通知我。我大喊“蛋糕做好了!”他们都同时出现并试图抓住一些。但是只有一把上菜刀,所以他们不得不不断地伸手去拿刀,没有拿到,等待下一次机会。

【讨论】:

    【解决方案3】:

    尝试检查 GetClipboardOwner,如果它不为 null 且不是您的 Application.Handle,则无法打开以修改其内容。
    即使看起来不错,但当你真正去做时,它可能不再是了。
    所以在循环中添加尝试,直到你得到它或很好地放弃(例如通知用户)。

    【讨论】:

      【解决方案4】:

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

      这适用于您尝试打​​开剪贴板、打开文件、创建或删除目录 - 您应该简单地尝试这样做,可能循环多次,并优雅地处理错误。

      【讨论】:

        【解决方案5】:

        首先请注意,这在您的应用程序中可能不是问题。其他应用程序锁定了剪贴板或弄乱了通知链,现在您的应用程序无法访问它。当我确实遇到此类问题时,我会重新启动计算机,它们会神奇地消失……嗯……至少在我再次运行产生问题的应用程序之前。

        此代码(未在 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;
        

        此外,删除“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.');
        

        【讨论】:

        • 你的睡眠时间不够长。 15ms不会做任何事情。在 5x15ms 之后,剪贴板打开的可能仍然有它。
        • 您可能是对的,但另一方面,如果您使用较长的延迟,您可能会因为剪贴板正在使用而导致应用程序挂起几秒钟。可能每个程序员都应该决定可以接受多少“冻结”。
        • 更好:延迟甚至可以用作过程的参数。 --- 我刚刚更新了代码。
        • 国际海事组织,您最好延长延迟时间。这些变薄本质上是不可预测的。您不知道哪个应用程序打开了剪贴板,或者为什么。而且您不知道剪贴板通知链中还有多少其他应用程序。您可能需要等待几秒钟才能成功获得对剪贴板的独占访问权限。如果 250 毫秒还不够,下次尝试 500,然后 1000,然后 2000。因此,将 (iDelayMs * RetryCount) 相乘,得到你想要的。
        【解决方案6】:

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

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

        【讨论】:

          猜你喜欢
          • 2020-05-24
          • 1970-01-01
          • 1970-01-01
          • 2013-07-10
          • 1970-01-01
          • 2022-07-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多