WMIのクラスを全部取る・その中のプロパティ値もできるだけ取る

もくじ
https://tera1707.com/entry/2022/02/06/144447

やりたいこと

WMIでなにか値をとりたいなと思ったときに、どのクラスからどんな値が取れるのかがわからないので、いつも出だしで困る。

あまり良いやり方ではないかもしれないが、とりあえずもう全部のWMIで取れる値を全部取ってしまってから、その中になんか目的のあたいっぽいのがないかなー?という調べ方をしたい。

で、そのために、全部のクラス(のインスタンス?)から、全部のプロパティの値をとって全部表示するようなことがしたい。

※WMIのクラス、とか、インスタンス、とか、プロパティ、とか言っているが、それが用語として正しいのかはっきりわかってないのが今の現状。ゆえにそんな変な調べ方しかできないのだが、知識を付けるのは追々やるとして、いったんそういう変なことができるのかどうか調べてみる。。。

※以前も、似たようなことを言っていて、そのときはWbemTestというツールを使ってみた。(が、以前目的のモノにすぐたどり着けない。。。)

https://zenn.dev/tera1707/articles/87bf99621770e1

やったこと

ObjectQuery()をnewするときに、

var query = new ObjectQuery("SELECT * FROM meta_class");

というクエリにする(FROMにmeta_classを指定する)と、WMIのクラスを全部取ってこれる様子。

下のコードのようにすれば、全部のインスタンスの値が取れてるっぽい。 つまり、Wim32_BIOSとかWin32_ComputerSystemとか、もろもろいつも使うようなクラスの値が全部とれてるっぽい。

ぽい、というのは、下記のコードを実行すると、途中でクォータ違反ですというエラーが出てしまい、途中で終わってしまい、全部みれないため。

※どうも、連続して大量にWMIでデータをとると、そういうエラーになるらしい。

public void GetInfo()
{
    var scope = new ManagementScope("\\\\.\\ROOT\\cimv2");

    //var query = new ObjectQuery("SELECT * FROM Win32_ComputerSystem");
    var query = new ObjectQuery("SELECT * FROM meta_class");

    var searcher = new ManagementObjectSearcher(scope, query);

    using (ManagementObjectCollection queryCollection = searcher.Get())
    {
        try
        {
            foreach (ManagementObject mo in queryCollection)
            {
                Console.WriteLine($"■{mo.ClassPath}");

                if (mo.ClassPath.ToString().Contains("Win32_LocalTime") || mo.ClassPath.ToString().Contains("Win32_NTLogEvent") || mo.ClassPath.ToString().Contains("Win32_SoftwareElement"))
                    continue;

                ManagementClass mc = new ManagementClass(mo.ClassPath);

                foreach (ManagementObject c in mc.GetInstances())
                {
                    Console.WriteLine($"  ▼{c.ClassPath}");

                    PropertyDataCollection props = c.Properties;
                    // WMIのオブジェクトのプロパティを列挙する
                    foreach (PropertyData p in props)
                    {
                        Console.WriteLine($"   {p.Name,-30} : {p.Value}");
                    }
                    c.Dispose();
                }
                mo.Dispose();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }
    }
    searcher.Dispose();

    Console.WriteLine("finished.......");
}

で、途中で落ちるのでは困る。

なので、上記コードでコメントアウトしている

    //var query = new ObjectQuery("SELECT * FROM Win32_ComputerSystem");
    var query = new ObjectQuery("SELECT * FROM meta_class");

の部分を反対にして、Win32_ComputerSystemの情報を取るようにしてやると、まぁWin32_ComputerSystemに含まれる値はうまいこと取れる。

本番でWMIでデータをとるときは、全部とってくるようなことはしないので、結局大体使えそうなクラスを見定めておいて、SELECT * FROM 使えそうなクラス名にするしかなさそう。

※ちなみに、私のPCで上記コードを実行すると、400万行以上のテキストになった。 本番で使いそうなクラスの値は、たまたま含まれていたが、途中では終わっている様子。

使えそうなクラス名を探すとき

上のコードの、プロパティを全部とる部分を消して、

public void GetInfo()
{
    var scope = new ManagementScope("\\\\.\\ROOT\\cimv2");

    var query = new ObjectQuery("SELECT * FROM meta_class");

    var searcher = new ManagementObjectSearcher(scope, query);

    using (ManagementObjectCollection queryCollection = searcher.Get())
    {
        foreach (ManagementObject mo in queryCollection)
        {
            Console.WriteLine($"■{mo.ClassPath}");
            mo.Dispose();
        }
    }
    searcher.Dispose();

    Console.WriteLine("finished.......");
}

としてやると、クラス名は全部取れる様子。
wbemtestのツールと、上記で吐いたクラス一覧を合わせてみるしかないか。。。。

※上記で吐いたクラスだけの一覧は、私のPCでは1300行程度だった。

ManagementClassを使わないと中身がnullになる ← 追記:間違いだった ←追記:やっぱり間違いじゃなかった(nullになる)

以前調べた、WMIでCPU時間を取る方法のコードと今回のコードだと、foreach (ManagementObject mo in queryCollection)の中で、

今回は

ManagementClass mc = new ManagementClass(mo.ClassPath);
foreach (ManagementObject c in mc.GetInstances())
{・・・

という感じで、ManagementClassをnewして、そいつでGetInstanceをしている部分がことなる。 (CPU時間のときは、それがなかった)

今回は、Win32_ComputerSystemクラスのプロパティの値を取りたかったのだが、ManagementClassを使わないと(前回のように、searcher.Get()でとったManagementObjectCollection の中身を直接見る感じだと)、Win32_ComputerSystemのプロパティの値は全部nullだった。

                ManagementObjectCollection queryCollection = searcher.Get();

                foreach (ManagementObject m in queryCollection)
                {
                    // WMIオブジェクトのプロパティを取得して表示
                    Console.WriteLine("{0}, {1}, {2}, {3}, {4}", m["Name"], m["IDProcess"], m["PercentProcessorTime"], m["PrivateBytes"], (uint.Parse(m["IOWriteBytesPerSec"].ToString())));
                }

クラス(ガワ)には中身の値がないけど、とインスタンス(中身)にはある、みたいな違いがあるのかもしれない。

※現状、勉強不足でよくわかっていない。(CPU時間はそれでとれてたのに、なぜ今回はnullなのか、等)

ManagementClassを使わなくても値が取れた

ManagementClassを使わないと中身がnullになる、は間違いだった。 WMIでCPU時間を取る方法のコードと似たやり方で、値もとれた。

実験していたときに、なにか変なことをしていたっぽい。

下記で、うまく取れた。

public void GetInfo()
{
    var scope = new ManagementScope("\\\\.\\ROOT\\cimv2");

    var query = new ObjectQuery("SELECT * FROM Win32_ComputerSystem");
    //var query = new ObjectQuery("SELECT * FROM meta_class");

    var searcher = new ManagementObjectSearcher(scope, query);

    using (ManagementObjectCollection queryCollection = searcher.Get())
    {
        try
        {
            foreach (ManagementObject mo in queryCollection)
            {
                Console.WriteLine($"■{mo.ClassPath}");
                PropertyDataCollection props = mo.Properties;

                foreach (PropertyData p in props)
                {
                    Console.WriteLine($"   {p.Name,-30} : {p.Value}");
                }
                mo.Dispose();
            }
        }
        catch (Exception ex) 
        {
            Console.WriteLine(ex);
        }
    }
    searcher.Dispose();

    Console.WriteLine("finished.......");
}

※やっぱり、その後もう一度試したらまた値が空になっていた。 なにか条件がある??一度インスタンス取得したら、そのあとはインスタンス取らなくても値が入ってる、とか?

備考

WMIについての知識がたりないせいで、なにか無理やり感、頓珍漢なことしてる感を感じる。

おいおい勉強必要。

参考

全部のWMIのクラス?を取ってくる https://mycsharp.de/forum/threads/69845/lan-adapter-auslesen?page=1

PropertyData クラス https://learn.microsoft.com/ja-jp/dotnet/api/system.management.propertydata?view=dotnet-plat-ext-7.0

WMI クラスの取得

https://learn.microsoft.com/ja-jp/windows/win32/wmisdk/retrieving-a-class

WMI インスタンスの取得

https://learn.microsoft.com/ja-jp/windows/win32/wmisdk/retrieving-an-instance