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!

4 Comments

Comments have been disabled for this content.