Tabla de contenidos
Este es el primer tutorial o guía que he escrito en mi vida, asi que si encuentran algun error de ortografía, o no les gusta algo que puse, pido disculpas :-). Algo también que me gustaría poner acá es que crecí con los términos en Inglés de la programación, asi que he hecho un esfuerzo en ponerlos en Castellano, aunque estos no tengan un significado claro para mi, como por ejemplo (hilo, cola, pila, etc) suenan muy raros y graciosos jejeje, asi que si encuentran un termino por ahí en Inglés, es porque no se cómo se llama en Castellano.
Aqui traduzco un poco lo que el MSDN nos da a saber acerca de los ganchos en Windows, por ejemplo como instalarlos, desinstalarlos, manejarlos, etc. No es la información completa. Si te gusta leer o no sabes acerca de los ganchos quizás te ayude leer la traducción que puse aquí, para los que ya saben no necesitan leerlo.
Me decidí a escribir este tutorial acerca de los hooks en Windows, porque hace algun tiempo buscaba información sobre este tema, y no encontraba mucha información, y la que encontraba estaba en Inglés y no encontraba nada en Castellano. Tambien la ayuda del MSDN me ayudo, pero el código que ponia como algo de ejemplo es para VC++ :-).
Bueno lo que tratare de mostrar en este tutorial
acerca de los ganchos de windows es algo básico de cómo agregar
mas funcionalidad (si es que asi se le puede llamar) a programas foraneos (programas
ya compilados que no tenemos los source codes y no tienen soporte de plugin
claro). Usaremos para nuestro ejemplo el famoso Notepad que viene con Windows,
le agregaremos un menú dinamicamente y simplemente mostraremos un messagebox
al clickear en un item del menú :-).
Así que este tutorial tendrá información acerca de ganchos,
subclassing, y MMF (memory mapped files).
Ok, Voy a agregar un menú
dinamicamente a todas las instancias del Notepad, después de haber instalado
el gancho. Cada instancia del Notepad tiene un ID de hilo distinto, entonces
eso me da a entender, de que el tipo de gancho que instalare sera global o system-wide,
y como dice el MSDN debo crear/usar una DLL para ganchos globales, y también
dice que debo compartir algunos datos que usaré en otro proceso. Esto
es porque cada instancia de un programa o DLL tiene un espacio privado en la
memoria, y no puedo accesar a ellas normalmente, entonces las variables en diferentes
procesos seran diferentes o simplemente invalidos.
Es por eso que debo compartir por lo menos una variable en todas las
instancias de la DLL, la variable que compartire sera el manejador del gancho
(HHOOK) que debo pasarle a CallNextHookEx.
Como Delphi según se o_0, no permite asignarle a un segmento el atributo
de shared, como lo permite por ejemplo VC++ y MASM32, entonces usare MMF (memory
mapped files - archivos mapeados en memoria).
Para eso usaré CreateFileMapping, MapViewOfFile, UnmapViewOfFile,
y CloseHandle. Compartiré el HHOOK que es igual a un DWORD, entonces
sera 4 bytes los que reservaré.
Declaro las variables y constantes que necesitaré:
szClassName: es el
nombre de la class del Notepad.
szMMFName: es el nombre que le daré al espacio de memoria y donde
tendré acceso de lectura y escritura.
WM_STOPSUBCLASS: es un mensaje que enviaré a las ventanas del
Notepad para darles a saber que ya quiero terminar de subclassearlas.
ID_ItemX: son los IDs que le daré a los items de los menues que
agregare dinamicamente.
pdwDatos: es un puntero a un DWORD, donde almacenaré el manejador
del gancho (HHOOK).
hMap: una simple variable de tipo DWORD, la usaré para guardar
el manejador que me devuelve CreateFileMapping.
Cada vez que mi DLL sea cargada por un proceso, usare OpenGlobalData para obtener un puntero al espacio creado y cuando se descarge mi DLL usare CloseGlobalData.
(* cerramos todo *).
procedure CloseGlobalData();
begin
UnmapViewOfFile(pdwDatos);
CloseHandle(hMap);
end;
Ahora como se cuando la
DLL es cargada/descargada ?
Quizas ya sepas esto, pero en una ayuda de Borland leí que se debía
hacer así.
(*
entrada, llamo a
process_attach *)
begin
DllProc := @DLLEntryPoint;
DllEntryPoint(DLL_PROCESS_ATTACH);
end.
Ahora solo necesito instalar/desinstalar el gancho
de tipo WH_CBT y crear una función filtro.
Windows nos dice que la función filtro de un gancho de tipo WH_CBT puede
recibir esto:
nCode: HCBT_ACTIVATE, HCBT_CLICKSKIPPED,
HCBT_CREATEWND, HCBT_DESTROYWND, HCBT_KEYSKIPPED, HCBT_MINMAX, HCBT_MOVESIZE,
HCBT_QS, HCBT_SETFOCUS, y HCBT_SYSCOMMAND.
wParam: Depende del nCode.
lParam: Depende del nCode.
Estoy intersado en la notificacion HCBT_CREATEWND, que es la que recibire cuando una ventana esté a punto de ser creada y los parámetros wParam y lParam tendrán estos valores.
wParam: Manejador de la ventana que será
creada.
lParam: Puntero a una estructura CBT_CREATEWND.
Crearé dos procedimientos para instalar/desinstalar el gancho y serán exportadas, para que un programa pueda llamarlas.
(*
exportar; instalar el gancho de tipo CBT *)
procedure StartHook(); stdcall;
begin
pdwDatos^ := SetWindowsHookEx(WH_CBT, @CBTProc, hInstance, 0);
end;
(* exportar;
desinstalar el gancho *)
procedure StopHook();
stdcall;
begin
RestaurarWndProcs();
UnHookWindowsHookEx(pdwDatos^);
end;
Subclasseo la ventana, osea cambio su función
que procesa los mensajes de Windows, y le doy una nueva. Windows llamará
primero a mi funcion y luego yo llamaré a la función vieja, de
esta forma puedo atrapar los mensajes que generará mi menú dinámico
:-).
Como ves también guardo el puntero a la función WNDPROC vieja
en la misma ventana, así no tengo que crear un array dinámico,
y me ahorro mucho trabajo. Si usara un array dinámico de punteros, tendría
que compartirlo entre todos los procesos también, así como comparto
el valor de pdwDatos^.
Función nueva que procesa los mensajes
Solo me falta mostrar lo que
RestaurarWndProcs es.
Este procedimiento lo llamo cuando desinstalo el gancho. Este procedimiento
simplemente enumera las ventanas buscando por una ventana la cual tenga su class
igual a la class del Notepad, y si la encuentra le envía el mensaje WH_STOPSUBCLASS.
Si el Notepad fue creado antes de que el gancho sea instalado, igualmente recibirá
el mensaje, pero no lo entendera y no sucederá nada.
Código fuente del proyecto DLL.
Click aquí para descargar los fuentes de ejemplo de este tutorial.
Bueno, eso cubre todo creo o_0.
chao
Liebesschmerz--