2009-12-07 13 views
13

estoy usando el siguiente código para copiar texto en el portapapeles:¿Cómo puedo solucionar los errores "No se puede abrir el portapapeles: acceso denegado"?

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

aparentemente al azar me sale "no se puede portapapeles abierto: Acceso denegado" errores. Supongo que estos errores son causados ​​por otra aplicación que bloquea el portapapeles, pero parece que nunca hago nada con otras aplicaciones que deberían causar bloqueos.

Extrañamente, mis usuarios parecen estar reportando más errores con Vista y Windows 7 que con XP.

¿Hay alguna forma de comprobar si el portapapeles está bloqueado antes de intentar acceder a él?

+0

Tenga en cuenta este fragmento de la documentación de Delphi: "Portapapeles.Abrir -> Abre el portapapeles, evitando que otras aplicaciones cambien sus contenidos hasta que el portapapeles esté cerrado.Llamada abierta antes de agregar una serie de elementos al portapapeles. Esto evita otras aplicaciones sobrescribir el portapapeles hasta que se cierre. (Al agregar un solo elemento al portapapeles, no es necesario llamar a Abrir). " – Ampere

Respuesta

16

Esto no es un problema de Delphi. Debido a que el portapapeles se puede bloquear en cualquier momento, incluso si lo comprueba, si el portapapeles no está bloqueado actualmente, podría bloquearse directamente después de la comprobación.

Usted tiene dos posibilidades:

  1. no utilice la clase portapapeles Delphi. En su lugar, use funciones API sin procesar, donde tendrá un control un poco más detallado sobre posibles situaciones de error.
  2. Espere que su código falle al agregar un manejador de excepciones. A continuación, agregue un código de reintento, es decir, vuelva a intentar establecer el texto tres veces, quizás con un retroceso exponencial, antes de arrojar su propio error.

Recomendaría la segunda solución, porque sería el enfoque más Delphi-like y al final dará como resultado un código más limpio.

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; 
+3

Esto tampoco es un problema de Win32; es un hecho simple cuando se programa para sistemas concurrentes. – mghie

1

No hay forma de verificar algo y luego, dependiendo del resultado, hacer otra cosa con la expectativa de que no puede fallar, porque a menos que el control y la acción sean una operación atómica siempre existe la posibilidad de que hilo hace lo mismo en paralelo.

Esto se aplica si intenta abrir el portapapeles, abrir un archivo, crear o eliminar un directorio; simplemente debe intentar hacerlo, tal vez varias veces en un bucle, y manejar correctamente los errores.

0

Intente comprobar GetClipboardOwner, si no es nulo y no es su Application.Handle, no puede Abrir para modificar su contenido.
E incluso parece ir bien, puede que ya no lo sea cuando lo haces.
Así que agregue una opción, excepto en un bucle hasta que lo obtenga o renuncie amablemente (notificando al usuario, por ejemplo).

1

En primer lugar, tenga en cuenta que esto probablemente no sea un problema en su aplicación. Otras aplicaciones bloquearon el portapapeles o arruinaron la cadena de notificaciones y ahora su aplicación no puede acceder a ella. Cuando tengo problemas como este, reinicio la computadora y mágicamente se van ... bueno ... al menos hasta que vuelva a ejecutar la aplicación que crea el problema.

Este código (no verificado en Delphi) puede ayudarlo. No solucionará el problema: la cadena de notificación está rota (nada excepto un reinicio de PC alguna vez lo arreglará) pero solucionará el problema si una aplicación bloquea el portapapeles por un tiempo.Aumentar las MaxRetries si esa aplicación molestos mantiene el portapapeles bloqueado por un tiempo muy largo (segundos):

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; 

Además, puede ser una buena idea para dejar caer el 'aumento' y convertirlo en una función y utilizarla de esta manera:

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

Su sueño no es suficiente. 15ms no va a hacer nada. Después de 5x15ms, lo que sea que tenía el portapapeles abierto probablemente todavía lo tenga. –

+0

Es posible que tenga razón, pero, por otro lado, si utiliza un retraso prolongado, es posible que la aplicación se cuelgue durante unos segundos solo porque el Portapapeles está en uso. Probablemente cada programador debería decidir cuánto "congelar" es aceptable. – Ampere

+0

Mejor aún: la demora se puede usar incluso como parámetro para el procedimiento. --- Acabo de actualizar el código. – Ampere

8

extraño que mis usuarios parecen ser informes más de los errores con Vista y Windows 7 que con XP

Esto puede tener que ver con cómo Vista/Win7 se ocupa de la notificación del visor del portapapeles. Aunque todavía admiten la "cadena de visor del portapapeles" de XP, que envía un mensaje de notificación que debe enviarse de nuevo a cada oyente por turno (y si una aplicación no lo hace, las demás aplicaciones no reciben ninguna notificación). Comenzando con Vista, las aplicaciones se notifican directamente. Y no hay nada que evite que intenten acceder al portapapeles de una sola vez.

Analogía: tengo 3 hijos. Tengo un pastel. Con las reglas de XP, le digo al niño mayor que coma un poco, luego le digo al siguiente niño más grande que tome una porción. Ella obtiene su porción, le dice a su hermano, él le da la suya, y le dice a su hermano, quien se la da, y todo procede de una manera ordenada.
Problema: El niño del medio lleva la torta a su habitación, no le dice a la más joven, y la más joven se pierde.

Con Vista/Windows7, ese sistema aún existe. Pero las aplicaciones más nuevas pueden solicitar ser notificadas inmediatamente, tan pronto como llegue el pastel a la cocina. Grito "¡la torta está lista!" y todos aparecen al mismo tiempo y tratan de agarrar un poco. Pero solo hay un cuchillo para servir, por lo que deben seguir buscando el cuchillo, no obtenerlo y esperando la próxima oportunidad.

0

Supongo que está ejecutando su aplicación en Win 8 o superior.

Simplemente haga clic con el botón derecho en su archivo .exe de la aplicación, vaya a la pestaña Compatibilidad y cambie el modo de compatibilidad en Windows XP o versiones anteriores. Funcionará, ¡garantizado!

Cuestiones relacionadas