Windows进程通信(2)--使用内存映射文件

转自:http://blog.csdn.net/xiaobai1593/article/details/7488351

内存映射文件是通过两个或多个进程映射同一个文件映射对象的视图来实现的,这意味着它们将共享物理存储器的同一个页

面。因此,当一个进程将数据写入一个共享文件映射

对象的视图时,其他进程可以立即看到它们视图中的数据变更情况。如果多个进程共享单个文件映射对象,那么所有进程必须

使用相同的名字来表示该文件映射对象。

附:

下图展示了Windows内存管理API的体系结构:

Windows内存管理API体系结构

附图:Windows内存管理API体系结构(转自:http://www.cppblog.com/woaidongmao/archive/2011/08/12/153184.html

(1)数据发送进程

在数据发送进程中,程序应首先调用CreateFileMapping()函数创建一个命名的文件映射内核对象,得到相应的文件映射内核

对象句柄hFileMapping;如果打开成功,则调用MapViewOfFile()函数将文件映射内核对象hFileMapping映射到当前应用程

序的地址空间,即返回一个指针;之后就可以像使用本地指针一样使用它;最后使用UnmapViewOfFile()函数解除当前应用程

序的内存地址空间对文件映射内核对象的映射,传入参数为lpBuffer。

实现代码如下:

#include <windows.h>
#include <stdio.h>
#include <string.h>
void main() 
{
//创建一个有名的共享内存 
HANDLE hFileMapping = CreateFileMapping(HANDLE(0xFFFFFFFF), //0xFFFFFFFF表示创建一个进程间共享的对象 
NULL,
PAGE_READWRITE, //读写共享 
0,
100, //共享区间大小 
"mySharedMemory"); //映射文件名,即共享内存的名称 
if (NULL==hFileMapping) 
{ 
if(ERROR_ALREADY_EXISTS==GetLastError()) 
{
printf("Already exists!"); 
} 
else
{
printf("Create Sheared Memory unsuccessfully!"); 
} 
return; 
} 
//映射到本进程的地址空间 
char* pLocalMem = (char*)MapViewOfFile(hFileMapping, FILE_MAP_WRITE, 0, 0, 100); 
if (NULL == pLocalMem) 
{
printf("Get Share memory unsuccessfully!"); 
return; 
} 
//写入到内存中
strcpy(pLocalMem, "I love china! -Bai YongHui");
//取消本进程地址空间的映射
UnmapViewOfFile(pLocalMem); 
pLocalMem = NULL; 
} 

(2)数据接收进程

在数据接收进程中,首先调用OpenFileMapping()函数打开一个命名的文件映射内核对象,得到相应的文件映射内核对象句柄

hFileMapping;如果打开成功,则调用MapViewOfFile()函数映射对象的一个视图,将文件映射内核对象hFileMapping映射

到当前应用程序的进程地址pLocalMem,通过该指针可以读写共享的内存区域;最后调用UnmapViewOfFile()函数来解除视

图映射,传入参数为pLocalMem;调用CloseHandle()函数来关闭文件映射内核空间,传入参数为hFileMapping。

代码实现如下:

#include <windows.h>
#include <string.h>
#include <stdio.h>
void main() 
{
	HANDLE hFileMapping = OpenFileMapping(FILE_MAP_READ, false, "mySharedMemory"); 
	if (NULL == hFileMapping) 
	{ 
		printf("Open share memory unsuccessfully!"); 
		return; 
	} 
	//获得共享内存句柄
	char * pLocalMem = (char*)MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, 100); 
	if (NULL == pLocalMem) 
	{ 
		printf("Open share memory unsuccessfully!"); 
		return; 
	} 
	printf("文件映射中的内容为%s\n", pLocalMem); 
	//取消本进程地址空间的映射; 
	UnmapViewOfFile(pLocalMem);
	pLocalMem=NULL; 
	//关闭文件映射内核文件
	CloseHandle(hFileMapping); 
} 

3.几个主要的函数解析:

1. CreateFileMapping(). 函数功能:创建文件映射内核对象;

函数原型:HANDLE CreateFileMapping( HANDLE hFile, LPSECURITY_ATTRIBUTES lpFileMappingAttributes,

DWORD flProtect, DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCTSTR lpName );

参数:

1> hFile指定欲在其中创建映射的一个文件句柄。通常取值:(HANDLE)0xFFFFFFFF(-1,即INVALID_HANDLE_VALUE)

表示在页面文件中创建一个可共享的文件映射。

2> lpFileMappingAttributes忽略,必须取值为NULL;

3> flProtect文件映射所需的访问权限,可以取值如下:PAGE_READONLY:赋予只读权限。如果进行写入或执行操作,会导

致非法访问;注意:hFile所指定的文件必须在创建时具有GENERIC_READ权限;PAGE_READWRITE:赋予读写权限,

hFile所指定的文件必须在创建时具有GENERIC_READ and GENERIC_WRITE权限;PAGE_WRITECOPY:不支持

4> dwMaximumSizeHigh 指明文件映射的最大长度的高32位

5> dwMaximumSizeLow 指明文件映射的最大长度的低32位。如果该参数和dwMaximumSizeHigh都是0,则文件映射内

核对象最大可以取hFile所指明文件的现有大小;

6> lpName指定文件映射对象的名字。如存在这个名字的一个映射,函数会用flProtect权限试图去打开它。返回值:如果成

功,则返回一个文件映射内核对象句柄;(如果该句柄已经存在,则返回已存在的句柄,并且GetLastError()返回

ERROR_ALREADY_EXISTS错误)如果失败,则返回NULL,可以调用GetLastError()获取更详细的错误信息;

2. OpenFileMapping(). 函数功能:打开一个现有的文件映射对象。

函数原型:HANDLE WINAPI OpenFileMapping( __in DWORD dwDesiredAccess, __in BOOL bInheritHandle, __in

LPCTSTR lpName);

参数:

1> dwDesiredAccess 指明打开文件映射内核对象的访问权限,与CreateFileMapping()中的参数取值相同;

2> bInheritHandle 如果取值为TRUE,则这个函数返回的句柄能由当前进程启动的新进程继承;否则返回的句柄不能被子进

程继承;

3> lpName 要打开的文件映射内核对象的名字。如果该名字对应的文件映射内核对象已经被打开,而且 dwDesiredAccess

指定的安全权限不冲突的话,可以成功打开返回值:如果成功,则返回一个打开的文件映射句柄;失败则返回NULL,可以调用

GetLastError()获取详细的错误信息;

3. MapViewOfFile().函数功能:将一个文件映射内核对象映射到调用进程的地址空间中,并返回映射到进程地址中的起始址;

函数原型如下:LPVOID MapViewOfFile( HANDLE hFileMappingObject, DWORD dwDesiredAccess, DWORD

dwFileOffsetHigh, DWORD dwFileOffsetLow, DWORD dwNumberOfBytesToMap );

参数:

1> hFileMappingObject 指向一个已经打开的文件映射内核对象(通过CreateFileMapping()函数返回)

2> dwDesiredAccess 指明访问文件映射内核对象的权限,可以取如下值: FILE_MAP_WRITE 既可读,亦可写(文件映射

内核对象必须有PAGE_READWRITE保护权限) FILE_MAP_READ 只读性的访问(文件映射内核对象必须有

PAGE_READWRITE保护权限) FILE_MAP_ALL_ACCESS Same as FILE_MAP_WRITE

3> dwFileOffsetHigh 指明文件中映射起点的高32位地址

4> dwFileOffsetLow 指明文件中映射起点的低32位地址高32位和低32位的结合指明一个基地址,从而可使文件映射内核对象

映射到以该地址为起点的进程空间中;

5> dwNumberOfBytesToMap 指定将文件映射内核对象的多少字节映射到进程地址中;如果取值为0,则整个文件映射对象

被映射进来。返回值:如果成功,则返回映射到进程空间的起始地址;失败则返回NULL,可以调用GetLastError()来获取详细

的错误信息;

4. UnmapViewOfFile().函数功能:在当前应用程序的内存地址空间解除对一个文件映射对象的映射。

函数原型:BOOL UnmapViewOfFile( LPCVOID lpBaseAddress );

参数:lpBaseAddress:指出文件映射内核对象映射到进程中的基地址

返回值:如果成功,则返回非零值(即TRUE);如果失败,则返回0(即FALSE);

4.C++控制台下内存映射文件实现进程通信源码

//send.cpp
#include <stdio.h>
#include <iostream>
#include <windows.h>
using namespace std;
int main(int argc,char **argv){
	HANDLE hMapFile =CreateFileMapping(
		INVALID_HANDLE_VALUE,
		NULL,
		PAGE_READWRITE,
		0,
		4*1024,
		"ShareFile"
		);
	if(hMapFile == NULL){
		cout << "分配内存空间出错" << endl;
		return 0;
	}
	LPVOID lpMapAddress = MapViewOfFile(
		hMapFile,
		FILE_MAP_ALL_ACCESS,
		0,
		0,
		0
		);
	if(lpMapAddress ==  NULL){
		cout << "申请内存失败" << endl;
		return 0;
	}
	char buf[4096];
	cin >> buf;
	lstrcpy((char*)lpMapAddress,buf);
	int i = 0;
here:
	cin >> i;
	if(i == 0){
		goto here; 
	}
	UnmapViewOfFile(lpMapAddress);
	return 0;
}
//recv.cpp
#include <stdio.h>
#include <iostream>
#include <windows.h>
using namespace std;
int main(int argc,char **argv){
	HANDLE hMapFile = OpenFileMapping(
		FILE_MAP_ALL_ACCESS,
		false,
		"ShareFile"
		);
	if(hMapFile == NULL){
		cout << "获取内存映射文件失败" << endl;
		return 0;
	}
	LPVOID lpMapAddress = MapViewOfFile(
		hMapFile,
		FILE_MAP_ALL_ACCESS,
		0,
		0,
		0
		);
	if(lpMapAddress == NULL){
		cout << "内存映射文件申请失败" << endl;
		return 0;
	}
	cout << (char *)lpMapAddress << endl;
	UnmapViewOfFile(lpMapAddress);
	return 0;
}
posted on 2013-02-06 23:17  qinfengxiaoyue  阅读(3329)  评论(0编辑  收藏  举报