Windows 进程间通信 共享内存(FileMapping)
net高级编程 2010-09-26 15:00:54 阅读44 评论 字号:大中小 订阅
FileMapping用于将存在于磁盘的文件放进一个进程的虚拟地址空间,并在该进程的虚拟地址空间中产生一个区域用于“存放”该文件,这个空间就叫做File View,系统并同时产生一个File Mapping Object(存放于物理内存中)用于维持这种映射关系,这样当多个进程需要读写那个文件的数据时,它们的File View其实对应的都是同一个File Mapping Object,这样做可节省内存和保持数据的同步性,并达到数据共享的目的。
当然在一个应用向文件中写入数据时,其它进程不应该去读取这个正在写入的数据。这就需要进行一些同步的操作。
下边来看一下具体的
API
。
CreateFileMaping
的用法:
HANDLE CreateFileMapping( //
返回
File Mapping Object
的句柄
HANDLE hFile, //
想要产生映射的文件的句柄
LPSECURITY_ATTRIBUTES lpAttributes, //
安全属性(只对
NT
和
2000
生效)
DWORD flProtect, //
保护标致
DWORD dwMaximumSizeHigh, //
在
DWORD
的高位中存放
File Mapping Object //
的大小
DWORD dwMaximumSizeLow, //
在
DWORD
的低位中存放
File Mapping Object //
的大小(通常这两个参数有一个为
0
)
LPCTSTR lpName // File Mapping Object
的名称。
);
1)
物理文件句柄
任何可以获得的物理文件句柄,如果你需要创建一个物理文件无关的内存映射也无妨,将它设置成为
0xFFFFFFFF(INVALID_HANDLE_VALUE)
就可以了
.
如果需要和物理文件关联,要确保你的物理文件创建的时候的访问模式和
"
保护设置
"
匹配,比如
:
物理文件只读,内存映射需要读写就会发生错误。推荐你的物理文件使用独占方式创建。
如果使用
INVALID_HANDLE_VALUE
,也需要设置需要申请的内存空间的大小,无论物理文件句柄参数是否有效
,
这样
CreateFileMapping
就可以创建一个和物理文件大小无关的内存空间给你
,
甚至超过实际文件大小,如果你的物理文件有效,而大小参数为
0
,则返回给你的是一个和物理文件大小一样的内存空间地址范围。返回给你的文件映射地址空间是可以通过复制,集成或者命名得到,初始内容为
0
。
2)
保护设置
就是安全设置
,
不过一般设置
NULL
就可以了
,
使用默认的安全配置
.
在
win2k
下如果需要进行限制
,
这是针对那些将内存文件映射共享给整个网络上面的应用进程使用是
,
可以考虑进行限制
.
3)
高位文件大小
32
位地址空间
,
设置为
0
。
4)
共享内存名称
命名可以包含
"Global"
或者
"Local"
前缀在全局或者会话名空间初级文件映射
.
其他部分可以包含任何除了
()
以外的字符
,
可以参考
Kernel Object Name Spaces.
5)
调用
CreateFileMapping
的时候
GetLastError
的对应错误
ERROR_FILE_INVALID
如果企图创建一个零长度的文件映射
,
应有此报
ERROR_INVALID_HANDLE
如果发现你的命名内存空间和现有的内存映射
,
互斥量
,
信号量
,
临界区同名就麻烦了
ERROR_ALREADY_EXISTS
表示内存空间命名已经存在
使用函数
CreateFileMapping
创建一个想共享的文件数据句柄,然后使用
MapViewOfFile
来获取共享的内存地址,然后使用
OpenFileMapping
函数在另一个进程里打开共享文件的名称,这样就可以实现不同的进程共享数据。
下边是
C#
是对使用的接口函数声明:
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Auto)]
public
static extern IntPtr CreateFile(string lpFileName,
int dwDesiredAccess, int dwShareMode, IntPtr lpSecurityAttributes,
int dwCreationDisposition, int dwFlagsAndAttributes,
IntPtr hTemplateFile);
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Auto)]
public
static extern IntPtr CreateFileMapping(IntPtr hFile,
IntPtr lpAttributes, int flProtect,
int dwMaximumSizeLow, int dwMaximumSizeHigh, string lpName);
[DllImport("kernel32", SetLastError = true)]
public
static extern bool FlushViewOfFile(IntPtr lpBaseAddress,
IntPtr dwNumBytesToFlush);
[DllImport("kernel32", SetLastError = true)]
public
static extern IntPtr MapViewOfFile(
IntPtr hFileMappingObject, int dwDesiredAccess, int dwFileOffsetHigh,
int dwFileOffsetLow, IntPtr dwNumBytesToMap);
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Auto)]
public
static extern IntPtr OpenFileMapping(
int dwDesiredAccess, bool bInheritHandle, string lpName);
[DllImport("kernel32", SetLastError = true)]
public
static extern bool UnmapViewOfFile(IntPtr lpBaseAddress);
[DllImport("kernel32", SetLastError = true)]
public
static extern bool CloseHandle(IntPtr handle);
我们在示例里
Server
端建立的一个
FileMapping,
命名为:
@"Global/MyFileMappingObject"
;
这样我们在
Client
端就可以打开同名的
FileMapping
,这样在
Server
和
Client
之前进行通信。每次
Server
将数据写入后,我们通过
Message
的方式通知
Client
端数据已经准备好,可以读取了。(关于
Message
我们之前说过,不在细说。)
具体看一下代码:
在看具体的代码这之前,先看一下其中用到的一些常量定义:
[Flags]
public enum MapProtection
{
PageNone = 0x00000000,
// protection - mutually exclusive, do not or
PageReadOnly = 0x00000002,
PageReadWrite = 0x00000004,
PageWriteCopy = 0x00000008,
// attributes - or-able with protection
SecImage = 0x01000000,
SecReserve = 0x04000000,
SecCommit = 0x08000000,
SecNoCache = 0x10000000,
}
public enum MapAccess
{
FileMapCopy = 0x0001,
FileMapWrite = 0x0002,
FileMapRead = 0x0004,
FileMapAllAccess = 0x001f,
}
public
const short FILE_ATTRIBUTE_NORMAL = 0x80;
public
const short INVALID_HANDLE_VALUE = -1;
public
const uint GENERIC_READ = 0x80000000;
public
const uint GENERIC_WRITE = 0x40000000;
public
const uint FILE_SHARE_READ = 0x00000001;
public
const uint CREATE_NEW = 1;
public
const uint CREATE_ALWAYS = 2;
public
const uint OPEN_EXISTING = 3;
Windows Message:
public
const int WM_USER = 0x0400;
public
const int WM_USER_DATAREADY = WM_USER + 101;
下边是
Server
端的:
创建
MapView:
fileHandle = Win32Wrapper.CreateFileMapping(
IntPtr.Zero, IntPtr.Zero,
(int)(MapProtection.PageReadWrite | MapProtection.SecCommit),
0, 1000000, Win32Wrapper.MappingFileName);
mappingHandle = Win32Wrapper.MapViewOfFile(
fileHandle, (int)MapAccess.FileMapWrite, 0, 0, new IntPtr(1024));
if
(mappingHandle == IntPtr.Zero)
{
MessageBox.Show("Open mapping file failed!");
}
写入信息并通知
Client
端:
private
void btnSend_Click(object sender, EventArgs e)
{
//Write message
string message = string.IsNullOrEmpty(this.sendTxt.Text) ?
"no message" : this.sendTxt.Text;
byte[] source = Encoding.ASCII.GetBytes(message);
byte[] msg = new byte[1024];
Array.Copy(source, msg, source.Length);
Marshal.Copy(msg, 0, mappingHandle, msg.Length);
Win32Wrapper.FlushViewOfFile(mappingHandle, new IntPtr(1024));
//Send message to client
IntPtr handle = GetClientMainFormHandle();
if (handle != IntPtr.Zero)
Win32Wrapper.SendMessage(
handle, Win32Wrapper.WM_USER_DATAREADY,
IntPtr.Zero, IntPtr.Zero);
}
得到
Client
端主窗体句柄的代码:
private
IntPtr GetClientMainFormHandle()
{
string name =
@"CSharp.MultiProcess.Communication.FileMappingClient";
Process process = GetProcessByName(name);
return process.MainWindowHandle;
}
private Process GetProcessByName(string processName)
{
Process[] processes = Process.GetProcesses();
foreach (Process p in processes)
{
//just for debug
//this method has some question because
//the visual studio started process name
//is not same with the release. so yan can
//close visual studio to test the project.
//normal, you should use
//if(p.ProcessName == processName)
//debug
if (p.ProcessName.StartsWith(processName))
return p;
//release
if (p.ProcessName == processName)
return p;
}
return null;
}
关闭
FileMapping:
private
void Server_FormClosing(
object sender, FormClosingEventArgs e)
{
if (mappingHandle != IntPtr.Zero)
{
Win32Wrapper.UnmapViewOfFile(mappingHandle);
Win32Wrapper.CloseHandle(mappingHandle);
}
if (fileHandle != IntPtr.Zero)
Win32Wrapper.CloseHandle(fileHandle);
}
Client
端代码:
当有
Message
收到时接收发送的数据:
protected
override void WndProc(ref Message m)
{
switch (m.Msg)
{
case Win32Wrapper.WM_USER_DATAREADY:
RecevieMessage();
break;
default:
base.WndProc(ref m);
break;
}
}
private void RecevieMessage()
{
IntPtr handle = Win32Wrapper.OpenFileMapping(
(int)MapProtection.PageReadWrite, false,
Win32Wrapper.MappingFileName);
IntPtr mappingFile = Win32Wrapper.MapViewOfFile(
handle, (int)MapAccess.FileMapRead,
0, 0, new IntPtr(1024));
//Marshal.GetLastWin32Error();
byte[] msg = new byte[1024];
Marshal.Copy(mappingFile, msg, 0, 1024);
string message = Encoding.ASCII.GetString(msg);
this.listBox1.Items.Add(message);
Win32Wrapper.UnmapViewOfFile(mappingFile);
Win32Wrapper.CloseHandle(mappingFile);
Win32Wrapper.CloseHandle(handle);
}
转载请注明原文地址: https://ibbs.8miu.com/read-2235575.html