Comunicación entre procesos en Windows

COMPARTIR EN REDES SOCIALES

En esta entrada vamos a implementar y explicar uno de los mecanismos de comunicación entre procesos en Windows. Utilizaremos IPC («Inter Process Communication») mediante memoria compartida.

Nuestro objetivo es comunicar un proceso A con un proceso B y enviar cualquier tipo de información de un proceso al otro.

Cada Sistema Operativo tiene diferentes mecanismos de comunicación entre procesos, en Windows hay bastantes mecanismos disponibles, y se dividen en 2 categorías prinpalmente:

  1. Comunicación entre procesos en una misma máquina. Es decir, procesos que no se comunican mediante red.
  2. Comunicación entre procesos que están en diferentes redes/máquinas. Esto conlleva realizar la comunicación mediante la red y por lo tanto, utilizar sockets.

En esta entrada veremos como realizar la comunicación de 2 procesos en una misma máquina utilizando para ello el API de Windows.

En el siguiente enlace tenéis la documentación oficial de Microsoft de los diferentes mecanismos de comunicación disponibles.

Y en estos 2 enlaces la documentación sobre la que hemos basado nuestro post:

Memoria Compartida en Windows

Memoria compartida mediante un nombre en Windows

¿ En qué consiste comunicar procesos mediante memoria compartida ?

Básicamente ambos procesos abren un descriptor hacia una zona de memoria compartida por ambos y pueden tanto escribir como leer. En nuestro post el primer proceso escribirá, el segundo proceso leerá los datos escritos por el primer proceso y enviará un mensaje diciendo que ha recibido los datos.

Nuestro proceso escritor

Veamos el código y posteriormente expliquémoslo:

#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>

struct Persona {
    char* nombre, *apellidos;
    int edad;
    double ordenadores;
};

#define BUF_SIZE 1024
TCHAR szName[] = TEXT("Global\MyFileMapping");

int _tmain()
{
    HANDLE hMapFile;
    void* pBuf;

    hMapFile = CreateFileMapping(
        INVALID_HANDLE_VALUE,    // use paging file
        NULL,                    // default security
        PAGE_READWRITE,          // read/write access
        0,                       // maximum object size (high-order DWORD)
        BUF_SIZE,                // maximum object size (low-order DWORD)
        szName);                 // name of mapping object


    if (hMapFile == NULL)
    {
        _tprintf(TEXT("Could not create file mapping object (%d).\n"),
            GetLastError());
        return 1;
    }
    
    printf("Area de memoria creada para compartir!\n");

    pBuf = (void*)MapViewOfFile(hMapFile,   // handle to map object
        FILE_MAP_ALL_ACCESS, // read/write permission
        0,
        0,
        BUF_SIZE);

    if (pBuf == NULL)
    {
        _tprintf(TEXT("Could not map view of file (%d).\n"),
            GetLastError());

        CloseHandle(hMapFile);

        return 1;
    }

    printf("Puntero hacia la memoria compartida obtenido para escribir en ella\n");

    struct Persona* persona1 = (struct Persona*)malloc(sizeof(struct Persona));
    persona1->nombre = (char*)malloc(30);
    persona1->apellidos = (char*)malloc(50);
    persona1->edad = 32;
    persona1->ordenadores = 4;

    strncpy_s(persona1->nombre,30, "Ivan", strlen("Ivan") + 1);
    strncpy_s(persona1->apellidos, 50, "Ramon Moran", strlen("Ramon Moran") + 1);

    CopyMemory((void*)pBuf, persona1->nombre, 30);
    CopyMemory((char*)pBuf + 30, persona1->apellidos, 50);
    CopyMemory((int*)pBuf + 80, &persona1->edad, sizeof(int));
    CopyMemory((double*)pBuf + 84, &persona1->ordenadores, sizeof(double));

    printf("Datos copiados en la memoria. Pulsa 2 veces intro para leer el mensaje de vuelta y salir del programa\n");

    _getch();

    printf("%s\n", (char*)pBuf);

    free(persona1->nombre);
    free(persona1->apellidos);
    free(persona1);

    UnmapViewOfFile(pBuf);

    CloseHandle(hMapFile);

    return 0;
}

El struct que vamos a enviar es:

struct Persona {
    char* nombre, *apellidos;
    int edad;
    double ordenadores;
};

Definimos el tamaño de la zona de memoria compartida y el nombre de la misma, el nombre se utilizará para que el segundo proceso la pueda abrir:

#define BUF_SIZE 1024
TCHAR szName[] = TEXT("Global\MyFileMapping");

Es decir, el primer proceso registrar la zona de memoria y le asigna un nombre y el segundo proceso abrirá la zona de memoria registrada por el primer proceso.

Creación de la zona de memoria:

HANDLE hMapFile;
void* pBuf;

hMapFile = CreateFileMapping(
    INVALID_HANDLE_VALUE,    // use paging file
    NULL,                    // default security
    PAGE_READWRITE,          // read/write access
    0,                       // maximum object size (high-order DWORD)
    BUF_SIZE,                // maximum object size (low-order DWORD)
    szName);                 // name of mapping object


if (hMapFile == NULL)
{
    _tprintf(TEXT("Could not create file mapping object (%d).\n"),
        GetLastError());
    return 1;
}

printf("Area de memoria creada para compartir!\n");

Mediante la línea 5 especificamos que utilizaremos memoria y no un archivo, ya que también se puede compartir información mediante procesos utilizando ficheros, pero esto es menos eficiente que hacerlo mediante memoria.

Mediante la línea 7 especificamos que será una zona de memoria que servirá tanto para leer como para escribir.

Mediante la línea 9 especificamos el tamaño de la zona de memoria y mediante la línea 10 cuál será su nombre.

Una vez creada la zona de memoria tenemos que obtener un puntero hacía la misma:

pBuf = (void*)MapViewOfFile(hMapFile,   // handle to map object
    FILE_MAP_ALL_ACCESS, // read/write permission
    0,
    0,
    BUF_SIZE);

if (pBuf == NULL)
{
    _tprintf(TEXT("Could not map view of file (%d).\n"),
        GetLastError());

    CloseHandle(hMapFile);

    return 1;
}

printf("Puntero hacia la memoria compartida obtenido para escribir en ella\n");

Ahora ya podemos crear nuestro struct Persona y escribirlo en la zona de memoria compartida:

struct Persona* persona1 = (struct Persona*)malloc(sizeof(struct Persona));
persona1->nombre = (char*)malloc(30);
persona1->apellidos = (char*)malloc(50);
persona1->edad = 32;
persona1->ordenadores = 4;

strncpy_s(persona1->nombre,30, "Ivan", strlen("Ivan") + 1);
strncpy_s(persona1->apellidos, 50, "Ramon Moran", strlen("Ramon Moran") + 1);

CopyMemory((void*)pBuf, persona1->nombre, 30);
CopyMemory((char*)pBuf + 30, persona1->apellidos, 50);
CopyMemory((int*)pBuf + 80, &persona1->edad, sizeof(int));
CopyMemory((double*)pBuf + 84, &persona1->ordenadores, sizeof(double));

printf("Datos copiados en la memoria. Pulsa 2 veces intro para leer el mensaje de vuelta y salir del programa\n");

_getch();

Aquí simplemente copiamos nuestros datos en la zona de memoria compartida. Fijaos que utilizamos aritmética de punteros en cada «CopyMemory».

Por último, leemos lo que nos devuelve el proceso 2 y liberamos la memoria:

printf("%s\n", (char*)pBuf);

free(persona1->nombre);
free(persona1->apellidos);
free(persona1);

UnmapViewOfFile(pBuf);

CloseHandle(hMapFile);

Nuestro proceso lector

Veamos el código:

#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#pragma comment(lib, "user32.lib")

#define BUF_SIZE 1024
TCHAR szName[] = TEXT("Global\MyFileMapping");

int _tmain()
{
    HANDLE hMapFile;
    void* pBuf;

    hMapFile = OpenFileMapping(
        FILE_MAP_ALL_ACCESS,   // read/write access
        FALSE,                 // do not inherit the name
        szName);               // name of mapping object

    

    if (hMapFile == NULL)
    {
        _tprintf(TEXT("Could not open file mapping object (%d).\n"),
            GetLastError());
        return 1;
    }

    printf("Zona de memoria compartida abierta!\n");

    pBuf = (void*)MapViewOfFile(hMapFile, // handle to map object
        FILE_MAP_ALL_ACCESS,  // read/write permission
        0,
        0,
        BUF_SIZE);

    if (pBuf == NULL)
    {
        _tprintf(TEXT("Could not map view of file (%d).\n"),
            GetLastError());

        CloseHandle(hMapFile);

        return 1;
    }

    printf("Puntero hacia la zona de memoria compartida abierto\n");

    printf("%s\n", (char*)pBuf);
    printf("%s\n", (char*)pBuf + 30);
    printf("%d\n", *((int*)pBuf + 80));
    printf("%lf\n", *((double*)pBuf + 84));

    CopyMemory((char*)pBuf, "Recibido", strlen("Recibido") + 1);

    //MessageBox(NULL, (LPCWSTR)pBuf, TEXT("Process2"), MB_OK);

    UnmapViewOfFile(pBuf);

    CloseHandle(hMapFile);

    return 0;
}

Primero abrimos la zona de memoria compartida:

HANDLE hMapFile;
void* pBuf;

hMapFile = OpenFileMapping(
    FILE_MAP_ALL_ACCESS,   // read/write access
    FALSE,                 // do not inherit the name
    szName);               // name of mapping object



if (hMapFile == NULL)
{
    _tprintf(TEXT("Could not open file mapping object (%d).\n"),
        GetLastError());
    return 1;
}

printf("Zona de memoria compartida abierta!\n");

La abrimos con permisos de lectura y escritura y con el nombre mediante el cuál la ha creado nuestro proceso escritor.

Posterior a esto obtenemos el puntero hacia la zona de memoria compartida:

pBuf = (void*)MapViewOfFile(hMapFile, // handle to map object
    FILE_MAP_ALL_ACCESS,  // read/write permission
    0,
    0,
    BUF_SIZE);

if (pBuf == NULL)
{
    _tprintf(TEXT("Could not map view of file (%d).\n"),
        GetLastError());

    CloseHandle(hMapFile);

    return 1;
}

printf("Puntero hacia la zona de memoria compartida abierto\n");

Leemos los datos y los mostramos:

printf("%s\n", (char*)pBuf);
printf("%s\n", (char*)pBuf + 30);
printf("%d\n", *((int*)pBuf + 80));
printf("%lf\n", *((double*)pBuf + 84));

Por último, enviamos un mensaje al proceso A y liberamos los recursos:

CopyMemory((char*)pBuf, "Recibido", strlen("Recibido") + 1);

UnmapViewOfFile(pBuf);

CloseHandle(hMapFile);

La ejecución de ambos procesos

Lanzamos el primer proceso:

Comunicación entre procesos en Windows. Salida al lanzar el primer proceso

Lanzamos el segundo proceso:

Comunicación entre procesos en Windows. Salida al lanzar el segundo proceso. Se observa como se leen los datos

Obtenemos el mensaje de vuelta en el primer proceso:

Comunicación entre procesos en Windows. Recibimos el mensaje de vuelta desde el segundo proceso

COMPARTIR EN REDES SOCIALES

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *