C# 读取内存映射文件或内存映射视图访问器的所有内容,而不知道其大小

C# 读取内存映射文件或内存映射视图访问器的所有内容,而不知道其大小,c#,.net,ipc,accessor,memory-mapped-files,C#,.net,Ipc,Accessor,Memory Mapped Files,我需要类似ReadToEnd或ReadAllBytes的东西来使用MappedViewAccessor读取MemoryMappedFile的所有内容如果我不知道它的大小,我该怎么做 我已经找到了,我看到了这个问题,但这不是我要找的东西: 编辑: 有一个问题,(int)stream.Length没有给出正确的长度,而是给出了使用的内部缓冲区的大小!我需要刷新这个问题,因为它非常紧迫。而是使用流: public static Byte[] ReadMMFAllBytes(string fileNa

我需要类似ReadToEnd或ReadAllBytes的东西来使用MappedViewAccessor读取MemoryMappedFile的所有内容如果我不知道它的大小,我该怎么做

我已经找到了,我看到了这个问题,但这不是我要找的东西:

编辑:

有一个问题,(int)stream.Length没有给出正确的长度,而是给出了使用的内部缓冲区的大小!我需要刷新这个问题,因为它非常紧迫。

而是使用流:

public static Byte[] ReadMMFAllBytes(string fileName)
{
    using (var mmf = MemoryMappedFile.OpenExisting(fileName))
    {
        using (var stream = mmf.CreateViewStream())
        {
            using (BinaryReader binReader = new BinaryReader(stream))
            {
                return binReader.ReadBytes((int)stream.Length);
            }
        }
    }
}
你不能那样做

视图访问器是以系统页面的最小大小创建的,这意味着它可能比实际文件大。视图流只是访问器的流形式,因此它也会给出相同的行为

“视图以系统页面为单位提供,并且视图的大小 向上舍入到下一个系统页面大小“

访问者将乐于在实际文件之外进行读写,而不会抛出异常。读取时,文件外的所有字节都将为零。写入时,写入文件外部的字节将被忽略


要从内存映射文件中读取与原始文件大小完全相同的文件,您必须知道该文件的大小。

这很难回答,因为您还没有指定应用程序的许多细节,但我认为Guffa和Amer的回答仍然部分正确:

  • MemoryMappedFile是比文件更多的内存;它是内存中4Kb页面的序列。因此,stream.Length实际上会为您提供所有字节(没有“内部缓冲区大小”),但它可能会为您提供比您预期的更多的字节,因为大小总是向上舍入到4Kb边界
  • “文件”语义来自将MemoryMappedFile与实际文件系统文件相关联。假设创建文件的进程总是调整文件大小,那么您可以通过文件系统获得文件的精确大小
如果以上所有内容都适合您的应用程序,则以下内容应适用:

    static byte[] ReadMemoryMappedFile(string fileName)
    {
        long length = new FileInfo(fileName).Length;
        using (var stream = File.Open(fileName, FileMode.OpenOrCreate, FileAccess.Read, FileShare.ReadWrite))
        {
            using (var mmf = MemoryMappedFile.CreateFromFile(stream, null, length, MemoryMappedFileAccess.Read, null, HandleInheritability.Inheritable, false))
            {
                using (var viewStream = mmf.CreateViewStream(0, length, MemoryMappedFileAccess.Read))
                {
                    using (BinaryReader binReader = new BinaryReader(viewStream))
                    {
                        var result = binReader.ReadBytes((int)length);
                        return result;
                    }
                }
            }
        }
    }
要写入数据,可以使用以下方法:

    private static void WriteData(string fileName, byte[] data)
    {
        using (var stream = File.Open(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite))
        {
            using (var mmf = MemoryMappedFile.CreateFromFile(stream, null, data.Length, MemoryMappedFileAccess.ReadWrite, null, HandleInheritability.Inheritable, true))
            {
                using (var view = mmf.CreateViewAccessor())
                {
                    view.WriteArray(0, data, 0, data.Length);
                }
            }

            stream.SetLength(data.Length);  // Make sure the file is the correct length, in case the data got smaller.
        }
    }

但是,当您执行上述所有操作时,您也可以直接使用该文件并避免内存映射。如果无法将其映射到文件系统,那么Guffa在数据本身中编码长度(或结束标记)的答案可能是最好的。

仅将@Amer Sawan解决方案转换为Vb.NET:

' Usage Example:
' Dim ReadBytes As Byte() = ReadMemoryMappedFile(Name:="My MemoryMappedFile Name") ' Read the byte-sequence from memory.
' Dim Message As String = System.Text.Encoding.ASCII.GetString(ReadBytes.ToArray) ' Convert the bytes to String.
' Message = Message.Trim({ControlChars.NullChar}) ' Remove null chars (leading zero-bytes)
' MessageBox.Show(Message, "", MessageBoxButtons.OK) ' Show the message.    '
'
''' <summary>
''' Reads a byte-sequence from a <see cref="IO.MemoryMappedFiles.MemoryMappedFile"/> without knowing the exact size.
''' Note that the returned byte-length is rounded up to 4kb, 
''' this means if the mapped memory-file was written with 1 byte-length, this method will return 4096 byte-length. 
''' </summary>
''' <param name="Name">Indicates an existing <see cref="IO.MemoryMappedFiles.MemoryMappedFile"/> assigned name.</param>
''' <returns>System.Byte().</returns>
Private Function ReadMemoryMappedFile(ByVal Name As String) As Byte()

    Try
        Using MemoryFile As IO.MemoryMappedFiles.MemoryMappedFile =
            IO.MemoryMappedFiles.MemoryMappedFile.OpenExisting(Name, IO.MemoryMappedFiles.MemoryMappedFileRights.ReadWrite)

            Using Stream = MemoryFile.CreateViewStream()

                Using Reader As New BinaryReader(Stream)

                    Return Reader.ReadBytes(CInt(Stream.Length))

                End Using ' Reader

            End Using ' Stream

        End Using ' MemoryFile

    Catch exNoFile As IO.FileNotFoundException
        Throw
        Return Nothing

    Catch ex As Exception
        Throw

    End Try

End Function
”用法示例:
'Dim ReadBytes As Byte()=ReadMemoryMappedFile(名称:=“My MemoryMappedFile Name”)'从内存中读取字节序列。
'Dim Message As String=System.Text.Encoding.ASCII.GetString(ReadBytes.ToArray)'将字节转换为字符串。
'Message=Message.Trim({ControlChars.NullChar})'删除空字符(前导零字节)
'MessageBox.Show(Message,“,MessageBoxButtons.OK)'Show消息。'
'
''' 
''在不知道确切大小的情况下从读取字节序列。
''注意,返回的字节长度被四舍五入到4kb,
''这意味着,如果映射内存文件的写入长度为1字节,则此方法将返回4096字节的长度。
''' 
''表示已分配的现有名称。
''System.Byte()。
私有函数ReadMemoryMappedFile(ByVal名称作为字符串)作为字节()
尝试
将MemoryFile用作IO.MemoryMappedFile.MemoryMappedFile=
IO.MemoryMappedFiles.MemoryMappedFiles.OpenExisting(名称,IO.MemoryMappedFiles.MemoryMappedFiles.ReadWrite)
使用Stream=MemoryFile.CreateViewStream()
将读取器用作新的二进制读取器(流)
返回Reader.ReadBytes(CInt(Stream.Length))
“终端使用”阅读器
结束使用“流”
结束使用“MemoryFile”
捕获exNoFile作为IO.FileNotFoundException
扔
一无所获
特例
扔
结束尝试
端函数

我想使用MemoryStream.ToArray()方法返回所有字节,下面的代码适合我:

using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting(MemoryMappedName))
{
    using (MemoryMappedViewStream stream = mmf.CreateViewStream())
    {
        using (MemoryStream memStream = new MemoryStream())
        {
            stream.CopyTo(memStream);
            return memStream.ToArray();
        }
    }
}

干杯

使用FileInfo类获取长度,如下所示

using System.Data;
using System.IO;
using System.IO.Compression;
using System.IO.MemoryMappedFiles;

// ...

public void WriteToMemoryMap(DataSet ds, string key, string fileName)
{
    var bytes = CompressData(ds);
    using (MemoryMappedFile objMf = MemoryMappedFile.CreateFromFile(fileName, FileMode.OpenOrCreate, key, bytes.Length))
    {
        using (MemoryMappedViewAccessor accessor = objMf.CreateViewAccessor())
        {
            accessor.WriteArray(0, bytes, 0, bytes.Length);
        }
    }
}
public DataSet ReadFromMemoryMap(string fileName)
{
    var fi = new FileInfo(fileName);
    var length = (int)fi.Length;
    var newBytes = new byte[length];
    using (MemoryMappedFile objMf = MemoryMappedFile.CreateFromFile(fileName, FileMode.Open))
    {
        using (MemoryMappedViewAccessor accessor = objMf.CreateViewAccessor())
        {
            accessor.ReadArray(0, newBytes, 0, length);
        }
    }
    return DecompressData(newBytes);
}
public byte[] CompressData(DataSet ds)
{
    try
    {
        byte[] data = null;
        var memStream = new MemoryStream();
        var zipStream = new GZipStream(memStream, CompressionMode.Compress);
        ds.WriteXml(zipStream, XmlWriteMode.WriteSchema);
        zipStream.Close();
        data = memStream.ToArray();
        memStream.Close();
        return data;
    }
    catch (Exception)
    {
        return null;
    }
}
public DataSet DecompressData(byte[] data)
{
    try
    {
        var memStream = new MemoryStream(data);
        var unzipStream = new GZipStream(memStream, CompressionMode.Decompress);
        var objDataSet = new DataSet();
        objDataSet.ReadXml(unzipStream, XmlReadMode.ReadSchema);
        unzipStream.Close();
        memStream.Close();
        return objDataSet;
    }
    catch (Exception)
    {
        return null;
    }
}

MemoryMappedFile创建的流的长度与文件系统页面大小一致(通常为4096)。您必须从其他地方获取文件大小。如果它是内存映射的文件,则可以使用该代码:

字节[]ReadAllMemoryMappedFileBytes(字符串文件路径) { var fileInfo=新的fileInfo(filePath); 使用(var file=MemoryMappedFile.CreateFromFile(filePath,FileMode.Open)) 使用(var stream=file.CreateViewAccessor()) { byte[]bytes=新字节[fileInfo.Length]; ReadArray(0,字节,0,字节.长度); 返回字节; } }
您对IPC有什么建议?放一些尾巴到文件中?或者什么?我想把文件的大小放在开头。@MohamedSakherSawan:是的,任何文件结构都可以使用文件中的数据来确定大小。尽管如此,您还是可以在头中包含当前偏移量/长度…@MohamedSakherSawan,您是否在IPC中使用内存映射文件?你为什么不把它写在你的问题里?这是一个重要的细节!答案有一些问题,请提供一个更好的答案。令人惊讶的是,我在读/写字符串时没有发现任何问题,那些四舍五入的4096字节在获取字符串字节时会消失。非常有用的解决方案。我不认为这是一个有效的问题。。。固件的大小是否适合内存区域?您的数据必须适合指定区域。您已经创建了文件的大小,如果您没有该大小,那么在使用抽象或API处理文件之前,需要向您提供或了解该大小。如果需要,还可以在内存映射文件中创建一个标头,以指示长度和当前偏移量。例如