(C++/Windows)起動元がコマンドプロンプトかGUIかを判定する

2016/01/10

題名の通りです。以下の需要を満たすためのものです。

  1. WindowsとC++で、Win32コンソールアプリケーションを作っている。
  2. 当該実行ファイルを、コマンドラインから起動した場合は、ふつうのコマンドと同様に、そのコマンドプロンプト内で完結した動作をしたい(ウインドウやメッセージボックスは邪魔なので出したくない)。
  3. 一方、デスクトップに実行ファイルのショートカットアイコンを作りそこからダブルクリックした場合、プログラムの終了時、コンソール画面がすぐに消滅してしまい、実行中に起きたことの履歴が確認できないので、例えばメッセージボックスでOKするまで閉じないで残したい。
  4. あるいは、I/Oファイル名をコマンドラインオプションから認識するが、実行ファイルをエクスプローラーからダブルクリックなどで起動したときは引数がない(ことがある)のでFileOpenDialogを出すなど。

すなわち、「自分の親プロセスが cmd.exe なのか否か」を判断する必要がある。

使い易い例題がなかったので、作ってみました。

Download

isInvokedFromCmdExe.zip

MSDNのサンプル(注※)をパクって、余計な部分を落とし、ちょいと味付けしただけです。なので、権利は主張しません(というか、できません)。オリジナルの Microsoft の記載をご確認の上、ご自由にお使いください。(※:このページを作成した2016年当時の情報です、2021年時点では更新があるかもしれませんが詳細未確認)
MSVC2010、MinGW gcc-3.4.5などでできます。他でも大体いけるでしょう。
ANSI実装です。Unicode実装はどうやったらよいか、わからん。
C++のstringやSTLのmapが例外を投げる可能性については未対処。

参考にしたもの

あと、自分のプロセスIDを取得する関数は、DWORD WINAPI GetCurrentProcessId(void); だそうです。


ちなみに、argv[0] がフルパスかどうかで判別するという単純だが手抜きな方法も、なくはありません。ただし、コマンドラインからフルパス指定で実行されたり、ランチャーから相対パスで実行されたりすると、意図しない挙動になります。また、パス名解析を自分で実装するのはミスしやすく、_splitpath_s() などWin32APIに任せるべき(ただし _splitpath_s() にはANSI実装の場合に別の問題があるので注意)。あとは、実装およびトラブルシューティングのコストで判断することになるのでしょう。


追補

[2016/01/21] 「自分の親プロセスが cmd.exe なのか否か」を検定するだけでは、まだ騙されるケースがありますね。

親プロセスのウインドウタイトルに対してFindWindow関数を用いて FindWindow("ConsoleWindowClass", parent->second.c_str()) のように検定すれば、起動元がコンソールであることが確定できるのかもしれません。(注: parent->second は上記サンプル isInvokedFromCmdExe.zip の中にある)
しかし、そこまでしつこく検定する意味あるか!? という気はします。逆に、同名のウインドウを誤認する可能性や、その下位のGetWindowText 関数呼び出しでハングする可能性が新たに生じます。
現時点ではやってませんし、やるつもりもありません。


略歴


トップページ / 計算機関係の雑多なこと / 起動元がコマンドプロンプトかGUIかを判定する
このサイトについて・著者連絡先