用C#实现的内存映射

时间:2022-12-23 15:29:27

当文件过大时,无法一次性载入内存时,就需要分次,分段的载入文件

主要是用了以下的WinAPI

LPVOID MapViewOfFile(HANDLE hFileMappingObject,

  DWORD dwDesiredAccess,

  DWORD dwFileOffsetHigh,

  DWORD dwFileOffsetLow,

  DWORD dwNumberOfBytesToMap);

  MapViewOfFile() 函数负责把文件数据映射到进程的地址空间,参数hFileMappingObject 为 CreateFileMapping()返回的文件映像对象句柄。参数dwDesiredAccess则再次指定了对文件数据的访问方式,而且同样要与 CreateFileMapping()函数所设置的保护属性相匹配。虽然这里一再对保护属性进行重复设置看似多余,但却可以使应用程序能更多的对数据的保护属性实行有效控制。MapViewOfFile()函数允许全部或部分映射文件,在映射时,需要指定数据文件的偏移地址以及待映射的长度。其中,文件的偏移地址由DWORD型的参数dwFileOffsetHigh和dwFileOffsetLow组成的64位值来指定,而且必须是操作系统的分配粒度的整数倍,对于Windows操作系统,分配粒度固定为64KB。当然,也可以通过如下代码来动态获取当前操作系统的分配粒度:

  SYSTEM_INFO sinf;

  GetSystemInfo(&sinf);

  DWORD dwAllocationGranularity = sinf.dwAllocationGranularity;

  参数dwNumberOfBytesToMap指定了数据文件的映射长度,这里需要特别指出的是,对于Windows 9x操作系统,如果MapViewOfFile()无法找到足够大的区域来存放整个文件映射对象,将返回空值(NULL);但是在Windows 2000下,MapViewOfFile()只需要为必要的视图找到足够大的一个区域即可,而无须考虑整个文件映射对象的大小。

由此看出,分页映射文件时,每页的起始位置startpos,必须为64K的整数倍。

以下贴出源代码,防止忘记了

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices; namespace BlueVision.SaYuan.FileMapping
{
public class ShareMemory
{
[DllImport( "user32.dll", CharSet = CharSet.Auto )]
public static extern IntPtr SendMessage( IntPtr hWnd, int Msg, int wParam, IntPtr lParam ); [DllImport( "Kernel32.dll", CharSet = CharSet.Auto )]
public static extern IntPtr CreateFileMapping( IntPtr hFile, IntPtr lpAttributes, uint flProtect, uint dwMaxSizeHi, uint dwMaxSizeLow, string lpName ); [DllImport( "Kernel32.dll", CharSet = CharSet.Auto )]
public static extern IntPtr OpenFileMapping( int dwDesiredAccess, [MarshalAs( UnmanagedType.Bool )] bool bInheritHandle, string lpName ); [DllImport( "Kernel32.dll", CharSet = CharSet.Auto )]
public static extern IntPtr MapViewOfFile( IntPtr hFileMapping, uint dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, uint dwNumberOfBytesToMap ); [DllImport( "Kernel32.dll", CharSet = CharSet.Auto )]
public static extern bool UnmapViewOfFile( IntPtr pvBaseAddress ); [DllImport( "Kernel32.dll", CharSet = CharSet.Auto )]
public static extern bool CloseHandle( IntPtr handle ); [DllImport( "kernel32", EntryPoint = "GetLastError" )]
public static extern int GetLastError(); [DllImport( "kernel32.dll" )]
static extern void GetSystemInfo( out SYSTEM_INFO lpSystemInfo ); [StructLayout( LayoutKind.Sequential )]
public struct SYSTEM_INFO
{
public ushort processorArchitecture;
ushort reserved;
public uint pageSize;
public IntPtr minimumApplicationAddress;
public IntPtr maximumApplicationAddress;
public IntPtr activeProcessorMask;
public uint numberOfProcessors;
public uint processorType;
public uint allocationGranularity;
public ushort processorLevel;
public ushort processorRevision;
}
/// <summary>
/// 获取系统的分配粒度
/// </summary>
/// <returns></returns>
public static uint GetPartitionsize()
{
SYSTEM_INFO sysInfo;
GetSystemInfo( out sysInfo );
return sysInfo.allocationGranularity;
} const int ERROR_ALREADY_EXISTS = ; const int FILE_MAP_COPY = 0x0001;
const int FILE_MAP_WRITE = 0x0002;
const int FILE_MAP_READ = 0x0004;
const int FILE_MAP_ALL_ACCESS = 0x0002 | 0x0004; const int PAGE_READONLY = 0x02;
const int PAGE_READWRITE = 0x04;
const int PAGE_WRITECOPY = 0x08;
const int PAGE_EXECUTE = 0x10;
const int PAGE_EXECUTE_READ = 0x20;
const int PAGE_EXECUTE_READWRITE = 0x40; const int SEC_COMMIT = 0x8000000;
const int SEC_IMAGE = 0x1000000;
const int SEC_NOCACHE = 0x10000000;
const int SEC_RESERVE = 0x4000000; IntPtr m_fHandle; IntPtr m_hSharedMemoryFile = IntPtr.Zero;
IntPtr m_pwData = IntPtr.Zero;
bool m_bAlreadyExist = false;
bool m_bInit = false;
uint m_MemSize = 0x1400000;//20M
long m_offsetBegin = ;
long m_FileSize = ;
FileReader File = new FileReader(); /// <summary>
/// 初始化文件
/// </summary>
/// <param name="MemSize">缓冲大小</param>
public ShareMemory( string filename, uint memSize )
{
// 分页映射文件时,每页的起始位置startpos,必须为64K的整数倍。
// memSize即缓存区的大小必须是系统分配粒度的整倍说,window系统的分配粒度是64KB
this.m_MemSize = memSize;
Init( filename );
} /// <summary>
/// 默认映射20M缓冲
/// </summary>
/// <param name="filename"></param>
public ShareMemory( string filename )
{
this.m_MemSize = 0x1400000;
Init( filename );
} ~ShareMemory()
{
Close();
} /// <summary>
/// 初始化共享内存
///
/// 共享内存名称
/// 共享内存大小
/// </summary>
/// <param name="strName"></param>
protected void Init( string strName )
{
//if (lngSize <= 0 || lngSize > 0x00800000) lngSize = 0x00800000; if ( !System.IO.File.Exists( strName ) ) throw new Exception( "未找到文件" ); System.IO.FileInfo f = new System.IO.FileInfo( strName ); m_FileSize = f.Length; m_fHandle = File.Open( strName ); if ( strName.Length > )
{
//创建文件映射
m_hSharedMemoryFile = CreateFileMapping( m_fHandle, IntPtr.Zero, ( uint )PAGE_READONLY, , ( uint )m_FileSize, "mdata" );
if ( m_hSharedMemoryFile == IntPtr.Zero )
{
m_bAlreadyExist = false;
m_bInit = false;
throw new Exception( "CreateFileMapping失败LastError=" + GetLastError().ToString() );
}
else
m_bInit = true; ////映射第一块文件
//m_pwData = MapViewOfFile(m_hSharedMemoryFile, FILE_MAP_READ, 0, 0, (uint)m_MemSize);
//if (m_pwData == IntPtr.Zero)
//{
// m_bInit = false;
// throw new Exception("m_hSharedMemoryFile失败LastError=" + GetLastError().ToString());
//} }
}
/// <summary>
/// 获取高32位
/// </summary>
/// <param name="intValue"></param>
/// <returns></returns>
private static uint GetHighWord( UInt64 intValue )
{
return Convert.ToUInt32( intValue >> );
}
/// <summary>
/// 获取低32位
/// </summary>
/// <param name="intValue"></param>
/// <returns></returns>
private static uint GetLowWord( UInt64 intValue )
{ return Convert.ToUInt32( intValue & 0x00000000FFFFFFFF );
} /// <summary>
/// 获取下一个文件块 块大小为20M
/// </summary>
/// <returns>false 表示已经是最后一块文件</returns>
public uint GetNextblock()
{
if ( !this.m_bInit ) throw new Exception( "文件未初始化。" );
//if ( m_offsetBegin + m_MemSize >= m_FileSize ) return false; uint m_Size = GetMemberSize();
if ( m_Size == ) return m_Size; // 更改缓冲区大小
m_MemSize = m_Size; //卸载前一个文件
//bool l_result = UnmapViewOfFile( m_pwData );
//m_pwData = IntPtr.Zero; m_pwData = MapViewOfFile( m_hSharedMemoryFile, FILE_MAP_READ, GetHighWord( ( UInt64 )m_offsetBegin ), GetLowWord( ( UInt64 )m_offsetBegin ), m_Size );
if ( m_pwData == IntPtr.Zero )
{
m_bInit = false;
throw new Exception( "映射文件块失败" + GetLastError().ToString() );
}
m_offsetBegin = m_offsetBegin + m_Size; return m_Size; //创建成功
}
/// <summary>
/// 返回映射区大小
/// </summary>
/// <returns></returns>
private uint GetMemberSize()
{
if ( m_offsetBegin >= m_FileSize )
{
return ;
}
else if ( m_offsetBegin + m_MemSize >= m_FileSize )
{
long temp = m_FileSize - m_offsetBegin;
return ( uint )temp;
}
else
return m_MemSize;
} /// <summary>
/// 关闭内存映射
/// </summary>
public void Close()
{
if ( m_bInit )
{
UnmapViewOfFile( m_pwData );
CloseHandle( m_hSharedMemoryFile );
File.Close();
}
} /// <summary>
/// 从当前块中获取数据
/// </summary>
/// <param name="bytData">数据</param>
/// <param name="lngAddr">起始数据</param>
/// <param name="lngSize">数据长度,最大值=缓冲长度</param>
/// <param name="Unmap">读取完成是否卸载缓冲区</param>
/// <returns></returns>
public void Read( ref byte[] bytData, int lngAddr, int lngSize, bool Unmap )
{
if ( lngAddr + lngSize > m_MemSize )
throw new Exception( "Read操作超出数据区" );
if ( m_bInit )
{
// string bb = Marshal.PtrToStringAuto(m_pwData);//
Marshal.Copy( m_pwData, bytData, lngAddr, lngSize );
}
else
{
throw new Exception( "文件未初始化" );
} if ( Unmap )
{
bool l_result = UnmapViewOfFile( m_pwData );
if ( l_result )
m_pwData = IntPtr.Zero;
}
} /// <summary>
/// 从当前块中获取数据
/// </summary>
/// <param name="bytData">数据</param>
/// <param name="lngAddr">起始数据</param>
/// <param name="lngSize">数据长度,最大值=缓冲长度</param>
/// <exception cref="Exception: Read操作超出数据区"></exception>
/// <exception cref="Exception: 文件未初始化"></exception>
/// <returns></returns>
public void Read( ref byte[] bytData, int lngAddr, int lngSize )
{
if ( lngAddr + lngSize > m_MemSize )
throw new Exception( "Read操作超出数据区" );
if ( m_bInit )
{
Marshal.Copy( m_pwData, bytData, lngAddr, lngSize );
}
else
{
throw new Exception( "文件未初始化" );
}
} /// <summary>
/// 从当前块中获取数据
/// </summary>
/// <param name="lngAddr">缓存区偏移量</param>
/// <param name="byteData">数据数组</param>
/// <param name="StartIndex">数据数组开始复制的下标</param>
/// <param name="lngSize">数据长度,最大值=缓冲长度</param>
/// <exception cref="Exception: 起始数据超过缓冲区长度"></exception>
/// <exception cref="Exception: 文件未初始化"></exception>
/// <returns>返回实际读取值</returns>
public uint ReadBytes( int lngAddr, ref byte[] byteData, int StartIndex, uint intSize )
{
if ( lngAddr >= m_MemSize )
throw new Exception( "起始数据超过缓冲区长度" ); if ( lngAddr + intSize > m_MemSize )
intSize = m_MemSize - ( uint )lngAddr; if ( m_bInit )
{
IntPtr s = new IntPtr( ( long )m_pwData + lngAddr ); // 地址偏移
Marshal.Copy( s, byteData, StartIndex, ( int )intSize );
}
else
{
throw new Exception( "文件未初始化" );
} return intSize;
} /// <summary>
/// 写数据
/// </summary>
/// <param name="bytData">数据</param>
/// <param name="lngAddr">起始地址</param>
/// <param name="lngSize">个数</param>
/// <returns></returns>
private int Write( byte[] bytData, int lngAddr, int lngSize )
{
if ( lngAddr + lngSize > m_MemSize ) return ; //超出数据区
if ( m_bInit )
{
Marshal.Copy( bytData, lngAddr, m_pwData, lngSize );
}
else
{
return ; //共享内存未初始化
}
return ; //写成功
}
}
internal class FileReader
{
const uint GENERIC_READ = 0x80000000;
const uint OPEN_EXISTING = ;
System.IntPtr handle; [DllImport( "kernel32", SetLastError = true )]
public static extern System.IntPtr CreateFile(
string FileName, // file name
uint DesiredAccess, // access mode
uint ShareMode, // share mode
uint SecurityAttributes, // Security Attributes
uint CreationDisposition, // how to create
uint FlagsAndAttributes, // file attributes
int hTemplateFile // handle to template file
); [System.Runtime.InteropServices.DllImport( "kernel32", SetLastError = true )]
static extern bool CloseHandle
(
System.IntPtr hObject // handle to object
); public IntPtr Open( string FileName )
{
// open the existing file for reading
handle = CreateFile
(
FileName,
GENERIC_READ,
,
,
OPEN_EXISTING,
, ); if ( handle != System.IntPtr.Zero )
{
return handle;
}
else
{
throw new Exception( "打开文件失败" );
}
} public bool Close()
{
return CloseHandle( handle );
}
}
}

用C#实现的内存映射的更多相关文章

  1. 内存映射文件MemoryMappedFile使用

    参考资料: http://blog.csdn.net/bitfan/article/details/4438458 所谓内存映射文件,其实就是在内存中开辟出一块存放数据的专用区域,这区域往往与硬盘上特 ...

  2. JAVA NIO FileChannel 内存映射文件

      文件通道总是阻塞式的. 文件通道不能创建,只能通过(RandomAccessFile.FileInputStream.FileOutputStream)getChannel()获得,具有与File ...

  3. Python之mmap内存映射模块(大文本处理)说明

    背景: 通常在UNIX下面处理文本文件的方法是sed.awk等shell命令,对于处理大文件受CPU,IO等因素影响,对服务器也有一定的压力.关于sed的说明可以看了解sed的工作原理,本文将介绍通过 ...

  4. 使用ZwMapViewOfSection创建内存映射文件总结

    标 题: [原创]使用ZwMapViewOfSection创建内存映射文件总结 作 者: 小覃 时 间: 2012-06-15,02:28:36 链 接: http://bbs.pediy.com/s ...

  5. C&num;大文件读取和查询--内存映射

    笔者最近需要快速查询日志文件,文件大小在4G以上. 需求如下: 1.读取4G左右大小的文件中的指定行,程序运行占用内存不超过500M. 2.希望查询1G以内容,能控制在20s左右. 刚开始觉得这个应该 ...

  6. 【转】C&num;大文件读取和查询--内存映射

    笔者最近需要快速查询日志文件,文件大小在4G以上. 需求如下: 1.读取4G左右大小的文件中的指定行,程序运行占用内存不超过500M. 2.希望查询1G以内容,能控制在20s左右. 刚开始觉得这个应该 ...

  7. Atitit&period;病毒木马的快速扩散机制原理nio&&num;160&semi;内存映射MappedByteBuffer

    Atitit.病毒木马的快速扩散机制原理nio 内存映射MappedByteBuffer 1. Java NIO(New Input/Output)1 1.1. 变更通知(因为每个事件都需要一个监听者 ...

  8. Java中用内存映射处理大文件

    在处理大文件时,如果利用普通的FileInputStream 或者FileOutputStream 抑或RandomAccessFile 来进行频繁的读写操作,都将导致进程因频繁读写外存而降低速度.如 ...

  9. linux编程之内存映射

    一.概述                                                   内存映射是在调用进程的虚拟地址空间创建一个新的内存映射. 内存映射分为2种: 1.文件映射 ...

随机推荐

  1. java文件cmd运行出现中文乱码

    今天刚开始学java,使用cmd命令执行java文件的时候,发现中文打出来是一串乱码. 于是就百度了一下,发现一个行之有效的方法. 首先使用命令:javac -encoding utf-8 Hello ...

  2. Google140道面试题

    FQ找来,可能历史比较悠久了,慢慢看. 原文连接:http://www.impactinterview.com/2009/10/140-google-interview-questions/ Goog ...

  3. Robot Framework-Windows版本安装

    Robot Framework-Mac版本安装 Robot Framework-Windows版本安装 Robot Framework-工具简介及入门使用 Robot Framework-Databa ...

  4. Actipro Ribbon For WPF 界面控件免费下载地址

    Actipro Ribbon可以添加ribbon用户界面到你的程序中,功能包含:ribbon大小调整.程序菜单.QAT.嵌入的多种控件.多种布局选项.按键提示.屏幕提示.WPF命令模式用法.多种样式. ...

  5. &lbrack;shell基础&rsqb;——sed命令

    关于sed sed 是一种在线编辑器,它一次处理一行内容.处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓 ...

  6. Linux之Samba的配置

    Samba的配置   对于linux与windows共享,和平共处,我们可以用Samba软件 Samba是一套免费的开源软件,可以在linux或其他类unix操作系统上实现windows域控制器,文件 ...

  7. OC之字符串 NSString与NSMutableString

    一.NSString 不可变字符串的操作1)将字符串常量对象直接赋值给字符串引用 NSString *str1=@"hello"; 字符串对象的输出格式:NSLog(@" ...

  8. 利用System&period;Net&period;Mail和多线程实现邮件发送

    对于邮件发送,一般来说,程序会响应超过1秒,这样对于用户体验来说,让用户等待的时间过长,而且发送的邮件越多时间就越长,所以这里我利用了线程的来处理邮件发送这种耗时的工作,废话不多说,直接上代码 pri ...

  9. malloc&sol;free 和 new&sol;delete

    (本文参考于网上) 首先两者都可用于申请动态内存和释放内存。 对于非内部数据类型的对象而言,只用malloc/free无法满足动态对象的要求.对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执 ...

  10. adb Android Debug Bridge 安卓调试桥

    adb devices 获取设备列表及设备状态 adb get-state 获取设备的状态,设备的状态有 3 钟,device , offline , unknown device:设备正常连接 of ...