もくじ
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 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;
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 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)
{
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 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