20 Stimmen

Wie kann ich die Meldung "Zwischenablage kann nicht geöffnet werden: Zugriff verweigert" beheben?

Ich verwende den folgenden Code, um Text in die Zwischenablage zu kopieren:

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

Scheinbar willkürlich erhalte ich die Meldung "Cannot open clipboard: Zugriff verweigert" Fehler. Ich vermute, dass diese Fehler durch andere Anwendungen verursacht werden, die die Zwischenablage sperren, aber ich scheine nie etwas mit anderen Anwendungen zu tun, die die Sperren verursachen sollten.

Seltsamerweise scheinen meine Benutzer mehr Fehler mit Vista und Windows 7 als mit XP zu melden.

Gibt es eine Möglichkeit zu prüfen, ob die Zwischenablage gesperrt ist, bevor man versucht, darauf zuzugreifen?

26voto

Daniel Rikowski Punkte 68643

Dies ist kein Delphi-Problem. Da die Zwischenablage jederzeit gesperrt werden kann, kann es sein, dass die Zwischenablage direkt nach der Prüfung gesperrt wird, wenn sie gerade nicht gesperrt ist, auch wenn Sie die Prüfung durchführen.

Sie haben hier zwei Möglichkeiten:

  1. Verwenden Sie nicht die Delphi-Zwischenablageklasse. Verwenden Sie stattdessen rohe API-Funktionen, bei denen Sie eine etwas feinere Kontrolle über mögliche Fehlersituationen haben.
  2. Erwarten Sie, dass Ihr Code fehlschlägt, indem Sie einen Exception-Handler hinzufügen. Fügen Sie dann einen Wiederholungscode hinzu, d.h. versuchen Sie dreimal, den Text zu setzen, vielleicht mit exponentiellem Backoff, bevor Sie Ihren eigenen Fehler auslösen.

Ich würde die zweite Lösung empfehlen, da dies der Delphi-ähnlichere Ansatz ist und am Ende zu einem saubereren Code führt.

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;

9voto

Chris Thornton Punkte 15410

Seltsamerweise scheinen meine Benutzer zu sein mehr dieser Fehler bei Vista und Vista und Windows 7 als mit XP

Dies kann damit zu tun haben, wie Vista/Win7 mit der Benachrichtigung über die Zwischenablage umgehen. Während sie noch die XP-"Zwischenablagenbetrachterkette" unterstützen, die eine Benachrichtigungsnachricht sendet, die nacheinander an jeden Abhörer erneut gesendet werden muss (und wenn eine Anwendung dies nicht tut, werden die anderen Anwendungen nicht benachrichtigt). Ab Vista werden die Anwendungen direkt benachrichtigt. Und es gibt nichts, was sie daran hindert, auf die Zwischenablage gleichzeitig zuzugreifen.

Analogie: Ich habe 3 Kinder. Ich habe einen Kuchen. Mit den XP-Regeln sage ich dem ältesten Kind, dass es ein Stück Kuchen haben soll, dann sage ich dem nächstältesten Kind, dass es ein Stück haben soll. Sie bekommt ihr Stück, sagt es ihrem Bruder, der bekommt seins, und sagt es seinem Bruder, der seins bekommt, und so geht alles seinen geordneten Gang.
Problem: Das mittlere Kind nimmt den Kuchen mit in sein Zimmer, sagt dem Jüngsten nichts davon, und der Jüngste verpasst es.

Bei Vista/Windows7 gibt es dieses System noch. Aber neuere Anwendungen können verlangen, sofort von mir benachrichtigt zu werden, sobald der Kuchen in der Küche ankommt. Ich rufe: "Der Kuchen ist fertig!", und alle tauchen gleichzeitig auf und versuchen, etwas abzubekommen. Aber es gibt nur ein Serviermesser, also müssen sie immer wieder nach dem Messer greifen, es nicht erreichen und auf die nächste Gelegenheit warten.

2voto

Francesca Punkte 21286

Versuchen Sie, GetClipboardOwner zu überprüfen. Wenn es nicht null und nicht Ihr Application.Handle ist, können Sie es nicht öffnen, um seinen Inhalt zu ändern.
Und selbst wenn es so aussieht, als ob man es schaffen könnte, ist es vielleicht nicht mehr so, wenn man es tatsächlich tut.
Fügen Sie also ein Try Except in einer Schleife ein, bis Sie es schaffen oder aufgeben (z. B. den Benutzer benachrichtigen).

1voto

mghie Punkte 31618

Es gibt keine Möglichkeit, etwas zu prüfen und dann je nach Ergebnis etwas anderes zu tun in der Erwartung, dass es nicht fehlschlagen könnte, denn wenn die Prüfung und die Aktion nicht eine einzige atomare Operation sind, besteht immer die Möglichkeit, dass ein anderer Prozess oder Thread das Gleiche parallel tut.

Dies gilt unabhängig davon, ob Sie versuchen, die Zwischenablage zu öffnen, eine Datei zu öffnen, ein Verzeichnis zu erstellen oder zu löschen - Sie sollten einfach versuchen, dies zu tun, vielleicht mehrere Male in einer Schleife, und Fehler anständig behandeln.

1voto

Server Overflow Punkte 19774

Bitte beachten Sie zunächst, dass es sich hierbei wahrscheinlich nicht um ein Problem in Ihrer Anwendung handelt. Andere Anwendungen haben die Zwischenablage gesperrt oder die Benachrichtigungskette durcheinander gebracht und nun kann Ihre Anwendung nicht mehr darauf zugreifen. Wenn ich solche Probleme habe, starte ich den Computer neu und sie verschwinden auf magische Weise... zumindest bis ich die Anwendung, die das Problem verursacht, erneut ausführe.

Dieser Code (nicht in Delphi geprüft) könnte Ihnen helfen. Er wird das Problem nicht beheben, wenn die Benachrichtigungskette unterbrochen ist (nichts außer einem PC-Neustart wird es jemals beheben), aber er wird das Problem beheben, wenn eine Anwendung die Zwischenablage für eine Weile sperrt. Erhöhen Sie die MaxRetries, wenn die lästige Anwendung die Zwischenablage WIRKLICH LANGE Zeit (Sekunden) sperrt:

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;

Es könnte auch eine gute Idee sein, das "raise" wegzulassen und es in eine Funktion umzuwandeln und so zu verwenden:

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.');

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X