Windowsapiメモ その1 備考録とエトセトラ
このシリーズは、WindowsAPIを使用してハマりそう/ハマった内容について記述していく備考録です。
(ライブラリ豊富な今、WindowsAPIを直接叩く機会があるのかどうかは不明ですが…)
不定期特集第1回目は、外部プロセスを起動するCreateProcess APIです。
CreateProcess の基本情報
MSDNより
BOOL CreateProcess(
LPCTSTR lpApplicationName, // 実行可能モジュールの名前
LPTSTR lpCommandLine, // コマンドラインの文字列
LPSECURITY_ATTRIBUTES lpProcessAttributes, // セキュリティ記述子
LPSECURITY_ATTRIBUTES lpThreadAttributes, // セキュリティ記述子
BOOL bInheritHandles, // ハンドルの継承オプション
DWORD dwCreationFlags, // 作成のフラグ
LPVOID lpEnvironment, // 新しい環境ブロック
LPCTSTR lpCurrentDirectory, // カレントディレクトリの名前
LPSTARTUPINFO lpStartupInfo, // スタートアップ情報
LPPROCESS_INFORMATION lpProcessInformation // プロセス情報
);
ポイント
- lpProcessAttributes は、NULLを指定すると規定のセキュリティ設定を使う(WindowsNT/2000以降)
- lpCurrentDirectory は、NULLを指定すると呼び出し側プロセスと同じディレクトリが使われる
- この関数は、プロセスの生成に成功または失敗するとすぐに制御を返す。呼び出したプロセスの終了を待つ場合は、別途WaitForSingleObjectAPIを使う必要がある。
そのままコールするとなると、実行の度に各変数の初期化処理を書く必要があり、面倒です。
ラッパ関数を用意するのが良いと思います。
ハマりポイント
第2引数の要素にスペースがある場合は気を付けよう
わりと基本的なことですが、例えば引数の1つになんかしらのパスを指定する場合は、スペースが含まれるケースをちゃんと考えないといけません。
たとえば、次のようなケースです。
STARTUPINFO si;
PROCESS_INFORMATION pi;
const TCHAR * exePath = _T("Executable program.exe");
const TCHAR * anyPath = _T(" C:\\Documents and Settings\\hoge\\file.txt");
ZeroMemory(&pi, sizeof(pi));
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
CreateProcess(exePath, anyPath,
NULL, NULL,
TRUE, NORMAL_PRIORITY_CLASS,
NULL, NULL,
&si, &pi);
このケースの場合、以下のように解釈されてしまいます。
- 第1引数(実行ファイル) : Executable program.exe
- 第2引数 : C:\Documents
- 第3引数 : and
- 第4引数 : Settings\file,txt
正しい解釈をさせたい場合は、パスをダブルクォーテーションで囲むのが一般的です。
anyPathの指定を以下のように訂正します。
const TCHAR * anyPath = _T(" \"C:\\Documents and Settings\\hoge\\file.txt\"");
訂正後の引数解釈は、以下になります。
- 第1引数(実行ファイル) : Executable program.exe
- 第2引数 : C:\Documents and Settings\file.txt
この他に、全てのスペースの前にエスケープ文字を入れるという方法もありますが、面倒なのでここでは触れません。
第2引数の先頭には、スペースを挿入しよう
上の例で普通に実践していますが、第2引数でコマンドライン引数を指定する場合、引数の先頭にスペースを挿入することを推奨します。
つまり、第2引数の値として、以下のような設定は避けるべきです。
const TCHAR * anyPath = _T("\"C:\\Documents and Settings\\hoge\\file.txt\"");
先頭にスペースが無い場合、第2引数の先頭要素の解釈が不定となります。
これはOSにより多少異なるようで、
Windows8.1 の場合、プロセスを普通に起動できる場合もあれば、起動できない場合もあります。(私の場合はGetOpenFileNameAPIをコールし、IDOK処理をした後は起動できなくなりました。)
WindowsXP の場合、プロセスの起動は普通にできるものの、コマンドライン引数の1番目の指定が反映されないような動作になるようです。
コマンドライン引数を確実に処理してもたいたい場合は、先頭にスペースを入れましょう。
ただし、これには例外もあり、第1引数(実行ファイルパス)をNULLにして、第2引数に実行ファイルパスを含める場合は、先頭のスペースは無くても問題ないようです。
ただし、実行ファイル名にスペースが含まれる場合は、ダブルクォーテーションで囲む必要があります。
blog comments powered by Disqus