トップWindows-Tips > 外部プロセスの起動

外部プロセスの起動

C言語のコンソール・プログラムやWindowsプログラムから別のアプリケーションプログラムなど 外部プロセスを起動する方法について述べる。

1.system関数

一番簡単なのは system関数を使う方法である。 例えば、次のプログラムは NotePadを起動する。 この場合、NotePadの終了を待つ。

void main() {
    system("notepad");
}

Windowsアプリケーションで このsystem関数を実行した場合には、コマンドプロンプト画面が表示される。 system関数で起動するのがコンソール・アプリケーションであり、 実行の様子を観察したい場合にはこれでよいが、 NotePadなどWindowsアプリケーションを起動する場合などでは コマンドプロンプト画面が表示されるのは好ましくない。

Windowsアプリケーションからコンソール・アプリケーションを起動する場合、 system("pi 120"); とすると、πの値を120桁計算した結果画面が一瞬表示されてすぐ消える。

表示内容を確認したい場合には、コマンドプロセッサを経由して system("cmd /k pi 120"); とすればよい。/k はコマンド実行した後終了しないオプションである。 (ここで、pi.exeは自作のプログラムであり、一般のパソコンで実行できるものではない。 単なるコンソール・アプリケーションの一例である。)

2.CreateProcess関数

外部プロセス起動Windows APIとしては WinExec関数がある。

UINT WinExec(
   LPCSTR lpCmdLine,   // コマンドラインへのポインタ
   UINT uCmdShow       // ウィンドウの表示状態
);

例えば WinExec("notepad", SW_SHOW); とすれば、Windowsアプリケーションから NotePad が起動できる。 Windowsアプリケーションからコンソールアプリケーションを起動する場合も同様である。 第二引数を SW_SHOW とすればコマンドプロンプト画面が表示され、SW_HIDE とすれば表示されない。

しかし、この関数は Windows の以前のバージョンとの互換性のために残されているものであり、 Win32 アプリケーションでは、CreateProcess 関数を使うことが推奨されている。

下に使用例を示す。CreateProcess関数はWinExec関数より高い機能を持つ代わりに、 使い方が少々面倒である。

void createProcess(char* szCmd, DWORD flag, BOOL fWait) {
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    memset(&si, 0, sizeof(STARTUPINFO));
    memset(&pi, 0, sizeof(PROCESS_INFORMATION));
    si.cb = sizeof(STARTUPINFO);
    CreateProcess(NULL, szCmd, NULL, NULL, FALSE, flag, NULL, NULL, &si, &pi);
    if (fWait) WaitForSingleObject(pi.hProcess, INFINITE);	//終了を待つ.
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread); 
}

system関数の場合、起動したプログラムが終了するまでsystem関数から戻らない。 CreateProcess関数の場合には、WaitForSingleObject関数を使って実行を待つこともできるし、 起動させるだけで、別の処理に移ることもできる。 起動したプログラムの完了を待たない場合、次のように記述をシンプル化できる。

    PROCESS_INFORMATION pi = { 0 }; 
    STARTUPINFO si = { sizeof(STARTUPINFO) };
    CreateProcess(NULL, szCmd, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);

STARTUPINFO構造体の定義を下に示す。最初の要素は構造体のサイズである。 この値だけ設定して CreateProcess関数をコールする。上のようにすれば、第2要素以降はゼロクリアされる。

typedef struct _STARTUPINFO {
  DWORD  cb;
  LPTSTR lpReserved;
  LPTSTR lpDesktop;
  LPTSTR lpTitle;
  DWORD  dwX;
  DWORD  dwY;
  DWORD  dwXSize;
  DWORD  dwYSize;
  DWORD  dwXCountChars;
  DWORD  dwYCountChars;
  DWORD  dwFillAttribute;
  DWORD  dwFlags;
  WORD   wShowWindow;
  WORD   cbReserved2;
  LPBYTE lpReserved2;
  HANDLE hStdInput;
  HANDLE hStdOutput;
  HANDLE hStdError;
} STARTUPINFO, *LPSTARTUPINFO;

PROCESS_INFORMATION構造体の定義を下に示す。

typedef struct _PROCESS_INFORMATION {
  HANDLE hProcess;
  HANDLE hThread;
  DWORD  dwProcessId;
  DWORD  dwThreadId;
} PROCESS_INFORMATION, *LPPROCESS_INFORMATION;

この場合、次のようにすれば、第1要素は 0 にセットされ、後続の要素は全てゼロにセットされる。 つまり構造体の初期は先頭からいくつか初期値を与えれば、残りの要素はゼロクリアされる。 もちろん、全ての要素の初期値を省略したときは、ゼロクリアされない。

 PROCESS_INFORMATION pi = { 0 };

構造体の初期化時間が問題になることは滅多にないが、実行時間を極限まで短縮したい場合、 コンパイラによっては前のプログラムで示したように memset関数を使って初期化する方が 高速な場合もある。

3.ShellExecute関数

shell32.dll に含まれるShellExecute関数でプロセスを起動する方法もある。 ヘッダファイルは shellapi.h である。

例えば ShellExecute(NULL, "open", "notepad", NULL, NULL, SW_SHOWNORMAL); とすれば、Windowsアプリケーションから NotePad が起動できる。