potisanのプログラミングメモ

プログラミング素人です。昔の自分を育ててくれたネット情報に少しでも貢献できるよう、情報を貯めていこうと思っています。Windows環境のC++やC#がメインです。

C# imageres.dll.munからProgressRingのリソース(PNGデータ)を読み込む C#12 (.NET 8)版

過去の投稿のコードをC# 12 (.NET 8)版に書き換えたものです。nintやオブジェクト初期化子により少し簡潔になります。

unsafe

using System.ComponentModel;
using System.Runtime.InteropServices;

[assembly: DefaultDllImportSearchPaths(DllImportSearchPath.System32)]

// リソースファイルからProgressRingのPNGデータを読み込む。
using var imageResDllMunHandle = SafeLibraryHandle.Load(
    @"SystemResources\imageres.dll.mun");
var pngData = Utility.LoadResource(imageResDllMunHandle, 5021, "PNG");

// ProgressRingのPNGデータから画像を作成する。
using var pngImage = Utility.CreateImageFromByteArray(pngData);

// ProgressRingの画像をフォームへ表示する。
var mainForm = new Form();
mainForm.Controls.Add(new PictureBox() { Image = pngImage, AutoSize = true });
Application.Run(mainForm);

static class Utility
{
    /// <summary>
    /// バイト配列から<c>Image</c>インスタンスを返します。
    /// </summary>
    public static Image CreateImageFromByteArray(byte[] data)
    {
        using var stream = new MemoryStream(data, false);
        return Image.FromStream(stream);
    }

    /// <summary>
    /// モジュールのリソースデータを読み込みます。
    /// </summary>
    /// <remarks>
    /// モジュールが解放されても使用できるように関数はバイト配列を返します。
    /// </remarks>
    public static byte[] LoadResource(SafeHandle handle, ushort id, string type)
    {
        return LoadResource(handle, $"#{id}", type);
    }

    /// <summary>
    /// モジュールのリソースデータを読み込みます。
    /// </summary>
    /// <remarks>
    /// モジュールが解放されても使用できるように関数はバイト配列を返します。
    /// </remarks>
    public static byte[] LoadResource(SafeHandle handle, string name, string type)
    {
        var resInfoHandle = NativeMethods.FindResourceW(handle, name, type);
        if (resInfoHandle == 0) throw new Win32Exception();

        var resDataHandle = NativeMethods.LoadResource(handle, resInfoHandle);
        if (resDataHandle == 0) throw new Win32Exception();

        var resSize = NativeMethods.SizeofResource(handle, resInfoHandle);
        if (resSize == 0) throw new Win32Exception();

        var resPointer = NativeMethods.LockResource(resDataHandle);
        if (resPointer == 0) throw new Win32Exception();

        var buffer = GC.AllocateUninitializedArray<byte>(checked((int)resSize));
        Marshal.Copy(resPointer, buffer, 0, buffer.Length);

        return buffer;
    }

    private static class NativeMethods
    {
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true)]
        public static extern nint FindResourceW(SafeHandle hModule, string lpName, string lpType);
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern nint LoadResource(SafeHandle hModule, nint hResInfo);
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern nint LockResource(nint hResData);
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern uint SizeofResource(SafeHandle hModule, nint hResInfo);
    }
}

/// <summary>
/// ライブラリハンドルを安全に管理します。
/// </summary>
sealed class SafeLibraryHandle : SafeHandle
{
    public SafeLibraryHandle()
        : base(default, false)
    { }

    public SafeLibraryHandle(nint handle, bool ownsHandle)
        : base(handle, ownsHandle)
    { }

    protected override bool ReleaseHandle()
    {
        NativeLibrary.Free(handle);
        return true;
    }

    public override bool IsInvalid => handle == default;

    public static SafeLibraryHandle Load(string? path)
    {
        ArgumentNullException.ThrowIfNull(path);

        nint handle = 0;
        try
        {
            handle = NativeLibrary.Load(path);
            return new SafeLibraryHandle(handle, true);
        }
        catch
        {
            NativeLibrary.Free(handle);
            throw;
        }
    }
}

unsafe

using System.ComponentModel;
using System.Runtime.InteropServices;

[assembly: DefaultDllImportSearchPaths(DllImportSearchPath.System32)]

// リソースファイルからProgressRingのPNGイメージを読み込む。
using var imageResDllMunHandle = SafeLibraryHandle.Load(
    @"SystemResources\imageres.dll.mun");
using var pngImage = Utility.LoadResourceAsImage(imageResDllMunHandle, 5021, "PNG");

// ProgressRingの画像をフォームへ表示する。
var mainForm = new Form();
mainForm.Controls.Add(new PictureBox() { Image = pngImage, AutoSize = true });
Application.Run(mainForm);

static class Utility
{
    /// <summary>
    /// バイト配列から<c>Image</c>インスタンスを返します。
    /// </summary>
    public static Image CreateImageFromByteArray(byte[] data)
    {
        using var stream = new MemoryStream(data, false);
        return Image.FromStream(stream);
    }

    /// <summary>
    /// モジュールのリソースデータを読み込みます。
    /// </summary>
    public static Image LoadResourceAsImage(SafeHandle handle, ushort id, string type)
    {
        return LoadResourceAsImage(handle, $"#{id}", type);
    }

    /// <summary>
    /// モジュールのリソースデータを<c>Image</c>として読み込みます。
    /// </summary>
    public static Image LoadResourceAsImage(SafeHandle handle, string name, string type)
    {
        var resInfoHandle = NativeMethods.FindResourceW(handle, name, type);
        if (resInfoHandle == 0) throw new Win32Exception();

        var resDataHandle = NativeMethods.LoadResource(handle, resInfoHandle);
        if (resDataHandle == 0) throw new Win32Exception();

        var resSize = NativeMethods.SizeofResource(handle, resInfoHandle);
        if (resSize == 0) throw new Win32Exception();

        var resPointer = NativeMethods.LockResource(resDataHandle);
        if (resPointer == 0) throw new Win32Exception();

        unsafe
        {
            using var stream = new UnmanagedMemoryStream((byte*)resPointer, resSize);
            return Image.FromStream(stream);
        }
    }

    private static class NativeMethods
    {
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true)]
        public static extern nint FindResourceW(SafeHandle hModule, string lpName, string lpType);
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern nint LoadResource(SafeHandle hModule, nint hResInfo);
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern nint LockResource(nint hResData);
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern uint SizeofResource(SafeHandle hModule, nint hResInfo);
    }
}

/// <summary>
/// ライブラリハンドルを安全に管理します。
/// </summary>
sealed class SafeLibraryHandle : SafeHandle
{
    public SafeLibraryHandle()
        : base(default, false)
    { }

    public SafeLibraryHandle(nint handle, bool ownsHandle)
        : base(handle, ownsHandle)
    { }

    protected override bool ReleaseHandle()
    {
        NativeLibrary.Free(handle);
        return true;
    }

    public override bool IsInvalid => handle == default;

    public static SafeLibraryHandle Load(string? path)
    {
        ArgumentNullException.ThrowIfNull(path);

        nint handle = 0;
        try
        {
            handle = NativeLibrary.Load(path);
            return new SafeLibraryHandle(handle, true);
        }
        catch
        {
            NativeLibrary.Free(handle);
            throw;
        }
    }
}