【C#】WMIでOSのメモリ使用量を取得する

WMI(Windows Management Infrastructure)という機能を使うとWindowsのシステムの各種情報を取得することができます。今回はこの機能を使用して

確認環境

  • Windows10
  • VisualStudio 2019
  • .NET Framework 4.7.2 / .NET 5

プロジェクトの設定

WMI の機能を使用するために System.Management.dll をプロジェクトの参照に追加する必要があります。

.NET Framework系

.NET Framework のプロジェクトの場合以下操作でアセンブリを追加します。

ソリューションエクスプローラー > 対象のプロジェクト > 参照 > 参照の追加

表示されるウインドウで

アセンブリ > System.Management

にチェックを入れ「OK」を選択します。

.NET 系

Windows 専用の機能のため標準のライブラリには入っていないため nuget から別途入手します。

.NET Core 及び .NET 5 以降だと System.Management は nuget から別途導入します。

VisualStudio の GUI から操作する場合、以下手順で導入できます。

ソリューションエクスプローラー > 対象のプロジェクト > 依存関係 > NuGet パッケージの管理

表示されるタブに「System.Management」と入力し「インストール」を選択します。

パッケージマネージャー経由の場合、以下の操作でコンソールを表示し

ツール > NuGet パッケージマネージャー > パッケージ マネージャー コンソール

コンソールに以下を入力してパッケージを導入します。

// .NET 5
Install-Package System.Management -Version 5.0.0

// .NET 6
Install-Package System.Management -Version 6.0.0

メモリ使用量を取得する

以下、メモリ使用量を WMI から1秒ごとに取得する場合の実装例となります。

using System;
using System.Management;
using System.Threading;


internal class Program
{
    private static void Main(string[] args)
    {
        using (var wt = new WmiTrance())
        {
            wt.Init();

            while (true)
            {
                WmiMemoryInfo info = wt.GetInfo();
                Console.Write($"{info.TotalMemory}, ");
                Console.Write($"{info.FreePhysicalMemory}, ");
                Console.Write($"{info.TotalVirtualMemorySize}, ");
                Console.WriteLine($"{info.FreeVirtualMemory}");
                Thread.Sleep(1000);
            }
        }
    }
}

/// <summary>
/// WMIからトレース用にPCの統計情報出力します。
/// </summary>
public class WmiTrance : IDisposable
{
    private ManagementClass mc;

    public void Init()
    {
        this.mc = new ManagementClass("Win32_OperatingSystem");
    }

    public WmiMemoryInfo GetInfo()
    {
        using (ManagementObjectCollection moc = mc.GetInstances())
        {
            foreach (ManagementObject mo in moc)
            {
                using (mo)
                {
                    // 物理メモリ合計
                    ulong a = (ulong)mo["TotalVisibleMemorySize"];
                    // 物理メモリFree
                    ulong b = (ulong)mo["FreePhysicalMemory"];
                    // 総メモリ合計
                    ulong c = (ulong)mo["TotalVirtualMemorySize"];
                    // 総メモリFree
                    ulong d = (ulong)mo["FreeVirtualMemory"];
                    
                    return new WmiMemoryInfo(a, b, c, d);
                }
            }
        }
        return default;
    }

    public void Dispose()
    {
        using (this.mc) { }
        this.mc = null;
    }
}

/// <summary>
/// WMIから取得したメモリ情報を記録します。
/// </summary>
public struct WmiMemoryInfo
{
    // 物理メモリ合計
    public readonly ulong TotalMemory;
    // 物理メモリ空き容量
    public readonly ulong FreePhysicalMemory;
    // 総メモリ合計
    public readonly ulong TotalVirtualMemorySize;
    // 総メモリ空き容量
    public readonly ulong FreeVirtualMemory;

    public WmiMemoryInfo(ulong totalMemory, ulong freePhysicalMemory, ulong totalVirtualMemorySize, ulong freeVirtualMemory)
    {
        this.TotalMemory = totalMemory;
        this.FreePhysicalMemory = freePhysicalMemory;
        this.TotalVirtualMemorySize = totalVirtualMemorySize;
        this.FreeVirtualMemory = freeVirtualMemory;
    }
}

「new ManagementClass("Win32_OperatingSystem");」でオブジェクトを作成し、「GetInstances」メソッドで情報を取得します。取れる情報はコレクションになっているので foreach などで回す必要があります(ただし今回は複数要素数入って来ることは無いので初回でループを終了します)

ManagementObjectCollection はプロパティ名の文字列でインデクサでアクセスすると対応した値が取得できます。

また、GetInstances メソッドは処理速度がかなり遅いので(実行すると ~50ms 程度かかってしまうので)高頻度の実行は避けましょう。非同期版のメソッドは用意されていないためメインスレッド上で高頻度で実行した場合GUIの応答性が低下する可能性があります。