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 Visual C++, 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:
UINT RegisterWindowMessage(
LPCTSTR lpNombre // nombre del mensaje
);
Símplemente indicando una cadena con el nombre del mensaje, el sistema nos retorna un identificador de
mensaje único, dentro del rango 0xC000 hasta 0xFFFF. Cuando se intenta registrar varias veces el mismo nombre
de mensaje, el sistema retornará el mismo identificador.
En nuestro ejemplo para Visual C++, puede verse el uso que he dado a esta función dentro del método Registrar de
la clase CNotificacion.
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:
LRESULT SendMessage(
HWND hWnd, // descriptor de ventana destino
UINT Msg, // identificador del mensaje a enviar
WPARAM wParam, // primer parámetro
LPARAM lParam // segundo parámetro
);
Los parámetros son los siguientes:
hWnd: 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 para Visual C++, se usa la función SendMessage junto con el parámetro especial HWND_BROADCAST dentro
del método Enviar de la clase CNotificacion.
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 de C++Builder para simplificar el código, concretamente en la propiedad WindowProc de la clase TControl.
En el ejemplo para C++Builder 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.).
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", que he dedicado a las estructuras de memoria más importantes.
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 los siguiente ejemplos: