在同一个可执行文件或dll的多个实例间共享静态数据
1. 创建自己的段:
#pragma data_seg("Shared")
LONG g_lApplocatinInstance = 0;
#pragma data_seg()
当编译器编译这段代码的时候,会创建一个名为Shared的段,并将pragma指示符之间所有带有初始值的变量放在这个段中。
变量后面的#pragma data_seg()告诉编译器停止把后面的变量放到Shared段中,而是重新开始把它们放回到默认的数据段中。
在visual C++中,编译器还提供了一个allocate声明符,它允许我们将未经初始化的数据放到任何我们想要放的段中。
_declspec(allocate(“Share”)) int d;
为了共享变量,仅仅告诉编译器把变量放到单独的段中是不够的。我们还必须告诉链接器要共享这个段中的变量。
#pragma comment(linker, "/Section:Shared,RWS")
逗号后面用来指定想要的属性:R表示READ, W表示WRITE, E表示EXECUTE, S表示SHARED.
使用内存映射文件
要使用内存映射文件,需要执行下面三个步骤。
(1) 创建或打开一个文件内核对象,该对象标识了我们想要用做内存映射文件的那个磁盘文件。
(2) 创建一个文件映射内核对象来告诉系统文件的大小以及我们打算如何访问文件。
(3) 告诉系统把文件映射对象的部分或全部映射到进程的地址空间中。
用完内存映射文件之后,必须执行下面三个步骤来做清理工作。
(1) 告诉系统从进程地址空间中取消对文件映射内核对象的映射。
(2) 关闭文件映射内核对象。
(3) 关闭文件内核对象。
HANDLE WINAPI CreateFileMapping(
__in HANDLE hFile,
__in LPSECURITY_ATTRIBUTES lpAttributes,
__in DWORD flProtect,
__in DWORD dwMaximumSizeHigh,
__in DWORD dwMaximumSizeLow,
__in LPCTSTR lpName
);
dwMaximumSizeHigh,dwMaximumSizeLow参数:对于小于4GB的文件来说,dwMaximumSizeHigh始终为0。
如果想要用当前的文件大小创建一个文件映射对象,那么只要传0给这两个参数就可以了。
如果想要读取文件或在不改变文件大小的前提下访问文件,那么同样需要传0给这两个参数。
如果想要给文件追加数据,那么在选择文件最大大小的时候应该留有余地。
如果当前磁盘上的文件大小为0字节,就不能传两个0,这样就相当于告诉系统我们想要一个大小为0的文件映射对象。这时CreateFileMapping函数会认为这样是错误的而返回NULL.
下面的伪代码是一个使用内核映射文件的例子:
HANDLE hFile = CreateFile(…);
HANDLE hFileMapping = CreateFileMapping(hFile, …..);
PVOID pvFile = MapViewOfFile(hFileMapping, ….);
…….
UnmapViewOfFile(pvFile);
ColseHandle(hFileMapping);
CloseHandle(hFile);
例子:
#include <windows.H>
DWORD dwFileSize;
int main(void)
{
HANDLE h = CreateFile("C:\\Users\\Mzf\\Desktop\\1.txt",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if(INVALID_HANDLE_VALUE != h)
{
dwFileSize = GetFileSize(h, NULL);
//创建文件映射内核对象,大小为文件大小 + 最后末尾一个 0
//要给文件追加数据,那么在选择文件最大大小的时候应该留有余地。
//_strrev()函数要求最后以 0 结尾。
HANDLE hMapping = CreateFileMapping(h, NULL, PAGE_READWRITE, 0,
dwFileSize + sizeof(TCHAR) , NULL);
if(NULL != hMapping)
{
PVOID pvFile = MapViewOfFile(hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if(pvFile == NULL)
{
MessageBox(NULL, "fuck", "fuck", MB_OK);
return -1;
}
PSTR pvAnsi = (PSTR)pvFile;
//文件结尾添加 0
pvAnsi[dwFileSize / sizeof(TCHAR)] = 0;
//逆转字符串
_strrev(pvAnsi);
//在一个文本文件中,每一行的末尾时一个回车符’\r’后跟一个换行符’\n’。
//调用_strrev(pvAnsi)时这些字符也被逆转
//所以必须把每一对”\n\r”换回原来的”\r\n”
pvAnsi = strstr(pvAnsi, "\n\r");
while(pvAnsi != NULL)
{
*pvAnsi++ = '\r';
*pvAnsi++ = '\n';
pvAnsi = strstr(pvAnsi, "\n\r");
}
UnmapViewOfFile(pvFile);
}
CloseHandle(hMapping);
}
//_strrev(pvAnsi)不会把终止符0颠倒
//把文件指针定位到原有文件的大小为止,以截掉末尾的 0
SetFilePointer(h, dwFileSize, NULL, FILE_BEGIN);
SetEndOfFile(h);
CloseHandle(h);
return 0;
}
注:本例只对ANSI字符适用。
以页交换文件为后备存储器的内存映射文件
到目前为止,可通过前面讨论的技术映射磁盘文件的视图。许多应用程序会在运行的时候创建一些数据,并需要将这些数据传输给其他进程,或与其他进程共享这些数据。如果为了共享数据而必须让应用程序在磁盘上创建数据文件并把数据保存在文件中,那将非常不方便。
微软意识到了这一点,并加入了相应的支持,让系统能够创建以页交换文件为后备存储器的内存映射文件,这样就不需要用磁盘上专门的文件来作为后备存储器了。
static HANDLE s_hFileMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 4 * 1024, TEXT("mzf"));
PVOID pVoid = MapViewOfFile(s_hFileMap, FILE_MAP_READ |FILE_MAP_WRITE, 0, 0, 0);
Edit_GetText(GetDlgItem(hwnd, IDC_EDIT1), (LPTSTR)pVoid, 4*1024);
……
在别的进程中:
HANDLE hFileMapT = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, TEXT("mzf"));
PVOID pVoid = MapViewOfFile(hFileMapT, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
Edit_SetText(GetDlgItem(hwnd, IDC_EDIT1), (PTSTR)pVoid);