potisanのプログラミングメモ

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

C# Win32 APIを使ってバイトの単位を変換する

C#でWin APIのShell Lightweight Utility Functionsを用いてバイトの単位を変換する補助関数のコードです。拡張メソッドとして定義していますが、thisを外せば関数として利用することもできます。this UInt64 value等のUInt64部分を変更することで対応する型を増やすことができます。

StrFormatByteSizeExWの対応バージョンがWindows Vista with SP1 [desktop apps only]&Shlwapi.dll (version 6.0 or later)、StrFormatByteSizeWの対応バージョンがWindows 2000 Professional, Windows XP [desktop apps only]&Shlwapi.dll (version 4.71 or later)で後者の方が対応範囲が広いことから、フラグなしバージョンはStrFormatByteSizeWを利用しています。

// StringByteFormatExtensions.cs
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Text;

namespace Potisan.Windows.ByteFormat
{
    [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode=true)]
    public static class StringByteFormatExtensions
    {
        [SuppressUnmanagedCodeSecurity]
        private static class NativeMethods
        {
            [DllImport("shlwapi.dll", SetLastError = true,
                CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
            public static extern IntPtr StrFormatByteSizeW(
                Int64 qdw,
                StringBuilder pszBuf,
                UInt32 cchBuf);
            [DllImport("shlwapi.dll", SetLastError = true,
                CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
            public static extern IntPtr StrFormatByteSizeExW(
                UInt64 qdw,
                StrFormatByteSizeExFlags flags,
                StringBuilder pszBuf,
                UInt32 cchBuf);
        }

        private const int ERROR_INSUFFICIENT_BUFFER = 122;

        [Flags]
        public enum StrFormatByteSizeExFlags
        {
            RoundToNearestDisplayedDigit = 1,
            TruncateUndisplayedDEcimalDigits = 2,
        }

        public static String FormatByteSize(this Int64 value, int expanding = 256)
        {
            StringBuilder buffer = new StringBuilder(expanding);
            while (NativeMethods.StrFormatByteSizeW(
                value, buffer,
                checked((uint)buffer.Capacity)) != IntPtr.Zero
                && buffer.Length + 1 == buffer.Capacity)
            {
                buffer.Capacity += expanding;
            }
            if (Marshal.GetLastWin32Error() != 0)
                throw new Win32Exception();
            return buffer.ToString();
        }
        public static String FormatByteSize(this Int32 value, int expanding = 256)
        {
            return FormatByteSize((Int64)value, expanding);
        }
        public static String FormatByteSize(this UInt32 value, int expanding = 256)
        {
            return FormatByteSize((Int64)value, expanding);
        }
        public static String FormatByteSize(this UInt64 value, int expanding = 256)
        {
            return FormatByteSize((Int64)value, expanding);
        }

        public static String FormatByteSize(this UInt64 value, StrFormatByteSizeExFlags flags, int expanding = 256)
        {
            StringBuilder buffer = new StringBuilder(expanding);
            while (NativeMethods.StrFormatByteSizeExW(
                value, flags, buffer,
                checked((uint)buffer.Capacity)) != IntPtr.Zero
                && buffer.Length + 1 == buffer.Capacity)
            {
                buffer.Capacity += expanding;
            }
            if (Marshal.GetLastWin32Error() != 0)
                throw new Win32Exception();
            return buffer.ToString();
        }
        public static String FormatByteSize(this UInt32 value, StrFormatByteSizeExFlags flags, int expanding = 256)
        {
            return FormatByteSize((UInt64)value, flags, expanding);
        }
        public static String FormatByteSize(this Int32 value, StrFormatByteSizeExFlags flags, int expanding = 256)
        {
            return FormatByteSize((UInt64)value, flags, expanding);
        }
        public static String FormatByteSize(this Int64 value, StrFormatByteSizeExFlags flags, int expanding = 256)
        {
            return FormatByteSize((UInt64)value, flags, expanding);
        }
    }
}
using System.Text;
using System.Windows.Forms;
using Potisan.Windows.ByteFormat;

namespace VolumeDeviceIoControl1
{
    static class Program
    {
        static void Main()
        {
            StringBuilder builder = new StringBuilder();

            long i = 12345678910;
            builder.AppendLine(i.FormatByteSize());
            builder.AppendLine(i.FormatByteSize(
                StringByteFormatExtensions.StrFormatByteSizeExFlags.RoundToNearestDisplayedDigit));

            // 11.4 GB
            // 11.5 GB
            MessageBox.Show(builder.ToString());
        }
    }
}

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