仅当加载 DLL 时,共享 DLL 内存才会保留。 应用程序可以使用 SetSharedMem 和 GetSharedMem 函数访问共享内存。以下示例演示 DLL 入口点函数如何使用文件映射对象来设置可由加载 DLL 的进程共享的内存。
实现共享内存的 DLL
该示例使用文件映射将命名共享内存块映射到加载 DLL 的每个进程的虚拟地址空间中。 为此,入口点函数必须:
- 调用 CreateFileMapping 函数以获取文件映射对象的句柄。 加载 DLL 的第一个进程创建文件映射对象。 后续进程打开现有对象的句柄。 有关详细信息,请参阅 创建File-Mapping对象;
- 调用 MapViewOfFile 函数将视图映射到虚拟地址空间。 这使进程能够访问共享内存;
请注意,虽然可以通过为 CreateFileMapping 的 lpAttributes 参数传入 NULL 值来指定默认安全属性,但可以选择使用 SECURITY_ATTRIBUTES 结构来提供额外的安全性。
// The DLL code
#include <windows.h>
#include <memory.h>
#define SHMEMSIZE 4096
static LPVOID lpvMem = NULL; // pointer to shared memory
static HANDLE hMapObject = NULL; // handle to file mapping
// The DLL entry-point function sets up shared memory using a
// named file-mapping object.
BOOL WINAPI DllMain(HINSTANCE hinstDLL, // DLL module handle
DWORD fdwReason, // reason called
LPVOID lpvReserved) // reserved
{
BOOL fInit, fIgnore;
switch (fdwReason)
{
// DLL load due to process initialization or 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
TEXT("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, // object 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;
// DLL unload due to process termination or 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);
}
// The export mechanism used here is the __declspec(export)
// method supported by Microsoft Visual Studio, but any
// other export method supported by your development
// environment may be substituted.
#ifdef __cplusplus // If used by C++ code,
extern "C" { // we need to export the C interface
#endif
// SetSharedMem sets the contents of the shared memory
__declspec(dllexport) VOID __cdecl SetSharedMem(LPWSTR lpszBuf)
{
LPWSTR lpszTmp;
DWORD dwCount=1;
// Get the address of the shared memory block
lpszTmp = (LPWSTR) lpvMem;
// Copy the null-terminated string into shared memory
while (*lpszBuf && dwCount<SHMEMSIZE)
{
*lpszTmp++ = *lpszBuf++;
dwCount++;
}
*lpszTmp = '\0';
}
// GetSharedMem gets the contents of the shared memory
__declspec(dllexport) VOID __cdecl GetSharedMem(LPWSTR lpszBuf, DWORD cchSize)
{
LPWSTR lpszTmp;
// Get the address of the shared memory block
lpszTmp = (LPWSTR) lpvMem;
// Copy from shared memory into the caller's buffer
while (*lpszTmp && --cchSize)
*lpszBuf++ = *lpszTmp++;
*lpszBuf = '\0';
}
#ifdef __cplusplus
}
#endif
共享内存可以映射到每个进程中的不同地址。 因此,每个进程都有自己的 lpvMem 实例,lpvMem 声明为全局变量,以便它可用于所有 DLL 函数。 该示例假定 DLL 全局数据不共享,因此加载 DLL 的每个进程都有自己的 lpvMem 实例。
请注意,当文件映射对象的最后一个句柄关闭时,将释放共享内存。 若要创建永久性共享内存,需要确保某些进程始终具有文件映射对象的打开句柄。
使用共享内存的进程
以下进程使用上面定义的 DLL 提供的共享内存。 第一个进程调用 SetSharedMem 来编写字符串,而第二个进程调用 GetSharedMem 来检索此字符串。
此过程使用 DLL 实现的 SetSharedMem 函数将字符串“这是一个测试字符串”写入共享内存。 它还启动一个子进程,该进程将从共享内存中读取字符串。
// Parent process
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
extern "C" VOID __cdecl SetSharedMem(LPWSTR lpszBuf);
HANDLE CreateChildProcess(LPTSTR szCmdline)
{
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
BOOL bFuncRetn = FALSE;
// Set up members of the PROCESS_INFORMATION structure.
ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
// Set up members of the STARTUPINFO structure.
ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
siStartInfo.cb = sizeof(STARTUPINFO);
// Create the child process.
bFuncRetn = CreateProcess(NULL,
szCmdline, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
if (bFuncRetn == 0)
{
printf("CreateProcess failed (%)\n", GetLastError());
return INVALID_HANDLE_VALUE;
}
else
{
CloseHandle(piProcInfo.hThread);
return piProcInfo.hProcess;
}
}
int _tmain(int argc, TCHAR *argv[])
{
HANDLE hProcess;
if (argc == 1)
{
printf("Please specify an input file");
ExitProcess(0);
}
// Call the DLL function
printf("\nProcess is writing to shared memory...\n\n");
SetSharedMem(L"This is a test string");
// Start the child process that will read the memory
hProcess = CreateChildProcess(argv[1]);
// Ensure this process is around until the child process terminates
if (INVALID_HANDLE_VALUE != hProcess)
{
WaitForSingleObject(hProcess, INFINITE);
CloseHandle(hProcess);
}
return 0;
}
此过程使用 DLL 实现的 GetSharedMem 函数从共享内存中读取字符串。 它由上述父进程启动。
// Child process
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
extern "C" VOID __cdecl GetSharedMem(LPWSTR lpszBuf, DWORD cchSize);
int _tmain( void )
{
WCHAR cBuf[MAX_PATH];
GetSharedMem(cBuf, MAX_PATH);
printf("Child process read from shared memory: %S\n", cBuf);
return 0;
}