18

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

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

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

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

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

4

6 回答 6

23

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

你有两种可能:

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

我推荐第二种解决方案,因为它更像是德尔福的方法,最终会产生更清晰的代码。

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;
于 2009-12-07T11:03:23.293 回答
9

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

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

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

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

于 2011-01-12T16:37:05.033 回答
2

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

于 2009-12-07T10:57:53.980 回答
1

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

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

于 2009-12-07T10:57:04.333 回答
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;

此外,删除“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.');
于 2011-01-10T16:40:37.827 回答
-2

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

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

于 2017-07-29T05:09:57.460 回答