Использование разделяемой памяти в динамически подключаемой библиотеке (DLL)
Пример ниже демонстрирует, как функция точки входа в DLL может использовать объект "проекция файла в память", чтобы установить память, которая может быть совместно использоваться процессами, загружающими DLL. Разделяемая память DLL сохраняется только до тех пор, пока DLL загружена. Приложения могут использовать функции SetSharedMem и GetSharedMem, чтобы получить доступ к совместно используемой памяти.
Пример использует проекцию файла в память, чтобы отобразить блок именованной совместно используемой памяти в виртуальное адресное пространство каждого процесса, который загружает DLL. Чтобы сделать это, функция точки входа должна:
Обратите внимание! на то, что пока Вы можете определить заданные по умолчанию атрибуты защиты, передавая значение ПУСТО (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 и не завершает работу, то он имеет дескриптор объекта "проекция файла в память", который препятствует освобождению совместно используемой памяти.