2012-03-03 15 views
9

En una aplicación Delphi XE, estoy tratando de configurar un gancho global para monitorear los cambios de enfoque. El gancho se crea en un archivo DLL:SetWindowsHookEx crea un gancho local. ¿Cómo hacerlo global?

focusHook := SetWindowsHookEx(WH_CBT, @FocusHookProc, HInstance, 0); 
// dwThreadId (the last argument) set to 0 should create a global hook 

En la misma DLL tengo el procedimiento de enlace que publica un mensaje a la ventana de la aplicación host:

function FocusHookProc(code : integer; wParam: WPARAM; lParam: LPARAM) : LResult; stdcall; 
begin 
    if (code < 0) then 
    begin 
    result := CallNextHookEx(focusHook, code, wParam, lParam); 
    exit; 
    end; 

    result := 0; 

    if (code = HCBT_SETFOCUS) then 
    begin 
    if (hostHWND <> INVALID_HANDLE_VALUE) then 
     PostMessage(hostHWND, cFOCUSMSGID, wParam, lParam); 
    end; 
end; 

Esto funciona, pero el anfitrión sólo recibe notificaciones en cambios de foco dentro de la aplicación misma. Hay una nota y algunos TButtons en el formulario principal, y cambiar el foco entre ellos produce el mensaje esperado. Sin embargo, ningún cambio de foco fuera de la aplicación en sí nunca se informa.

Supongo que tiene algo que ver con varias instancias de DLL que se inyecta en otros procesos. Hay una pregunta similar con una respuesta aceptada here, pero es para C, y no puedo ver cómo puedo hacer lo mismo en un dll Delphi (por ejemplo, las declaraciones pragma para configurar la memoria compartida).

(Esto es sobre todo una prueba de concepto, pero aún me gustaría ponerlo a funcionar. Necesito saber qué ventana estaba activa justo antes de que mi aplicación se activara al hacer clic, alt + tab, activación hotkey etc. El problema es que si se usa el mouse o alt + tab, GetForegroundWindow siempre devuelve el manejador de la ventana de mi propia aplicación, sin importar qué tan temprano lo puse, como enganchar la cola de mensajes principal de la aplicación. única solución viable, aunque no me gusta mucho la idea.)

Respuesta

15

Desde la DLL es inyectado en otro proceso, estás no va a conseguir cualquier punto de ruptura golpean para que no sea el proceso que es cualquier cosa depuración Además, cada instancia de la DLL en el otro proceso también obtiene sus propios datos globales/estáticos. Si hostHWND es global, no tendrá el mismo valor en el otro proceso que en este. De hecho, ni siquiera se inicializará. Necesita usar un bloque de memoria compartida para compartir valores entre los procesos. Es posible que sea necesario utilizar mutexes compartidos y otros objetos de sincronización para garantizar que las escrituras de memoria compartida estén protegidas. Finalmente, si está usando Windows Vista +, solo los procesos con el mismo nivel de acceso y debajo obtendrán la DLL inyectada. IOW, si está ejecutando el proceso como el usuario que inició sesión, solo se procesará la ejecución como usuario registrado y se inyectará dicha DLL.

+2

+1 en cada DLL que tiene sus propios datos siendo el problema probable aquí. Sin embargo, en este caso específico, el OP puede no necesitar el uso de la memoria compartida: una solución simple para el código anterior sería dar a la ventana de destino una nombre de clase específico, y hacer que el código de enlace use FindWindow para encontrar ese objetivo. Además, otra limitación de los ganchos: los ganchos de 32 bits solo se pueden enganchar en el código de 32 bits; del mismo modo con 64 bits; de modo que conectar ambos procesos de 32 y 64 bits puede ser problemático. – BrendanMcK

3

Pruebe usar WinEvents en lugar del gancho CBT: SetWinEventHook buscando EVENT_OBJECT_FOCUS como evento mínimo y máximo, con el indicador WINEVENT_OUTOFPROC y 0 para idThread e idProcess. Esto le dará un gancho que puede escuchar los eventos de enfoque de cualquier proceso en el mismo escritorio, sin requerir una DLL separada, y funcionará en aplicaciones de 32 bits y de 64 bits.

Hay un par de advertencias: una es que los eventos no son instantáneos; hay un ligero retraso ya que básicamente se publican en su proceso (que es cómo funciona la opción de fuera de proc que evita que se requiera una DLL), pero pueden ser lo suficientemente rápidos para su uso. (Y que tendría el mismo problema si utiliza PostMessage en su gancho DLL de todos modos!)

Además, obtendrá más eventos que los cambios reales de enfoque HWND: diversos controles envían estos eventos de cambio de enfoque a la señal interna enfoque cambio: enfoque moviéndose entre elementos en un cuadro de lista, por ejemplo. Puede filtrarlos filtrando en la devolución de llamada solo aquellos con idObject = OBJID_WINDOW e idChild = 0.

Alternativamente, si escuchas de eventos EVENT_SYSTEM_FOREGROUND en lugar de EVENT_OBJECT_FOCUS (see MSDN for the full list of events), entonces parece que sólo debe obtener eventos ventana de primer plano de alto nivel, que suena como lo que son en realidad después de aquí.

Cuestiones relacionadas