potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。はてなブログ無料版なので記事の上の方はたぶん広告です。記事中にも広告挿入されるみたいです。

C# MemoryMappedViewAccessorとMarshalを組み合わせた小物関数

メモリマップドファイルを利用する場合にお世話になるUnmanagedMemoryAccessorとWin APIを用いる場合にお世話になるSystem.Interop.Runtime.Marshalを組み合わせた小物関数です。

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;
using System.Text;
using System.Linq;

internal static partial class Helper
{
    /// <summary>
    /// アクセッサーからNUL文字(\0)終端の文字列を読み込みます。
    /// </summary>
    /// <param name="accessor">文字列を読み込むアクセッサー。</param>
    /// <param name="position">読み込みを開始する位置。</param>
    /// <param name="encoding">読み込む文字列のエンコーディング。</param>
    /// <returns>読み込んだ文字列。</returns>
    public static string ReadNullTerminatedString(
        this UnmanagedMemoryAccessor accessor,
        long position,
        Encoding encoding)
    {
        List<byte> nameBuffer = new List<byte>();
        for (byte b = accessor.ReadByte(position); b != 0; b = accessor.ReadByte(position))
        {
            nameBuffer.Add(b);
            position++;
        }
        return encoding.GetString(nameBuffer.ToArray());
    }

    /// <summary>
    /// アクセッサーから構造体を読み込みます。
    /// </summary>
    public static StructType ReadMarshalStructure<StructType>(
        this UnmanagedMemoryAccessor accessor,
        long position)
    {
        byte[] buffer = new byte[Marshal.SizeOf(typeof(StructType))];
        accessor.ReadArray<byte>(position, buffer, 0, buffer.Length);
        GCHandle bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
        try
        {
            return (StructType)Marshal.PtrToStructure(
                bufferHandle.AddrOfPinnedObject(), typeof(StructType));
        }
        finally
        {
            bufferHandle.Free();
        }
    }

    /// <summary>
    /// アクセッサーから連続した構造体の配列を読み込みます。
    /// </summary>
    public static StructType[] ReadMarshalStructureArray<StructType>(
        this UnmanagedMemoryAccessor accessor,
        long position,
        int length)
    {
        var buffer = new byte[Marshal.SizeOf(typeof(StructType)) * length];
        buffer = new byte[Marshal.SizeOf(typeof(StructType)) * length];
        accessor.ReadArray<byte>(position, buffer, 0, buffer.Length);
        var bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
        try
        {
            var result = new StructType[length];
            for (int i = 0; i < length; i++)
            {
                result[i] = (StructType)Marshal.PtrToStructure(
                new IntPtr(bufferHandle.AddrOfPinnedObject().ToInt64()
                    + Marshal.SizeOf(typeof(StructType)) * i),
                    typeof(StructType));
            }
            return result;
        }
        finally
        {
            bufferHandle.Free();
        }
    }

    /// <summary>
    /// アクセッサーから連続した構造体の配列を条件が満たされる限り読み込みます。
    /// </summary>
    public static StructType[] ReadMarshalStructureArrayWhere<StructType>(
        this UnmanagedMemoryAccessor accessor,
        long position,
        Func<StructType, bool> Predictor)
    {
        var result = new List<StructType>();
        var buffer = new byte[Marshal.SizeOf(typeof(StructType))];
        var bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
        try
        {
            for (long i = 0; i < long.MaxValue; i++)
            {
                accessor.ReadArray<byte>(
                    position + Marshal.SizeOf(typeof(StructType)) * i,
                    buffer, 0, buffer.Length);
                StructType s = (StructType)Marshal.PtrToStructure(
                    bufferHandle.AddrOfPinnedObject(), typeof(StructType));
                if (!Predictor(s)) break;
                result.Add(s);
            }
        }
        finally
        {
            bufferHandle.Free();
        }
        return result.ToArray();
    }
}

2021/3/10:この記事は別のブログで投稿した記事を移動したものです。