Ver Mensaje Individual
  #17  
Antiguo 10-07-2003
andres1569 andres1569 is offline
Miembro
 
Registrado: may 2003
Posts: 908
Reputación: 21
andres1569 Va por buen camino
Hola:

Sebas, leí tu mensaje hace días, pero ya me temía que llevaría un ratillo de sacar esto. He comprobado que es cierto lo que tú dices, si desplegamos un menú y movemos el ratón sobre él, no se dispara el evento OnMessage del ApplicationEvents, por lo tanto, no se reinicia el contador. Me temo que esto se debe a que los menús son controles que Delphi no implementa directamente sino que delega en Windows, y una vez que se muestran, los eventos que suceden sobre ellos no son capturados por la aplicación, salvo cuando el usuario hace click sobre una opción.

En definitiva, me he decidido a implementarlo mediante Hooks de ratón y de teclado y ha dado buen resultado. Reconozco que nunca había usado este recurso de programación, aunque había oído hablar de ellos bastantes veces. Básicamente se trata de registrar en Windows unas funciones que se interponen entre los eventos y nuestra aplicación, como una capa intermedia. Hay varios tipos de Hooks, para lo que nos preocupa capturamos sólo los que tienen que ver con acciones directas del usuario. Éste es el código nuevo, donde ya podemos olvidarnos del componente TApplicationEvents y del evento OnMessage (por cierto ya vamos por la versión 2.0 de este código, release 5, quien tenga la versión 1.0 que se baje el Update Pack 1 ):
Código:
var
  Form1: TForm1;
  UltimoAcceso : Longint;  // variable global
  HookHandleTec : hHook = 0;  // Hook de teclado
  HookHandleRat : hHook = 0;  // Hook de ratón

implementation

{$R *.DFM}

// ésta es la función Callback que instalamos para controlar eventos de teclado
function CapturaMensajesTeclado (Code: Integer; wParam, lParam: Longint) 
  : Longint;  stdcall;
begin
  Case Code of
    HC_ACTION,
    HC_NOREMOVE : begin
                    UltimoAcceso := GetTickCount;
                    result := 0;
                  end
    else result := CallNextHookEx (WH_KEYBOARD, Code, wParam, lParam);
    end;
end;

// ésta es la función Callback que instalamos para controlar eventos del mouse
function CapturaMensajesRaton (Code: Integer; wParam, lParam: Longint) 
  : Longint;  stdcall;
begin
  Case Code of
    HC_ACTION,
    HC_NOREMOVE : begin
                    UltimoAcceso := GetTickCount;
                    result := 0;
                  end
    else result := CallNextHookEx (WH_MOUSE, Code, wParam, lParam);
    end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  // inicializamos el Contador de tiempo y registramos en Windows las funciones Hook
  UltimoAcceso := GetTickCount;  
  HookHandleTec := SetWindowsHookEx (WH_KEYBOARD, CapturaMensajesTeclado, hInstance, 0);
  HookHandleRat := SetWindowsHookEx (WH_MOUSE, CapturaMensajesRaton, hInstance, 0); 
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  // si se llega al tope previsto, se muestra un mensaje 
  if GetTickCount - UltimoAcceso > 10000 then
  begin
    UltimoAcceso := GetTickCount; // reinicalizamos el contador, sólo para pruebas
    ShowMessage('Tiempo excedido');
  end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  // Liberamos los Hooks
  UnHookWindowsHookEx (HookHandleTec);
  UnHookWindowsHookEx (HookHandleRat);
end;
Como veis, ha cambiado la forma de hacerlo, pero la filosofía es la misma. Ahora, las notificaciones de eventos de usuario no nos vienen desde la aplicación sino que las notifica el mismo Windows.

Por cierto, los Hooks que instala una aplicación, como los del ejemplo, afectan al thread de la aplicación, de modo que si el usuario se pone a jugar al Solitario sin pasar el ratón por encima de nuestro programa, nuestro contador seguirá contando como si estuviera inactivo. Si se quisiera controlar a nivel del sistema, para asegurarse de que el usuario realmente no está actuando con la máquina, habría que registrar los Hooks desde una DLL, que cargaríamos al arrancar nuestra aplicación. De esa forma, los Hooks afectarían a todas las aplicaciones (threads) que tuviéramos activas en ese moneto. Si tenéis acceso a la "Guía de Desarrollo en Delphi X", de Teixeira y Pacheco, ahí viene muy bien explicado cómo usar los Hooks. En la DDG 5 está en la pag. 665.

Respecto a lo de los threads los ejemplos que te puedo poner son sacados de libros. Si no tienes ninguno a mano, te los posteo en otro mensaje (ahora dame un respiro ). En concreto tengo aquí un ejemplo bastante bueno de Marco Cantú, libro "Mastering Delphi 4.0", pag. 925, donde aplica un thread para realizar una operación sobre una tabla. Para usar un thread, básicamente, hay que crear una clase derivada de TThread y redefinir el método Execute, donde irá el código que quieres que se ejecute "separadamente" del resto. Luego, en el momento que creas el TThread, Delphi ya se encarga de registrarlo en la lista de hilos de Windows y de que se ejecute el código que le has programado. Imagino que en cualquier libro de Delphi debe venir algo sobre el asunto.

Saludos
__________________
Guía de Estilo
Responder Con Cita