Использование разделяемой памяти в динамически подключаемой библиотеке (DLL)


Пример ниже демонстрирует, как функция точки входа в DLL может использовать объект "проекция файла в память", чтобы установить память, которая может быть совместно использоваться процессами, загружающими DLL. Разделяемая память DLL сохраняется только до тех пор, пока DLL загружена. Приложения могут использовать функции SetSharedMem и GetSharedMem, чтобы получить доступ к совместно используемой памяти.

Пример использует проекцию файла в память, чтобы отобразить блок именованной совместно используемой памяти в виртуальное адресное пространство каждого процесса, который загружает DLL. Чтобы сделать это, функция точки входа должна:

  1. Вызвать функцию CreateFileMapping, чтобы получить дескриптор объекта "проекция файла в память". Первый процесс, который загружает DLL, создает объект "проекция файла в память". Последующие процессы открывают дескриптор существующего объекта. Дополнительную информацию, см. в статье Создание объекта "проецируемый файл".
  2. Вызвать функцию MapViewOfFile, чтобы проецировать представление в виртуальное адресное пространство. Это дает возможность процессу получить доступ к совместно используемой памяти. Дополнительную информацию, см. в статье Создание представления данных файла.
Обратите внимание! на то, что пока Вы можете определить заданные по умолчанию атрибуты защиты, передавая значение ПУСТО (NULL) для параметра lpAttributes функции CreateFileMapping, Вы может выбирать для использования структуру SECURITY_ATTRIBUTES, чтобы обеспечить дополнительную защиту.

 

// Функция точки входа в DLL подготавливает разделяемую 
// память используя именованный объект "проекция файла 
// в память". 

#include <windows.h> 
#include <memory.h> 
 
#define SHMEMSIZE 4096 
 
static LPVOID lpvMem = NULL;      // указатель на разделяемую
                                  // память
static HANDLE hMapObject = NULL;  // дескриптор отображенного
                                  // файла
 
BOOL DllMain(HINSTANCE hinstDLL,  // дескриптор модуля DLL
    DWORD fdwReason,              // причина вызова 
    LPVOID lpvReserved)           // зарезервирован 
{ 
    BOOL fInit, fIgnore; 
 
    switch (fdwReason) 
    { 
        // DLL загружается соответствующим процессом 
        // инициализации или вызовом LoadLibrary. 
 
          case DLL_PROCESS_ATTACH: 
 
            // Создаем именованный объект "проекция файла".
 
            hMapObject = CreateFileMapping( 
                INVALID_HANDLE_VALUE, // используем файл подкачки
                NULL,                 // атрибуты защиты по умолчанию
                PAGE_READWRITE,       // доступ к чтению/записи
                0,                    // размер: старшие 32 бита
                SHMEMSIZE,            // размер: младшие 32 бита
                "dllmemfilemap");     // имя объекта отображения
            if (hMapObject == NULL) 
                return FALSE; 
 
            // Первый процесс, который присоединяем к 
            // инициализируемой памяти.
 
            fInit = (GetLastError() != ERROR_ALREADY_EXISTS); 
 
            // Получим указатель на отображение файла
            // в разделяемой памяти.
 
            lpvMem = MapViewOfFile( 
                hMapObject,     // объект представления проецирования
                FILE_MAP_WRITE, // доступ к чтению записи
                0,              // старшее смещение:  от отображения
                0,              // младшее смещение:   начало
                0);             // по умолчанию: проекция всего файла            if (lpvMem == NULL) 
                return FALSE; 
 
            // Инициализация памяти, если она для первого процесса.
 
            if (fInit) 
                memset(lpvMem, '\0', SHMEMSIZE); 
 
            break; 
 
        // Связанный процесс создает новый поток. 
 
        case DLL_THREAD_ATTACH: 
            break; 
 
        // Поток связанного процесса заканчивает работу.
 
        case DLL_THREAD_DETACH: 
            break; 
 
        // DLL выгружается из процесса вследствие  
        // завершение работы процесса или вызова FreeLibrary. 

        case DLL_PROCESS_DETACH: 
 
            // Отменяет отображение совместно используемой памяти
            // от адресного пространства процесса.
 
            fIgnore = UnmapViewOfFile(lpvMem); 
 
            // Закрываем дескриптор процесса объекта 
            // "проекция файла в память".
 
            fIgnore = CloseHandle(hMapObject); 
 
            break; 
 
        default: 
          break; 
     } 
 
    return TRUE; 
    UNREFERENCED_PARAMETER(hinstDLL); 
    UNREFERENCED_PARAMETER(lpvReserved); 
} 
 
// SetSharedMem устанавливает содержание совместно
// используемой памяти. 
 
VOID SetSharedMem(LPSTR lpszBuf) 
{ 
    LPSTR lpszTmp; 
 
    // Получим адрес блока совместно используемой памяти.
 
    lpszTmp = (LPSTR) lpvMem; 
 
    // Копируем символьную строку с нулем в конце в
    // совместно используемую память.
 
    while (*lpszBuf) 
        *lpszTmp++ = *lpszBuf++; 
    *lpszTmp = '\0'; 
} 
 
// GetSharedMem получает содержание совместно
// используемой памяти. 
 
VOID GetSharedMem(LPSTR lpszBuf, DWORD cchSize) 
{ 
    LPSTR lpszTmp; 
 
    // Получаем адрес блока совместно используемой памяти.
 
    lpszTmp = (LPSTR) lpvMem; 
 
    // Копируем совместно используемую память в буфер
    // вызывающей программы.
 
    while (*lpszTmp && --cchSize) 
        *lpszBuf++ = *lpszTmp++; 
    *lpszBuf = '\0'; 
} 

Обратите внимание! на то, что то, что совместно используемая память, может быть отображаемой по разным адресам в каждом процессе. По этой причине, каждый процесс имеет свой собственный экземпляр параметра lpvMem, который объявляется как глобальная переменная так, чтобы она была доступна всем функциям DLL. В примере предполагается, что глобальные данные DLL не разделяются, так что каждый процесс, который загружается, DLL имеет свой собственный экземпляр lpvMem.

В этом примере, совместно используемая память освобождается тогда, когда закрывается последний дескриптор объекта "проекция файла в память". Чтобы создать постоянную совместно используемую память, DLL может создать отдельный процесс (см. CreateProcess), когда DLL загружается впервые. Если этот отдельный процесс использует DLL и не завершает работу, то он имеет дескриптор объекта "проекция файла в память", который препятствует освобождению совместно используемой памяти.

Назад в оглавление
На главную страницу

Hosted by uCoz