症状
【症状】WMIコマンドでWin32_LogicalDisk が無効なクラスと言われる
背景とか途中経過とかいいから解決方法だけ教えろ!という緊急の方コチラ
お時間と精神に余裕のある方はぜひお付き合いください。
なんでこの記事を書くことになったか
とある案件でRaspberryPiPicoを使用することになったのだが、なぜかArduinoIDEでの書き込みが通らない。
IDEのコンソール出力から、ラズパイピコへプログラムを書き込む際にユーザーディレクトリ下のAppData\Local\Arduino15\packages\rp2040\hardware\rp2040\4.3.1/tools/uf2conv.py
を走らせてるっぽいとこまでは分かったのだが、uf2convの実行中に大量の赤文字エラーメッセージが羅列されて書き込みが通らない。
しかもこのエラーメッセージ、ほぼすべてが文字化けしている!(皆さんに見せたかったけどスクショ取るの忘れた orz)
ということで、ArduinoIDEが無事動くように頑張るぞと課題解決のたびに出かけたのですが最終的に何故かWindows君のシステム治療に落ち着いたのでネタとして記事にしました。
エラーが発生している処理の発見
uf2convの実行関数内に様々なデバッグメッセージを仕込んだりコメントアウトして挙動を確認したりすることで色々と格闘していくこと数時間、プログラムを書き込む前にデバイス情報を取得する関数内にてPowerSehllコマンドをサブプロセスでコールするコードを見つけた。ArduinoIDEコンソールでのエラー出力には必ずUnable to build drive list
を含んでいたのでこのtryが失敗していると見て間違いなさそうだ。
except:
try:
nul = open("nul:", "r")
r = subprocess.check_output(["powershell", "-NonInteractive", "-Command",
"Get-WmiObject -class Win32_LogicalDisk | "
"Format-Table -Property DeviceID, DriveType, Filesystem, VolumeName"],
stdin = nul)
nul.close()
except:
print("Unable to build drive list");
sys.exit(1)
このサブプロセスでコールされている問題のコマンドは以下の通り
powershell -NonInteractive -Command Get-WmiObject -class Win32_LogicalDisk | Format-Table -Property DeviceID, DriveType,Filesystem, VolumeName
ArduinoIDEで書き込みが行われる際に、接続されている物理ディスク(ここではターゲットマイコンのこと)を取得して対象ディスクをフォーマットするらしい。
このあとに対象ディスクへビルドしたバイナリデータをアップロードする処理も書かれていたので、RaspberryPiPicoのArduinoIDEでの書き込みでは毎回コントローラのフラッシュをフォーマットする仕様になっているようで間違えなさそうだ。
さて、問題のコマンドについてpowershell -NonInteractive -Command
はPowerShellコマンドをコールするときにCLIを開かず実行する指示なのでこれは素通りして、本命のコマンドはディスク情報取得のコマンドとフォーマットのコマンドだ。
Get-WmiObject -class Win32_LogicalDisk
Format-Table -Property DeviceID, DriveType,Filesystem, VolumeName
ArduinoIDEの処理出力画面は前述の通り綺麗に文字化けしていてまったく役に立たないのでPowerSehllを管理者権限で開いてこれらのコマンドを試していこうと思ったところで
> Get-WmiObject -class Win32_LogicalDisk
Get-WmiObject : 無効なクラスです "Win32_LogicalDisk"
発生場所 行:1 文字:1
+ Get-WmiObject -class Win32_LogicalDis
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidType: (:) [Get-WmiObject], ManagementException
+ FullyQualifiedErrorId : GetWMIManagementException,Microsoft.PowerShell.Commands.GetWmiObjectCommand
やってまいりました!
Windows君お得意の何もしていないのに壊れている現象が出てきました!
Win32_LogicalDiskが無効なクラス?!
さて発見されたエラーメッセージGet-WmiObject : 無効なクラスです "Win32_LogicalDisk"
でのWin32_LogicalDiskクラスについてですが、WMIプロパイダ内でのローカルストレージデバイスに関するデータソースをコールするクラスです。WMIについてとLogicalDiskクラスについては下記ページで詳しく解説されています。
WMIの破損
Win32_LogicalDiskのクラスだけピンポイントにパスが壊れたとかそんなことあるか?
と思ったのもつかの間、WMIがWindowsのシステム監視・管理用のインフラだったことを思い出し猛烈に嫌な予感がした僕はそのままPowerShellにsysteminfoをぶち込みました。
結果
> systeminfo
オペレーティング システム情報を読み込んでいます... エラー: 無効なクラスです
はい、確定しました。
WMIがぶっ壊れてます。
WMIの復旧
そもそもWMI自体がWindowsの挙動を監視するためのシステムでWindowsの重要インフラになっているはずなのにサイレントにぶっ壊れてるなんてことあるもんですね(白目)。
日本マイクロソフトのサポートチームによる技術ブログとかいう有能すぎる記事があったのでこちらを早速試してみます。
どうやらPowerSehllで実行できるWMIレポジトリの破損状況確認とその修復を行えるコマンドが存在しているらしくこれを試してみろとのことでした。
この手法はWMIが破損したときに行われる常套手法らしいです。
復旧コマンドの実行でデータベースに矛盾がないって嬉しい出力が返ってきました!
> winmgmt /salvagerepository
WMI データベースに矛盾はありません
みんなこれで治ってるそうだし、これで僕のPCも治るはず!
・・・まぁ、お約束ですね。人生そんなに楽じゃないわ。
WMIのレポジトリの手動削除
さて、王道のやり方は失敗したので下記の記事を参考に少々強硬手段で行きます。
WMIは以下のディレクトリ下にあるマップファイル等を参照して様々なクラス等をコールしてるみたいです。
\Windows\system32\wbem\repository
このディレクトリのことをWMIレポジトリと呼んでるみたいで、さっきの修復コマンドはこのディレクトリの修復を行うコマンドだったみたいです。
下記の記事ではWMIサービスを強制的に停止させ、再開する前にrepositoryディレクトリを削除。WMIサービスが立ち上がると再びrepositoryディレクトリが自動修復されるのでWMIレポジトリの修復が完了するという手筈みたいです。
記事通りに手順を踏んでやったけど...ダメ!
Autorecover MOFs の発見
ここまで挫折続きでしたが収穫もありました。
前章での作業中にレジストリエディタをいじる工程があったので、同ディレクトリ下のレジストリを片っ端から覗いていたのですが、ふとこんな名前のレジストリを発見...
名前はAutorecover MOFs
、さっき自動復旧で失敗したので見逃せない名前です。
レジストリのキーを読んでみると大量のMOF、MFLファイルのパスが登録されていました。
MFLファイルはシステム設定ファイルで、MOFファイルはCIMクラスが記述されているクラスフォーマットファイルらしいです。
...ん?クラス?
そもそも今回の出発点は、あるはずのクラスが無効だと言われたところからスタートしてるのでは?
ということでMicrosoftのラーニングページにてMOFファイルについてお勉強しました。
上の記事によると
Managed Object Format (MOF) は、Common Information Model (CIM) クラスを記述するために使用される言語です。
WMI プロバイダーで新しい WMI クラスを実装するには、Mofcomp.exe を使用して "WMI リポジトリ" にコンパイルされる MOF ファイルをお勧めします。 WMI 用 COM API を使用して CIM クラスとインスタンスを作成および操作することもできます。
これはもしかしてWMIサービスが立ち上がるときやWMI復旧コマンドを打ったときにAutorecover MOFs
に登録されたMOFファイル群がWMIリポジトリにコンパイルされることでWMIリポジトリが構築されているのでは?
ということでsystem32下のwbem内にあるMOFファイルからWin32_LogicalDiskクラスが定義されているMOFファイルを探す旅に出ました。
頑張って探すこと11ファイル目、cimwin32.mof
の3385行目に
class Win32_LogicalDisk : CIM_LogicalDisk
{
[Read : ToSubclass,Key : ToInstance ToSubclass DisableOverride,Override("DeviceId") : ToSubclass,MappingStrings{"WMI"} : ToSubclass] string DeviceID;
[read : ToSubclass,MappingStrings{"Win32API|File System Functions|GetVolumeInformation|FS_VOL_IS_COMPRESSED"} : ToSubclass] boolean Compressed;
[read : ToSubclass,MappingStrings{"Win32API|FileFunctions|GetDriveType"} : ToSubclass] uint32 DriveType;
[read : ToSubclass,MappingStrings{"Win32API|File System Functions|GetVolumeInformation"} : ToSubclass] string FileSystem;
[read : ToSubclass,MappingStrings{"Win32API|File System Functions|GetVolumeInformation"} : ToSubclass] uint32 MaximumComponentLength;
[read : ToSubclass,MappingStrings{"Win32API|Windows Networking Functions|WNetGetConnection"} : ToSubclass] string ProviderName;
[read : ToSubclass,MappingStrings{"Win32API|File System Functions|GetVolumeInformation|FS_FILE_COMPRESSION"} : ToSubclass] boolean SupportsFileBasedCompression;
[read : ToSubclass,write : ToSubclass,MappingStrings{"Win32API|File System Functions|GetVolumeInformation"} : ToSubclass] string VolumeName;
[read : ToSubclass,MappingStrings{"Win32API|File System Functions|GetVolumeInformation"} : ToSubclass] string VolumeSerialNumber;
[read : ToSubclass,MappingStrings{"Win32API|Device Input and Output Functions|DeviceIoControl"} : ToSubclass] uint32 MediaType;
[read : ToSubclass] boolean SupportsDiskQuotas;
[read : ToSubclass] boolean QuotasDisabled;
[read : ToSubclass] boolean QuotasIncomplete;
[read : ToSubclass] boolean QuotasRebuilding;
[Implemented,MappingStrings{"Fmifs.dll | Method ChkDskExRoutine"} : ToSubclass] uint32 Chkdsk([in] boolean FixErrors = FALSE,[in] boolean VigorousIndexCheck = TRUE,[in] boolean SkipFolderCycle = TRUE,[in] boolean ForceDismount = FALSE,[in] boolean RecoverBadSectors = FALSE,[in] boolean OkToRunAtBootUp = FALSE);
[Read : ToSubclass,MappingStrings{"FSCTL_IS_VOLUME_DIRTY"} : ToSubclass] boolean VolumeDirty;
[Static,Implemented,MappingStrings{"Chkntfs.exe"} : ToSubclass] uint32 ScheduleAutoChk([in] string LogicalDisk[]);
[Static,Implemented,MappingStrings{"Chkntfs.exe"} : ToSubclass] uint32 ExcludeFromAutochk([in] string LogicalDisk[]);
};
しかもAutorecover MOFs
にcimwin32.mof
が登録されていないので修復時にコンパイルされてないっぽい!
ということで、Microsoftによると、Mofcomp.exeを実行するとWMIレポジトリにWMIクラスとしてMOFがコンパイルされるそうなので、WMIサービスを停止した後にコマンドプロンプトを管理者権限で立ち上げて以下を実行。
Mofcomp C:\windows\system32\wbem\cimwin32.mof
再度WMIサービスを立ち上げると
Get-WmiObject -class Win32_LogicalDis
も無事動作して完全勝利です!
後処理としてAutorecover MOFs
にcimwin32.mof
を登録したのちにPCを再起動して無事動作を確認しました。
対処方法まとめ
ということで対処方法のまとめです。
【症状】Win32_LogicalDisk が無効なクラスと言われる
【解決方法】
- WMIサービスを停止する
- サービスから
Windows Management Instrumentation
を停止
- サービスから
- 管理者権限でコマンドプロンプトを立ち上げて以下を入力
Mofcomp C:\windows\system32\wbem\cimwin32.mof
- WMIサービスを再開する
-
【要注意:下の注意事項を読んでから実行するか決めてください】
レジストリエディタで以下の作業を行う-
コンピューター\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Wbem\CIMOM
へ移動する -
Autorecover MOFs
を右クリックして修正を選択 - 値のデータに
C:\windows\system32\wbem\cimwin32.mof
を追加
-
- 念押しの再起動
【注意事項】
- WMIサービスはいろんな処理が依存しているので手順をさっさと進めないと他のプロセスがサービスの立ち上げにかかります。サービスを止めてからは時間勝負なので頑張りましょう。
- レジストリエディタでの作業は結構危険を伴います。また、Autorecover MOFsレジストリの修正については未だ検証できていない部分が多く、値を追加することでの影響が未知数です。以上を踏まえて実行するか考えてください。
おわりに
ところで皆さんなぜこの苦行に挑戦することになったのかを忘れていないだろうか。
その理由は業務で使用するラズパイピコのArduinoIDEでの書き込みができなかったからである。
そう!断じてWMIプロパティでコンピュータの構成情報が見たかったのではなく業務で開発中のマイコンへの書き込み環境が壊れたからこの苦行に飛び込んだのだ!
そして、この苦行に費やした時間は早8時間...
この苦行は終わりではなく仕事の前哨戦として消化され、まさにこれから地獄の門が開くのであった。
納期がガガガ...
(おわり)