Even easier high speed reading/writing of binary data files with C#

In my previous posting I presented a way to avoid unnecessarily copying data during read/write of binary files by using low level C CTR file functions like fread() instead of System.IO. This works just fine - but requires you to link in an unmanaged code C DLL if not even doing programming in C yourself.

Now, thanks to Christof Sprenger at Microsoft Germany, this hurdle has been removed. He showed me how to use the existing low level file handle of a stream with the kernel32.dll ReadFile()/WriteFile() functions. No more C programming is necessary, although unsafe code is still needed. But that´s ok, I´d say.

Here´s an example how simple your code is if you use the new BinaryFileStream class:

struct ID3v1Tag
{
    ...
};

ID3v1Tag tag;

BinaryFileStream bfs = new BinaryFileStream("track01.mp3", System.IO.FileMode.Open);

bfs.Seek(-128, System.IO.SeekOrigin.End);
unsafe
{
    bfs.Read<ID3v1Tag>(&tag);
}

tag.Title = ...;
...
bfs.Write<ID3v1Tag>(&tag);

bfs.Close();

And here´s the BinaryFileStream itself:

using System.Runtime.InteropServices;

namespace System.IO
{
    public unsafe class BinaryFileStream : FileStream
    {
        #region CTORs

        public BinaryFileStream(string path, FileMode mode)
            : base(path, mode)
        {}

        public BinaryFileStream(string path, FileMode mode, FileAccess access)
            : base(path, mode, access)
        {}

        public BinaryFileStream(string path)
            : base(path, FileMode.Open)
        { }

        #endregion


        public int Read<StructType>(void* buffer) where StructType : struct
        {
            return Read(buffer, Marshal.SizeOf(typeof(StructType)));
        }


        public void Write<StructType>(void* buffer) where StructType : struct
        {
            Write(buffer, Marshal.SizeOf(typeof(StructType)));
        }


        #region Low-level file access

        protected int Read(void* buffer, int count)
        {
            int n = 0;

            if (!ReadFile(this.SafeFileHandle, buffer, count, out n, 0))
                throw new IOException(string.Format("Error {0} reading from file!", Marshal.GetLastWin32Error()));

            return n;
        }

        protected void Write(void* buffer, int count)
        {
            int n = 0;

            if (!WriteFile(this.SafeFileHandle, buffer, count, out n, 0))
                throw new IOException(string.Format("Error {0} writing to file!", Marshal.GetLastWin32Error()));
        }


        [DllImport("kernel32", SetLastError = true)]
        private static extern unsafe bool ReadFile(
            Microsoft.Win32.SafeHandles.SafeFileHandle hFile,
            void* pBuffer,           
            int numberOfBytesToRead, 
            out int numberOfBytesRead,
            int overlapped           
            );

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern unsafe bool WriteFile(
            Microsoft.Win32.SafeHandles.SafeFileHandle handle,
            void* bytes,
            int numBytesToWrite,
            out int numBytesWritten,
            int overlapped
            );

        #endregion
    }
}

Copy and enjoy!

Comments

# re: Even easier high speed reading/writing of binary data files with C#

Monday, March 20, 2006 3:23 AM by Bjørn Reppen

Super nice solution!

# re: Even easier high speed reading/writing of binary data files with C#

Monday, March 27, 2006 11:19 AM by Marcos

No so faster but a bit more clear are the FileHelpers

http://filehelpers.sourceforge.net

;)
Cheers

# Knowing .NET &raquo; Blog Archive &raquo; High-speed binary file reading in C#

Pingback from  Knowing .NET  &raquo; Blog Archive   &raquo; High-speed binary file reading in C#

# re: Even easier high speed reading/writing of binary data files with C#

Thursday, August 06, 2009 10:38 AM by roadman

Is this still valid for the "high-speed" after releasing of .NET 2.0 or 3.0?

Leave a Comment

(required) 
(required) 
(optional)
(required)