web-dev-qa-db-ja.com

「クリップボードを開けません:アクセスが拒否されました」エラーを修正するにはどうすればよいですか?

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

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

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

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

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

16
norgepaul

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

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

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

2番目のソリューションをお勧めします。これは、より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;
18
Daniel Rikowski

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

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

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

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

8
Chris Thornton

GetClipboardOwnerを確認してみてください。nullでもApplication.Handleでもない場合は、開くことでコンテンツを変更することはできません。
そして、行っても良さそうだとしても、実際にやってみるともうそうではないかもしれません。
[。

2
François

まず第一に、これはおそらくあなたのアプリケーションでは問題ではないことに注意してください。他のアプリケーションがクリップボードをロックしたか、通知チェーンを台無しにしたため、アプリケーションがクリップボードにアクセスできなくなりました。このような問題が発生した場合は、コンピューターを再起動すると、魔法のように消えてしまいます...まあ...少なくとも、問題を引き起こしているアプリケーションを再度実行するまでは。

このコード(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.');

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

これは、クリップボードを開こうとしても、ファイルを開こうとしても、ディレクトリを作成または削除しようとしても当てはまります。ループ内で数回実行して、エラーを適切に処理する必要があります。

1
mghie

Win8以降でアプリを実行していると思います。

App .exeファイルを右クリックし、[互換性]タブに移動して、Windows XP以下のバージョンで互換モードを変更します。動作することは保証されています!

0
Farshad H.