首页 > 代码库 > 用C#实现的内存映射

用C#实现的内存映射

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

主要是用了以下的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 = 183;        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 = 0;        long m_FileSize = 0;        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 > 0 )            {                //创建文件映射                m_hSharedMemoryFile = CreateFileMapping( m_fHandle, IntPtr.Zero, ( uint )PAGE_READONLY, 0, ( 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 = http://www.mamicode.com/MapViewOfFile(m_hSharedMemoryFile, FILE_MAP_READ, 0, 0, (uint)m_MemSize);>//if (m_pwData =http://www.mamicode.com/= 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 >> 32 );        }        /// <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 == 0 ) return m_Size;            // 更改缓冲区大小            m_MemSize = m_Size;            //卸载前一个文件            //bool l_result = UnmapViewOfFile( m_pwData );            //m_pwData = http://www.mamicode.com/IntPtr.Zero;            m_pwData = MapViewOfFile( m_hSharedMemoryFile, FILE_MAP_READ, GetHighWord( ( UInt64 )m_offsetBegin ), GetLowWord( ( UInt64 )m_offsetBegin ), m_Size );            if ( m_pwData =http://www.mamicode.com/= 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 0;            }            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 2; //超出数据区            if ( m_bInit )            {                Marshal.Copy( bytData, lngAddr, m_pwData, lngSize );            }            else            {                return 1; //共享内存未初始化            }            return 0; //写成功        }    }    internal class FileReader    {        const uint GENERIC_READ = 0x80000000;        const uint OPEN_EXISTING = 3;        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,                0,                0,                OPEN_EXISTING,                0,                0            );            if ( handle != System.IntPtr.Zero )            {                return handle;            }            else            {                throw new Exception( "打开文件失败" );            }        }        public bool Close()        {            return CloseHandle( handle );        }    }}

 

用C#实现的内存映射