C++ ときどき ごはん、わりとてぃーぶれいく☆

USAGI.NETWORKのなかのひとのブログ。主にC++。

C#: Windows 青魔法により別プロセスのコマンドラインを掠め取ってくる方法、または WMI の使用方法

残念な事に別プロセスの起動時に与えられたコマンドライン情報を System.Diagnostics.ProcessStartInfo プロパティーFileNameArguments から取得はできない。それらはプロセスの起動を自身のプロセスから行う際に自身が設定した場合にのみ意味がある。別プロセスを相手にするのなら、そのアプローチは諦めよう。

ではどうするか?黒魔法は惜しいアイデアだ。 powershell でも cmd でも良いが、

wmic process where "processid=32096" get commandline /format:value
CommandLine="C:\Program Files\Epic Games\UE_4.19\Engine\Binaries\Win64\UE4Editor.exe" C:\Users\usagi\tmp\Experimental\Experimental.uproject -debug -game

などと取得できる。この実行例は UE4 プロジェクトを開発環境からデバッグ実行した状態で実行された Experimental プロジェクトのコマンドライン。プロセスIDはどうやって確認したのか?それは 1つ前の記事「UE4/C++: ジェネリック・プラットフォーム・罠ビリティー」 を眺めて欲しい。

wmicSystem.Diagnostics.ProcessStartInfoArguments に適当な引数を与えて標準出力から結果を読み出してパースすれば良い。そういうやり方は生きている世界にもよるが今回の記事のレベルとしては黒魔法という事にしておく。このような黒魔法的な方法も C++ アプリから WMI でさっくりと情報を取りたい場合には C++ネイティブコードで素直にDLLをリンクしてCOMを扱う よりもよほど現実的に便利だったりする事もある。

今回は C# なので WMI をあっさり使える。 "黒魔法" 的に実装するよりもせいぜい "青魔法" 的な実装で済む。 C# プロジェクトの ReferencesSystem.Management を追加し、次のような実装を行えば先の wmic と同様の事を "一応" C# だけで実装できる。

using System.Management;
static private string GetProcessCommandline( Int32 process_id )
{
  using ( var s = new ManagementObjectSearcher() )
  {
    s.Query.QueryString = "select * from win32_process";
    using ( var c = s.Get() )
    {
      var pid_string = process_id.ToString();
      foreach ( var o in c )
        if ( o[ "processid" ].ToString() == pid_string )
          return o[ "commandline" ].ToString();
    }
  }
  return null;
}

C# のセンセイ級の諸姉諸兄におかれましてはにゃーんか納得できないお気持ちでしょうか。

「クエリーを文字列で書くのか👹」

はい、そういうわけで "青魔法" っぽいじゃないですか。あはは😅

foreach 使っていいのは ISO/IEC 23270:2006 C# (2.0) までだから👹」

ManagementObjectSearcher 系に Where とか LINQ 対応を実装してくれていない Microsoft に言って下さい。

「その失敗 null じゃなくて例外使えよ👹」、「WMI 原理主義者として CommandLine と書くべきと要求する👹」、「文字列じゃなくて Int32 に合わせて比較しろよ👹」…

自分で何か作る時には自分へ要求して下さい。本当に効率が良いか、必要な事なのかはさておき。

「にゃーん」

『にゃあああああああああああああああん♥』

と、いうわけで WMI "青魔法" 実装でした😃 C++ と違ってまだ必要ならちょろりと実装を書こうかという気がしますが、そもそも OS の API としてfjkfjk (゜∀。)

にゃーーーーん。

References