本节将示范如何利用文件映像使得入口点函数创建可被载入该DLL的进程共享的存储。该共享的DLL存储仅存活于DLL载入期间。
示例通过文件映像把一个命名的共享存储块映射到载入DLL的每个进程的虚地址空间中。要达到该目的,入口点函数必须:
调用 CreateFileMapping 函数获取一个文件映像对象的句柄。第一个载入DLL的进程创建文件映像对象,后续的进程仅打开存在对象的句柄。更多信息,参照“文件映像对象”。 通过调用 MapViewOfFile 函数可以把一个view映射到虚地址空间,使得进程能访问共享存储。更多信息,参照创建文件视图(File View)。 (译者注:通过查看MapViewOfFile的详细说明,MapViewOfFile把一个文件的View映射到调用进程的地址空间内;并根据以下代码,我认为View在这里翻译成视图应该是可以的,File View应该表示的是文件的一种查看方式)
// File: DLLSHMEM.C.
// The DLL entry-point function sets up shared memory using
// a named file-mapping object.
#include <windows.h>
#include <memory.h>
#define SHMEMSIZE 4096
static LPVOID lpvMem = NULL; // pointer to shared memory
BOOL DllMain(HINSTANCE hinstDLL, // DLL module handle
DWORD fdwReason, // reason called
LPVOID lpvReserved) // reserved
{
HANDLE hMapObject = NULL; // handle to file mapping
BOOL fInit, fIgnore;
switch (fdwReason)
{
// The DLL is loading due to process
// initialization or a call to LoadLibrary.
case DLL_PROCESS_ATTACH:
// Create a named file mapping object.
hMapObject = CreateFileMapping(
INVALID_HANDLE_VALUE, // use paging file
NULL, // default security attributes
PAGE_READWRITE, // read/write access
0, // size: high 32-bits
SHMEMSIZE, // size: low 32-bits
"dllmemfilemap"); // name of map object
if (hMapObject == NULL)
return FALSE;
// The first process to attach initializes memory.
fInit = (GetLastError() != ERROR_ALREADY_EXISTS);
// Get a pointer to the file-mapped shared memory.
lpvMem = MapViewOfFile(
hMapObject, // to map view of
FILE_MAP_WRITE, // read/write access
0, // high offset: map from
0, // low offset: beginning
0); // default: map entire file
if (lpvMem == NULL)
return FALSE;
// Initialize memory if this is the first process.
if (fInit)
memset(lpvMem, '\0', SHMEMSIZE);
break;
// The attached process creates a new thread.
case DLL_THREAD_ATTACH:
break;
// The thread of the attached process terminates.
case DLL_THREAD_DETACH:
break;
// The DLL is unloading from a process due to
// process termination or a call to FreeLibrary.
case DLL_PROCESS_DETACH:
// Unmap shared memory from the process's address space.
fIgnore = UnmapViewOfFile(lpvMem);
// Close the process's handle to the file-mapping object.
fIgnore = CloseHandle(hMapObject);
break;
default:
break;
}
return TRUE;
UNREFERENCED_PARAMETER(hinstDLL);
UNREFERENCED_PARAMETER(lpvReserved);
}
// SetSharedMem sets the contents of shared memory.
VOID SetSharedMem(LPTSTR lpszBuf)
{
LPTSTR lpszTmp;
// Get the address of the shared memory block.
lpszTmp = (LPTSTR) lpvMem;
// Copy the null-terminated string into shared memory.
while (*lpszBuf)
*lpszTmp++ = *lpszBuf++;
*lpszTmp = '\0';
}
// GetSharedMem gets the contents of shared memory.
VOID GetSharedMem(LPTSTR lpszBuf, DWORD cchSize)
{
LPTSTR lpszTmp;
// Get the address of the shared memory block.
lpszTmp = (LPTSTR) lpvMem;
// Copy from shared memory into the caller's buffer.
while (*lpszTmp && --cchSize)
*lpszBuf++ = *lpszTmp++;
*lpszBuf = '\0';
}
注意 共享存储能被映射到每个进程的不同地址中,所以,每个进程拥有自己的lpvMem参数实例,该参数作为全局变量声明,对DLL中所有函数都可用。该例假定DLL的全局数据不共享,所以,载入每个进程该DLL的进程都有自己的lpvMem实例。
该例中,文件映像对象最后一个句柄关闭时,共享内存将释放,要创建永久的共享内存,DLL可以在首次载入时,通过一个分派处理创建。如果分派处理使用了DLL,而且没有终止,就会得到一个到拒绝释放共享内存的文件映射对象的句柄。