zoukankan      html  css  js  c++  java
  • 用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 = 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 >> 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 = 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 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 );
            }
        }
    }
  • 相关阅读:
    在Spring 中如果Girl要Kiss Boy咋办捏?
    对象的序列化
    HibernateHQL
    Struts 动态FORM实现过程
    对struts一点理解总结
    Hibernate Query Language(HQL)。
    Hibernate中Inverse和Cascade
    Spring 中的内部bean 和集合
    设计模式到底离我们有多远
    Aspx页面转静态页面
  • 原文地址:https://www.cnblogs.com/bohetang/p/4039520.html
Copyright © 2011-2022 走看看