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!