プロセス

実行ファイル(.exeファイル)を起動すると、システムにプロセスが作成されます。
プロセスは起動中の実行ファイルの実体で、メモリに展開され使用可能な状態になったプログラムです。

プロセスは別のプロセス(プログラム)から起動することができます。
全く無関係なアプリケーションを起動することもできますが、主に複数の実行ファイルを組み合わせて一つのアプリケーションを作る場合に利用されます。

プロセスはひとつ以上のスレッドを持ちます。
スレッドをひとつだけ持つものをシングルスレッドプログラム、ふたつ以上のスレッドを持つものをマルチスレッドプログラムといいます。
プロセス内のスレッドをすべて終了させるとプロセスも終了します。

プロセスの作成

CreateProcess関数

プロセスはCreateProcess関数で作成できます。
この関数の引数に指定する値はかなり複雑ですが、特別なことをしない限りはそこまで難しくはありません。

BOOL CreateProcessW(
 LPCWSTR lpApplicationName,
 LPWSTR lpCommandLine,
 LPSECURITY_ATTRIBUTES lpProcessAttributes,
 LPSECURITY_ATTRIBUTES lpThreadAttributes,
 BOOL bInheritHandles,
 DWORD dwCreationFlags,
 LPVOID lpEnvironment,
 LPCWSTR lpCurrentDirectory,
 LPSTARTUPINFOW lpStartupInfo,
 LPPROCESS_INFORMATION lpProcessInformation
);
新しいプロセスを作成する。
成功した場合は0以外を、失敗した場合は0を返す。
lpApplicationName

起動する実行ファイル名の指定です。
絶対パスでの指定のほか、相対パスでも指定可能です。
拡張子(.exe)まで含める必要があります。
NULLを指定すると次の引数lpCommandLineの最初のトークン(区切られた文字列)を実行ファイル名として使用します。

lpCommandLine

プロセスに渡すコマンドライン引数の指定です。
引数は半角スペースを区切り文字として複数指定することが出来ます。

引数lpApplicationNameNULLの場合、最初の引数は実行ファイル名である必要があります。
実行ファイル名(パス名)に半角スペースが含まれる場合、パス全体をダブルクォーテーション("")で囲う必要があります。
実行ファイル名に拡張子がない場合は「.exe」が追加されますが、ファイル名がピリオドで終わる場合、およびファイル名にパスが含まれる場合は追加されません。
この関数は実行ファイル名と引数の間にNULL文字を追加するため、元の文字列はふたつに分離されます。
実行ファイル名は相対パスでの指定も可能ですが、ファイル名のみを指定した場合(ディレクトリパスが含まれない場合)は以下の順番で実行ファイルの場所を検索します。
(lpApplicationNameとはルールが異なるので注意)

  1. アプリケーションの読み込み元ディレクトリ
  2. 親プロセスのカレントディレクトリ
  3. 32ビットWindowsシステムディレクトリ
  4. 16ビットWindowsシステムディレクトリ
  5. Windowsディレクトリ
  6. 環境変数PATHで指定されているディレクトリ

この関数のワイド文字版は引数lpCommandLineを書き換えることがあります。
そのため、ここに書き換えできない値(const変数、文字列リテラル)を指定するとエラーが発生する可能性があります。

コマンドライン引数はC言語で作られたプログラムならばmain関数の引数として取得できます。
Windows APIではGetCommandLine関数で取得することができます。

lpProcessAttributes

新しいプロセスのプロセスハンドルを子プロセスに継承可能かどうかを指定するSECURITY_ATTRIBUTES構造体のハンドルを指定します。
NULLを指定すると継承されません。

bInheritHandles

新しいプロセスのプライマリスレッドハンドルを子プロセスに継承可能かどうかを指定するSECURITY_ATTRIBUTES構造体のハンドルを指定します。
NULLを指定すると継承されません。

bInheritHandles

呼び出し側のプロセスが持つオブジェクトの継承可能なハンドルを、新しいプロセスに継承させるか否かを指定します。
TRUEなら継承可能、FALSEなら継承不可です。

dwCreationFlags

作成するプロセスの属性を定数の組み合わせで指定します。
定数は引数dwCreationFlagsで説明します。

lpEnvironment

新しいプロセスの環境ブロックを文字列で指定します。
環境ブロックとはプロセスが使用する環境変数をまとめたものです。
環境変数は「変数名=値\0」という書式で記述されます。
変数名と値とは「=」で区切り、各環境変数の区切りにはNULL文字が使用されます。
環境ブロックの終端はNULL文字が使用されます。
(→ダブルNULL終端文字列)

この引数は引数dwCreationFlagsCREATE_UNICODE_ENVIRONMENTフラグを含めない場合、Unicode文字は使用できません。
NULLを指定すると呼び出し元プロセスの環境変数が使用されます。

lpCurrentDirectory

新しいプロセスのカレントディレクトリとなるパス文字列を絶対パスで指定します。
NULLを指定すると呼び出し元プロセスのカレントディレクトリが使用されます。

lpStartupInfo

STARTUPINFO構造体のポインタを指定します。
STARTUPINFO構造体はプロセス起動時のウィンドウステーションや外観の指定、および新しいプロセス作成後に標準入出力のハンドルが格納されます。
メンバについてはSTARTUPINFO構造体で説明します。
なお、引数dwCreationFlagsEXTENDED_STARTUPINFO_PRESENTを含める場合、この引数はSTARTUPINFOEX構造体のポインタを指定します。

lpProcessInformation

PROCESS_INFORMATION構造体のポインタを指定します。
PROCESS_INFORMATION構造体は新しいプロセスのプロセスハンドルやプロセスID等が格納されます。
メンバについてはPROCESS_INFORMATION構造体で説明します。

引数dwCreationFlags

CreateProcess関数の第六引数dwCreationFlagsは以下の定数の組み合わせを指定します。

定数 説明
作成フラグ
DEBUG_PROCESS 新しいプロセス、およびそのプロセスから作成されたプロセス(子孫プロセス)をデバッグする。
DEBUG_ONLY_THIS_PROCESS 子孫プロセスはデバッグしない。
CREATE_SUSPENDED プライマリスレッドが休止状態のプロセスを作成する。
スレッドはResumeThread関数で実行する。
DETACHED_PROCESS コンソールアプリのプロセスを作成する時、親プロセスのコンソールを継承しない。
CREATE_NEW_CONSOLEフラグとは同時に指定できない。
CREATE_NEW_CONSOLE 新しいプロセス用のコンソールを作成する。
DETACHED_PROCESSフラグとは同時に指定できない。
CREATE_NEW_PROCESS_GROUP 新しいプロセスを、新しいプロセスグループのルートプロセスにする。
プロセスIDは引数lpProcessInformation(PROCESS_INFORMATION構造体)に格納されるプロセスIDと同じ。
CREATE_UNICODE_ENVIRONMENT 引数lpEnvironmentが示す環境ブロックはUnicode文字であることを示す。
このフラグを指定しない場合はANSI文字である。
CREATE_SEPARATE_WOW_VDM 新しいプロセスを仮想DOSマシン内で実行する。
このフラグは16ビットWindows用のアプリケーションを起動するときのみ指定できる。
CREATE_SHARED_WOW_VDM WIN.INIファイルのWindowsセクション内の「DefaultSeparateVDM」スイッチがTRUEの場合でも共有仮想DOSマシン内で実行する。
このフラグは16ビットWindows用のアプリケーションを起動するときのみ指定できる。
CREATE_BREAKAWAY_FROM_JOB 呼び出し元プロセスがジョブに関連付けられている場合、新しいプロセスをジョブに関連付けない。
呼び出し元プロセスに関連付けられているジョブはJOB_OBJECT_LIMIT_BREAKAWAY_OK制限が設定されている必要がある。
CREATE_DEFAULT_ERROR_MODE 新しいプロセスは呼び出し元プロセスのエラーモードを継承せず、デフォルトのエラーモードを取得する。
CREATE_NO_WINDOW 新しいプロセスがコンソールアプリの場合、コンソールウィンドウを表示しない。
コンソールアプリではない場合、またはCREATE_NEW_CONSOLEフラグおよびDETACHED_PROCESSフラグが指定されている場合は無視される。
EXTENDED_STARTUPINFO_PRESENT 引数lpStartupInfoSTARTUPINFOEX構造体である。
このフラグを指定しない場合はSTARTUPINFO構造体である。
(Widows XP/Server 2003ではこのフラグはサポートされない)
CREATE_DEFAULT_ERROR_MODE 新しいプロセスは呼び出し元プロセスのエラーモードを継承せず、デフォルトのエラーモードを取得する。
優先度フラグ
NORMAL_PRIORITY_CLASS 通常
IDLE_PRIORITY_CLASS 低優先度
(システムがアイドル状態の場合に実行される)
HIGH_PRIORITY_CLASS 高優先度
REALTIME_PRIORITY_CLASS 可能な限り高い優先度
BELOW_NORMAL_PRIORITY_CLASS アイドル以上、通常以下
ABOVE_NORMAL_PRIORITY_CLASS 通常以上、高優先度以下

プロセスの優先度フラグを指定しない場合、NORMAL_PRIORITY_CLASS(通常優先度)が適用されます。
ただし呼び出し元のプロセスの優先度がIDLE_PRIORITY_CLASSまたはBELOW_NORMAL_PRIORITY_CLASSの場合はそれらが適用されます。

SECURITY_ATTRIBUTES構造体

引数lpProcessAttributesbInheritHandlesSECURITY_ATTRIBUTES構造体のポインタで、セキュリティ記述子の指定です。
この構造体は以下のようになっています。

typedef struct _SECURITY_ATTRIBUTES {
 DWORD nLength;
 LPVOID lpSecurityDescriptor;
 BOOL bInheritHandle;
} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;
セキュリティ記述子を格納する構造体。
nLength

この構造体のサイズです。
sizeof(SECURITY_ATTRIBUTES)を指定します。

lpSecurityDescriptor

オブジェクトのアクセス権を設定するSECURITY_DESCRIPTOR構造体のポインタです。
この構造体のメンバはプログラマが直接編集することはせず、専用の関数で値を取得/設定します。
アクセス権は複雑なためここでは説明しません。
NULLを指定すると既定のアクセス権が設定されます。

bInheritHandle

呼び出し元プロセスが持つ継承可能なハンドルを継承するか否かをTRUE/FALSEで設定します。

STARTUPINFO構造体

CreateProcess関数の第九引数lpStartupInfoはSTARTUPINFO構造体のポインタを指定します。

typedef struct _STARTUPINFOW {
 DWORD cb;
 LPWSTR lpReserved;
 LPWSTR lpDesktop;
 LPWSTR 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;
} STARTUPINFOW, *LPSTARTUPINFOW;
新しいプロセスのウィンドウステーションや外観の情報を格納する構造体。
cb

この構造体のサイズです。
つまりsizeof(STARTUPINFO)を指定します。

lpReserved

このメンバはシステムが予約済です。
NULL(0)を指定します。

lpDesktop

ウィンドウステーションおよびデスクトップ名を文字列で指定します。
システムは複数のデスクトップを作成することができ、通常は「WinSta0\Default」というデスクトップが使用されています。
NULL(0)を指定すると親プロセスのデスクトップを使用します。

lpTitle

コンソールプロセスを作成する場合に、コンソールウィンドウのタイトルとなる文字列です。
コンソールウィンドウを作成しない場合、および実行可能ファイル名を使用する場合はNULLを指定します。

dwX、dwY

新しいプロセスが作成するウィンドウのX座標/Y座標です。
作成するプロセスのCreateWindow関数で、オーバーラップウィンドウを作成し、座標にCW_USEDEFAULTが指定されている場合にこの値が使用されます。
このメンバはdwFlagsメンバにSTARTF_USEPOSITIONフラグが含まれていない場合は無視されます。

dwXSize、dwYSize

新しいプロセスが作成するウィンドウの横幅/縦幅です。
作成するプロセスのCreateWindow関数で、オーバーラップウィンドウを作成し、サイズにCW_USEDEFAULTが指定されている場合にこの値が使用されます。
このメンバはdwFlagsメンバにSTARTF_USESIZEフラグが含まれていない場合は無視されます。

dwXCountChars、dwYCountChars

新しいプロセスがコンソールプロセスである場合、作成されるコンソールウィンドウの横幅/縦幅を文字数で指定します。
このメンバはdwFlagsメンバにSTARTF_USECOUNTCHARSフラグが含まれていない場合は無視されます。

dwFillAttribute

新しいプロセスがコンソールプロセスである場合、作成されるコンソールウィンドウの初期テキストと背景色を指定します。 このメンバはdwFlagsメンバにSTARTF_USEFILLATTRIBUTEフラグが含まれていない場合は無視されます。

このメンバは以下の定数の組み合わせを指定します。
色は合成されます。
(例えばFOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUEで白になる)

定数 説明
FOREGROUND_RED 赤(テキスト)
FOREGROUND_GREEN 緑(テキスト)
FOREGROUND_BLUE 青(テキスト)
FOREGROUND_INTENSITY 強調(テキスト)
BACKGROUND_RED 赤(背景色)
BACKGROUND_GREEN 緑(背景色)
BACKGROUND_BLUE 青(背景色)
BACKGROUND_INTENSITY 強調(背景色)
dwFlags

この構造体の有効なメンバ、および新しいプロセスの初期動作を以下の定数の組み合わせで指定します。
(ここで指定しないメンバは無視されます)

定数 説明
STARTF_USESHOWWINDOW wShowWindowメンバを使用する
STARTF_USESIZE dwXSizedwYSizeメンバを使用する
STARTF_USEPOSITION dwXdwYメンバを使用する
STARTF_USECOUNTCHARS dwXCountCharsdwYCountCharsメンバを使用する
STARTF_USEFILLATTRIBUTE dwFillAttributeメンバを使用する
STARTF_RUNFULLSCREEN コンソールアプリケーションの場合に、コンソールウィンドウを全画面表示で実行する
(x86システムのみ)
STARTF_FORCEONFEEDBACK 起動時にカーソルを2秒間「矢印+砂時計」にする
2秒以内にGUI機能が呼び出されるとさらに5秒、その5秒以内にウィンドウが表示されるとさらに5秒与えられる
ただしGetMessage関数が呼び出された時点で残り時間に関係なくカーソルを元に戻す
STARTF_FORCEOFFFEEDBACK 起動時にカーソルを一切変更しない
STARTF_USESTDHANDLES hStdInputhStdOutputhStdErrorメンバを使用する
このフラグを使用する場合、プロセス作成関数で親プロセスのハンドルの継承を有効にする必要があるまたSTARTF_USEHOTKEYフラグとは同時に指定できない
STARTF_USEHOTKEY hStdInputメンバを使用する
STARTF_USESTDHANDLESフラグとは同時に指定できない
STARTF_TITLEISLINKNAME lpTitleメンバにはこのプロセスを実行するために呼び出したショートカットファイル(.lnk)のパスが含まれる
STARTF_TITLEISAPPIDフラグとは同時に指定できない
STARTF_TITLEISAPPID lpTitleメンバにはAppUserModelIDが含まれる
STARTF_TITLEISLINKNAMEフラグとは同時に指定できない
STARTF_PREVENTPINNING タスクバーへのピン留めをできなくする
STARTF_TITLEISAPPIDフラグと同時に指定する必要がある
STARTF_UNTRUSTEDSOURCE コマンドラインは信頼できないソースから取得されている
(Webコンテンツなど)
wShowWindow

新しいプロセス内でのShowWindow関数の引数に指定する定数SW_SHOWDEFAULTに設定する値を指定します。
ここに指定できる値はShowWindow関数の引数に指定する定数のうち、SW_SHOWDEFAULT以外の値です。
新しいプロセスのウィンドウの初期状態はここに指定した値により決定されます。
(→ShowWindow関数)
このメンバはdwFlagsメンバにSTARTF_USESHOWWINDOWフラグが含まれていない場合は無視されます。

cbReserved2

このメンバはシステムが予約済です。
0を指定します。

lpReserved2

このメンバはシステムが予約済です。
NULL(0)を指定します。

hStdInput

dwFlagsメンバにSTARTF_USESTDHANDLESフラグが含まれている場合、この引数は新しいプロセスの標準入力ハンドルを指定します。
このフラグを使用しない場合はキーボードが既定値となります。
ハンドルは継承可能で、CreateProcess関数の引数bInheritHandlesTRUEを指定する必要があります。

dwFlagsメンバにSTARTF_USEHOTKEYフラグが含まれている場合、この引数は新しいプロセスのホットキーとなるキーを指定します。
ホットキーを入力すると、ウィンドウがアクティブになります。
下位バイトは仮想キーコード、上位バイトには修飾キーを上位バイトに指定できます。
(MAKEWORDマクロを使用します)
修飾キーはCommCtrl.hをインクルードすると以下の定数が使用できます。

定数 説明
HOTKEYF_SHIFT Shiftキー
HOTKEYF_CONTROL Ctrlキー
HOTKEYF_ALT Altキー
HOTKEYF_EXT 拡張キー

上記以外の場合、このメンバは無視されます。

hStdOutput

新しいプロセスの標準出力ハンドルを指定します。
このフラグを使用しない場合はメンバは無視され、コンソールウィンドウバッファが既定値となります。
ハンドルは継承可能で、CreateProcess関数の引数bInheritHandlesTRUEを指定する必要があります。

hStdError

新しいプロセスの標準エラーハンドルを指定します。
このフラグを使用しない場合はメンバは無視され、コンソールウィンドウバッファが既定値となります。
ハンドルは継承可能で、CreateProcess関数の引数bInheritHandlesTRUEを指定する必要があります。

PROCESS_INFORMATION構造体

CreateProcess関数の最後の引数lpProcessInformationはPROCESS_INFORMATION構造体のポインタを指定します。
この構造体のメンバは値をセットして関数に渡すのではなく、関数から情報を得るために使用されます。

typedef struct _PROCESS_INFORMATION {
 HANDLE hProcess;
 HANDLE hThread;
 DWORD dwProcessId;
 DWORD dwThreadId;
} PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION
新しいプロセスのハンドルやIDを格納する構造体。
hProcess

新しいプロセスのハンドルが格納されます。

hThread

新しいプロセスのプライマリスレッド(メインスレッド)のハンドルが格納されます。

dwProcessId

新しいプロセスのIDが格納されます。

dwThreadId

新しいプロセスのプライマリスレッドのIDが格納されます。

CreateProcess関数が成功した場合、hProcessメンバとhThreadメンバは不要になったらCloseHandle関数で閉じる必要があります。

サンプルコード


#include <windows.h>
#include <strsafe.h>

//ウィンドウの生成等は省略

#define IDC_EDIT1 100
#define IDC_BUTTON1 101

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static hEdit;

	WCHAR *filepath;
	STARTUPINFO si;
	PROCESS_INFORMATION pi;

	switch (message)
	{
	case WM_CREATE: //ウィンドウの作成
		hEdit = CreateWindowEx(
			WS_EX_CLIENTEDGE,
			L"EDIT", L"notepad",
			WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL,
			10, 10, 250, 25,
			hWnd, (HMENU)IDC_EDIT1, hInst, NULL);
		CreateWindow(L"BUTTON", L"プロセス開始",
			WS_CHILD | WS_VISIBLE,
			10, 40,
			120, 25,
			hWnd, (HMENU)IDC_BUTTON1, hInst, NULL);
		break;

	case WM_COMMAND:
		switch (LOWORD(wParam))
		{
		case IDC_BUTTON1:
		{
			//エディットコントロールからパスを取得
			int len = GetWindowTextLength(hEdit);
			if (len <= 0)
				break;
			WCHAR *buf = (WCHAR*)malloc(sizeof(WCHAR) * (len + 1));
			if (!buf)
				break;
			filepath = (WCHAR*)malloc(sizeof(WCHAR) * (len + 3));
			if (!filepath)
				break;

			GetWindowText(hEdit, buf, len + 1);

			//パス文字列をダブルクォーテーションで囲う
			if (*(buf + len - 1) == L'"') {
				StringCchPrintf(filepath, len + 3, *buf == L'"' ? "%s" : L"\"%s", buf);
			}
			else {
				StringCchPrintf(filepath, len + 3, L"\"%s\"", *buf == L'"' ? buf + 1 : buf);
			}
			free(buf);
		}
			si = (STARTUPINFO){ 0 };
			si.cb = sizeof(STARTUPINFO);
			pi = (PROCESS_INFORMATION){ 0 };
			
			if(CreateProcess(
				NULL,			//実行ファイル名
				filepath,		//コマンドライン引数
				NULL,			//プロセスハンドルの継承不可
				NULL,			//スレッドハンドルの継承不可
				FALSE,			//ハンドルを継承しない
				0,				//プロセス属性なし(全て既定の設定)
				NULL,			//呼び出し元の環境ブロックを使用
				NULL,			//呼び出し元のカレントディレクトリを使用
				&si,			//STARTUPINFO構造体のポインタ
				&pi				//PROCESS_INFORMATION構造体のポインタ
			)) {
				//ハンドルを閉じる
				CloseHandle(pi.hThread);
				CloseHandle(pi.hProcess);
			}
			free(filepath);
			break;
		}
		break;

	case WM_DESTROY: //ウィンドウの破棄
		PostQuitMessage(0);
		break;

	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

エディットコントロールに入力した実行ファイル名からプロセスを作成するサンプルコードです。
(実行ファイル名のみ。コマンドライン引数には対応していません)
プロセスの作成のサンプル

エディットコントロールの初期値は「notepad」という文字列にしています。
これはWindows標準のメモ帳が起動します。
今回のコードはCreateProcess関数の第一引数ではなく、第二引数のコマンドライン引数に実行ファイル名を渡しています。
この場合、アプリケーションの実行ディレクトリ→カレントディレクトリの順で実行ファイルを検索しますが見つからない場合はWindowsディレクトリを検索します。
「notepad.exe」はWindowsディレクトリに存在するので、ファイル名だけで実行できます。

作成された新しいプロセスは、親プロセスとは基本的に無関係に動作します。
親プロセスを終了しても子プロセスはそのまま動作し続けます。

プロセスの終了状態

GetExitCodeProcess関数

プロセスが実行中か否かを判定するにはGetExitCodeProcess関数を使用します。

BOOL GetExitCodeProcess(
 HANDLE hProcess,
 LPDWORD lpExitCode
);
プロセスhProcessの終了状態をlpExitCodeに格納する。
成功した場合は0以外を、失敗した場合は0を返す。

引数lpExitCodeはDWORD型のポインタで、関数が成功するとここにプロセスの終了状態を表す値が格納されます。
プロセスが実行中の場合はSTILL_ACTIVEという定数が格納されます。
プロセスが終了している場合はプロセスの戻り値が格納されます。
(main関数の戻り値、またはExitProcess関数等に指定された値)

なお、プロセスハンドルはPROCESS_INFORMATION構造体のhProcessメンバに格納されています。