Notificación de procesos
En todos los sistemas de comunicación entre procesos hemos hablado de "notificar al proceso destino".
Vamos a tratar esta tema, aunque en realidad no tenga mucho que ver con los archivos proyectados, pero sí con su uso más importante. Además, la aplicación de ejemplo en Delphi, utiliza la notificación junto con los archivos proyectados, para comunicar varios procesos entre sí.
La notificación de procesos consiste en informar a un proceso de cierto dato, normalmente un descriptor.
Existen varios métodos para notificar a un proceso de que ha ocurrido "algo", aunque ahora sólo voy a exponer el más típico: los mensajes de ventana.
La explicación completa de qué son los mensajes de ventana o el bucle de mensajes, cómo funcionan, y cómo los implementa internamente Win32 sería muy extensa, así que vamos a suponer que todo el mundo sabe lo que es un mensaje, aunque no comprendamos muy bien qué ocurre internamente.
El sistema de mensajes se basa en registrar un mensaje especial, y notificarlo a todas las ventanas del sistema.
A este mensaje sólo responderán aquellas aplicaciones que hayan registrado previamente este mensaje, es decir, nuestras aplicaciones.
El paso de registrar el mensajes es sencillo, nos bastan con la siguiente función del API:
function RegisterWindowMessage(
lpNombre: PChar { nombre del mensaje }
): LongWord;
Símplemente indicando una cadena con el nombre del mensaje, el sistema nos retorna un identificador de mensaje único, dentro del rango $C000 hasta $FFFF. Cuando se intenta registrar varias veces el mismo nombre de mensaje, el sistema retornará el mismo identificador.
En nuestro ejemplo para Delphi, puede verse el uso que he dado a esta función dentro del método Registrar de la clase TNotificacion.
Una vez registrado, debemos lanzarlo a todas las aplicaciones del sistema, a través de la función PostMensage o SendMessage. No vamos a entrar en la diferencia entre ambas funciones, y lo dejaremos para otra ocasión, en que tratemos en profundidad los mensajes de ventana.
La sintaxis de ambas funciones es la misma, así que sólo explicaré una de ellas:
function SendMessage(
hWindow: HWND; { descriptor de ventana destino }
Msg: LongWord; { identificador del mensaje a enviar }
wParam: LongInt; { primer parámetro }
lParam: LongInt { segundo parámetro }
): LongInt;
Los parámetros son los siguientes:
hWindow: descriptor de la ventana destino. Los mensajes de ventana (como su propio nombre indica) se envían a una ventana específica. Este valor podemos obtenerlo a través de cualquier función que nos retorne un descriptor de ventana (como CreateWindow, GetWindow, FindWindow, etc.).
Existe un valor especial contenido en la constante HWND_BROADCAST. Este valor le indica a Windows que distribuya el mensaje por todas las ventanas del sistema, no sólo a una ventana concreta.
Msg: indica el identificador del mensaje a pasar. En nuestro caso concreto, este valor lo obtendremos a través de la función RegisterWindowMessage, aunque para otros casos, podemos utilizar un mensaje predefinido por el sistema (normalmente una constante que comienza por WM_) o un mensaje de usuario (cualquier valor superior a WM_USER, vamos: 1024).
wParam: en este parámetro se puede pasar cualquier valor de 32 bits como parámetro. La interpretación que se dará a este valor depende del mensaje que pasemos. En nuestro caso, vamos a utilizar este valor para pasar el descriptor de la ventana que envía el mensaje.
lParam: este parámetro funciona como wParam, pudiendo pasarse otro valor de 32 bits.
En el ejemplo, se usa la función SendMessage junto con el parámetro especial HWND_BROADCAST dentro del método Enviar de la clase TNotificacion.
El último paso que debemos dar, es interceptar el mensaje que recibe la aplicación. Para ello he utilizado una técnica llamada "Subclasificación del bucle de mensajes", aunque no voy a entrar a detallarla, ya que para ello, lo primero que tendría que hacer es explicar qué es un bucle de mensajes. Símplemente diré que me he apoyado en la VCL para simplificar el código, concretamente en la propiedad WindowProc de la clase TControl.
Puede verse cómo hacer la "subclasificación" dentro de la clase TMainForm, a través del método SubClassWndProc.
Sincronización entre procesos
En ciertos casos, además de la notificación es necesaria una sincronización al acceso de la memoria entre procesos. Esto es necesario para asegurarnos la coherencia de los datos en memoria, para que un proceso lea datos cuando realmente están disponibles o completos, o un proceso grabe cuando ningún otro lo está haciendo.
Para asegurarnos la sincronización entre procesos, se utilizan las mismas técnicas y objetos que para la sincronización entre hilos (semáforos, eventos, etc.), que está explicando nuestro compañero Salvador Jover en su serie sobre Threads. Así que para profundizar en este tema, os remito a su serie de artículos, en la que todos aprenderemos mucho.
Conclusión
Esto es todo lo que han dado de sí los archivos proyectados en memoria. Creo que con lo que aquí he expuesto, todo el mundo está en condiciones de empezar a usar este sistema, ya sea para compartir datos entre aplicaciones, o utilizarlo como método de entrada/salida a disco.
Bueno, pues esto se acaba, tanto el artículo como la primera parte de la serie "Los rincones del API Win32".
Espero que en estos cuatro artículos hayamos comprendido un poco mejor cómo se maneja la memoria en la plataforma Win32. Hemos abarcado casi todos los aspectos, desde los más sencillos y documentados, hasta los más oscuros.
Nos veremos en la próxima serie de artículos de "Los rincones del API Win32" que tratará sobre... ¿alguna sugerencia?
Los ejemplos
Todo lo que hemos ido explicando, se utiliza de modo práctico en el siguiente ejemplo: