18

次のコードを使用して、テキストをクリップボードにコピーしています。

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

一見ランダムに、「クリップボードを開けません: アクセスが拒否されました」というエラーが表示されます。これらのエラーは、他のアプリケーションがクリップボードをロックしていることが原因であると推測していますが、他のアプリケーションでロックを引き起こすようなことをしているようには見えません。

不思議なことに、私のユーザーは、XP よりも Vista と Windows 7 でより多くのエラーを報告しているようです。

クリップボードにアクセスする前に、クリップボードがロックされているかどうかを確認する方法はありますか?

4

6 に答える 6

23

これは Delphi の問題ではありません。クリップボードはいつでもロックできるため、チェックを行っても、現在クリップボードがロックされていないと、チェック直後にロックされる場合があります。

ここには 2 つの可能性があります。

  1. Delphi クリップボード クラスを使用しないでください。代わりに未加工の API 関数を使用すると、考えられるエラー状況をよりきめ細かく制御できます。
  2. 例外ハンドラーを追加して、コードが失敗することを想定してください。次に、再試行コードを追加します。つまり、独自のエラーをスローする前に、おそらく指数バックオフを使用して、テキストの設定を 3 回再試行します。

2 番目のソリューションをお勧めします。これは、より 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;
于 2009-12-07T11:03:23.293 に答える
9

不思議なことに、私のユーザーは、XP よりも Vista と Windows 7 でより多くのエラーを報告しているようです。

これは、Vista/Win7 がクリップボード ビューアー通知を処理する方法に関係している可能性があります。XP の「クリップボード ビューア チェーン」は引き続きサポートされていますが、これは各リスナーに順番に再送信する必要がある 1 つの通知メッセージを送信します (1 つのアプリがこれに失敗すると、他のアプリには通知されません)。Vista 以降、アプリは直接通知されます。そして、クリップボードに一度にアクセスしようとするのを妨げるものは何もありません。

類推: 私には 3 人の子供がいます。私はケーキを持っています。XP ルールでは、一番上の子にケーキを食べるように言い、次に上の子にケーキを食べるように言います。彼女は彼女のスライスを手に入れ、彼女の兄弟に伝え、彼は彼のものを手に入れ、彼の兄弟は彼のものを手に入れると伝え、すべてが整然と進行します。
問題: 真ん中の子が自分の部屋にケーキを持っていき、末っ子には言わず、末っ子が取り逃す。

Vista/Windows7 では、そのシステムは健在です。しかし、新しいアプリでは、ケーキがキッチンに到着したらすぐに通知を受け取るようにリクエストできます。「ケーキの出来上がり!」と叫びます。そしてそれらはすべて同時に現れ、いくつかをつかもうとします。しかし、サービングナイフは1つしかないため、ナイフに手を伸ばし続け、手に入れることができず、次の機会を待つ必要があります.

于 2011-01-12T16:37:05.033 に答える
2

GetClipboardOwner を確認してみてください。null ではなく、Application.Handle でもない場合は、コンテンツを開いて変更することはできません。
そして、行ってよさそうに見えても、実際に行ってみるとそうではないかもしれません。
したがって、それを取得するか、適切にあきらめるまで (たとえば、ユーザーに通知する)、ループ以外で try を追加します。

于 2009-12-07T10:57:53.980 に答える
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 に答える
1

チェックとアクションが 1 つのアトミック操作でない限り、別のプロセスまたはスレッドが同じことを行う可能性が常にあるため、何かをチェックし、結果に応じて失敗しないことを期待して別のことを行う方法はありません。並行して。

これは、クリップボードを開いたり、ファイルを開いたり、ディレクトリを作成または削除したりする場合でも保持されます。単純に、ループで数回試行し、エラーを適切に処理する必要があります。

于 2009-12-07T10:57:04.333 に答える