说道内存映射文件,我感觉好多人都对此不是很理解(我也是其中一个。。。),经过不断地学习,我自己对此也有一点小小的理解,(也可以细看我转发的一篇博文,图文并茂,我深受启发http://blog.csdn.net/qq_22642239/article/details/51496988)
使用内存映射文件首先应该创建一个内存映射文件的对象(也就是在你需要映射的文件上建立的一个对象)使用CreateFileMapping函数创建,函数返回一个内存映射文件对象的句柄(假如是hFileMap)。
创建过内存映射文件的对象之后,就应该使用MapViewOfFile函数创建内存映射文件的一个视图,创建成功,函数会返回一个地址,存取这个地址指定的内存块就相当于存取文件的内容了(具体细节可以看一下详细介绍内存映射文件那篇博文),这就是内存映射文件的使用方法。
本次程序编写介绍的的是使用内存映射文件在进程间共享数据(说的通俗点就是运行几个程序,程序对应相应的进程,建立一个内存映射文件共享区域,使每个进程都能利用这个区域的数据,程序中则是在每个进程对应的程序中显示出这个内存共享区域的数据,每个进程也可以在这个区域中写入数据),从一个程序中输入数据,可以在另一个程序中看到这个输入的数据。具体情况如下图:
我在第一个程序的文本编辑控件中输入111111111 在另外两个程序中也能读取到这个111111111,然后在下面那个静态文本框中显示出来。
下面来看一下程序的实现框架:
图标 | IDI_ICON1 |
对话框 | IDD_DIALOG1 |
编辑文本框 | IDC_TXT |
静态文本框1 | IDC_INFO 显示内存共享区域的数据 |
静态文本框2 | 显示提示字符 |
建立内存共享文件 | _CreateMMF() |
解除视图映射,关闭内存映射文件 | _CloseMMF() |
对话框消息处理过程 | _ProcDlgMain() |
程序入口,创建模态对话框 | start: DialogBoxParam() |
首先来看一下资源文件,自己调整好对话框的大小,根据自己的习惯定义对话框的对话框的风格就可以了,这次依然使用的是ResEdit资源编写工具,详细代码如下:
// Generated by ResEdit 1.6.6
// Copyright (C) 2006-2015
// http://www.resedit.net
#include <windows.h>
#include <commctrl.h>
#include <richedit.h>
#include "resource.h"
//
// Dialog resources
//
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDD_DIALOG1 DIALOG 0, 0, 223, 95
STYLE DS_3DLOOK | DS_CENTER | DS_MODALFRAME | DS_SETFONT | WS_CAPTION | WS_VISIBLE | WS_GROUP | WS_TABSTOP | WS_POPUP | WS_THICKFRAME | WS_SYSMENU
CAPTION "内存映射文件共享"
FONT 8, "Ms Shell Dlg"
{
LTEXT "", IDC_INFO, 11, 71, 192, 14, SS_LEFT, WS_EX_LEFT
LTEXT "请执行本程序的多个拷贝,并尝试在下面输入文本:", 0, 13, 13, 185, 9, SS_LEFT, WS_EX_LEFT
EDITTEXT IDC_TXT, 12, 36, 190, 20, ES_AUTOHSCROLL, WS_EX_LEFT
}
//
// Icon resources
//
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDI_ICON1 ICON ".\\icon2.ico"
下面来看一下源代码部分的问题:
首先在创建内存映射文件共享区域的时候,创建了内存映射文件的对象的,以及映射了对象的视图得到指向映射到内存的第一个字节的指针,当不再使用它们的时候,需要手动把它们都处理掉(需要我们自己动手处理。。。)。
还有一个就是以前遇到过的标号问题,这次写的时候居然都忘记了,在这里从新总结一下:
@@ ;标号
@F ;表示匹配本条指令后的第一个@@标号
@B ; 表示匹配本条指令前的第一个@@标号
这个标号只在MASM中有效
下面来看一下程序源代码,注意注释的地方:
.386
.model flat, stdcall
option casemap :none
include windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
IDD_DIALOG1 equ 100
IDI_ICON1 equ 101
IDC_TXT equ 40000
IDC_INFO equ 40001
.data?
hInstance dd ?
hWinMain dd ?
hFileMap dd ?
lpMemory dd ?
.const
szErr db '无法建立内存共将文件',0
szMMFName db 'MMF_Share_Example',0
.code
_CreateMMF proc
invoke OpenFileMapping,FILE_MAP_READ or FILE_MAP_WRITE,0,addr szMMFName
.if ! eax
invoke CreateFileMapping,-1,NULL,PAGE_READWRITE,0,4096,addr szMMFName ; 4096表示的是一个页的大小, win32内存分页机制中一个页的大小 为4kb
.if ! eax
jmp @F
.endif
.endif
mov hFileMap,eax
invoke MapViewOfFile,eax,FILE_MAP_READ or FILE_MAP_WRITE,0,0,0
.if eax
mov lpMemory,eax
mov dword ptr [eax],0
ret
.endif
invoke CloseHandle,hFileMap
@@:
invoke MessageBox,hWinMain,addr szErr,NULL,MB_OK
invoke EndDialog,hWinMain,-1
ret
_CreateMMF endp
_CloseMMF proc
invoke UnmapViewOfFile,lpMemory
invoke CloseHandle,hFileMap
mov lpMemory,0
mov hFileMap,0
ret
_CloseMMF endp
_ProcDlgMain proc uses ebx edi esi hWnd,wMsg,wParam,lParam
LOCAL @szBuffer[4096]:byte
mov eax,wMsg
.if eax == WM_TIMER
invoke SetDlgItemText,hWnd,IDC_INFO,lpMemory
.elseif eax == WM_CLOSE
invoke KillTimer,hWnd,1
invoke _CloseMMF
invoke EndDialog,hWinMain,0
.elseif eax == WM_INITDIALOG
push hWnd
pop hWinMain
invoke LoadIcon,hInstance,IDI_ICON1
invoke SendMessage,hWnd,WM_SETICON,ICON_BIG,eax
invoke SendDlgItemMessage,hWnd,IDC_TXT,EM_SETLIMITTEXT,100,0
invoke _CreateMMF
invoke SetTimer,hWnd,1,200,NULL
.elseif eax == WM_COMMAND
mov eax,wParam
.if ax == IDC_TXT && lpMemory
invoke GetDlgItemText,hWnd,IDC_TXT,lpMemory,4096
.endif
.else
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
_ProcDlgMain endp
start:
invoke GetModuleHandle,NULL
mov hInstance,eax
invoke DialogBoxParam,hInstance,IDD_DIALOG1,NULL,offset _ProcDlgMain,NULL
invoke ExitProcess,NULL
end start
下面来看一下本程序中的几个API函数:
OpenFileMapping()
功能:
打开一个现成的文件映射对象的函数
声明:
HANDLE WINAPI OpenFileMapping(
_In_ DWORD dwDesiredAccess,
_In_ BOOL bInheritHandle,
_In_ LPCTSTR lpName
);
参数:
lpName:指向的名字就是创建对象时使用的名字
dwDesiredAccess:指定保护类型,他可以是以下的取值:
FILE_MAP_WRITE(或FILE_MAP_ALL_ACCESS)-----------可写属性
FILE_MAP_READ--------可读属性
FILE_MAP_COPY-----------Copy on write属性
返回值:
打开的内存映射文件对象的句柄
CreateFileMapping()
功能:
创建一个新的文件映射内核对象。
参数原型:
HANDLE CreateFileMapping(
HANDLE hFile, //物理文件句柄
LPSECURITY_ATTRIBUTES lpAttributes, //安全设置
DWORD flProtect, //保护设置
DWORD dwMaximumSizeHigh, //高位文件大小
DWORD dwMaximumSizeLow, //低位文件大小
LPCTSTR lpName //共享内存名称
);
返回值:
Long,新建文件映射对象的句柄;零意味着出错。会设置GetLastError。即使函数成功,但倘若返回的句柄属于一个现成的文件映射对象,那么GetLastError也会设置成ERROR_ALREADY_EXISTS。在这种情况下,文件映射的长度就是现有对象的长度,而不是这个函数指定的尺寸。
MapViewOfFile()
功能:
将一个文件映射对象映射到当前应用程序的地址空间。MapViewOfFileEx允许我们指
定一个基本地址来进行映射。
原型:
LPVOID WINAPI MapViewOfFile(
__in HANDLE hFileMappingObject,
__in DWORD dwDesiredAccess,
__in DWORD dwFileOffsetHigh,
__in DWORD dwFileOffsetLow,
__in SIZE_T dwNumberOfBytesToMap
);
hFileMappingObject Long,文件映射对象的句柄。
dwDesiredAccess Long,下述常数之一:
FILE_MAP_WRITE 映射可读可写。文件映射对象必须通过PAGE_READWRITE访问创建。
FILE_MAP_READ 映射只读。文件映射对象必须通过PAGE_READ 或 PAGE_READWRITE访问创建。
FILE_MAP_ALL_ACCESS 与FILE_MAP_WRITE相同。
FILE_MAP_COPY 映射时保留写操作的副本。文件映射对象必须用PAGE_WRITECOPY访问在win95下创建
dwFileOffsetHigh Long,文件中映射起点的高32位地址。
dwFileOffsetLow Long,文件中映射起点的低32位地址。
dwNumberOfBytesToMap Long,文件中要映射的字节数。用零映射整个文件映射对象。
lpBaseAddress Long,指定映射文件映射对象的地址。如这个地址处没有足够的内存空间,那么对MapViewOfFileEx的调用会失效。零表示允许windows寻找一个地址。
返回值:
如果成功,则返回映射视图文件的开始地址值。
如果失败,则返回 NULL.可调用GetLastError()查看错误。
UnmapViewOfFile()
功能:
是停止当前程序的一个内存映射
原型:
Declare Function UnmapViewOfFile& Lib "kernel32" (ByVal lpBaseAddress As Long)
参数:
lpBaseAddress Long,指定要解除映射的一个文件映射的基准地址。这个地址是早先用MapViewOfFile函数获得的
返回值:
Long,非零表示成功,零表示失败。会设置GetLastError。
HANDLE WINAPI OpenFileMapping( _In_ DWORD dwDesiredAccess, _In_ BOOL bInheritHandle, _In_ LPCTSTR lpName );