プロセス
実行ファイル(.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
-
プロセスに渡すコマンドライン引数の指定です。
引数は半角スペースを区切り文字として複数指定することが出来ます。引数
lpApplicationName
がNULL
の場合、最初の引数は実行ファイル名である必要があります。
実行ファイル名(パス名)に半角スペースが含まれる場合、パス全体をダブルクォーテーション("")で囲う必要があります。
実行ファイル名に拡張子がない場合は「.exe」が追加されますが、ファイル名がピリオドで終わる場合、およびファイル名にパスが含まれる場合は追加されません。
この関数は実行ファイル名と引数の間にNULL文字を追加するため、元の文字列はふたつに分離されます。
実行ファイル名は相対パスでの指定も可能ですが、ファイル名のみを指定した場合(ディレクトリパスが含まれない場合)は以下の順番で実行ファイルの場所を検索します。
(lpApplicationName
とはルールが異なるので注意)- アプリケーションの読み込み元ディレクトリ
- 親プロセスのカレントディレクトリ
- 32ビットWindowsシステムディレクトリ
- 16ビットWindowsシステムディレクトリ
- Windowsディレクトリ
- 環境変数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終端文字列)この引数は引数
dwCreationFlags
にCREATE_UNICODE_ENVIRONMENT
フラグを含めない場合、Unicode文字は使用できません。
NULL
を指定すると呼び出し元プロセスの環境変数が使用されます。 - lpCurrentDirectory
-
新しいプロセスのカレントディレクトリとなるパス文字列を絶対パスで指定します。
NULL
を指定すると呼び出し元プロセスのカレントディレクトリが使用されます。 - lpStartupInfo
-
STARTUPINFO
構造体のポインタを指定します。
STARTUPINFO構造体はプロセス起動時のウィンドウステーションや外観の指定、および新しいプロセス作成後に標準入出力のハンドルが格納されます。
メンバについてはSTARTUPINFO構造体で説明します。
なお、引数dwCreationFlags
にEXTENDED_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 | 引数lpStartupInfo はSTARTUPINFOEX 構造体である。このフラグを指定しない場合は 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構造体
引数lpProcessAttributes
やbInheritHandles
はSECURITY_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 dwXSize
、dwYSize
メンバを使用するSTARTF_USEPOSITION dwX
、dwY
メンバを使用するSTARTF_USECOUNTCHARS dwXCountChars
、dwYCountChars
メンバを使用するSTARTF_USEFILLATTRIBUTE dwFillAttribute
メンバを使用するSTARTF_RUNFULLSCREEN コンソールアプリケーションの場合に、コンソールウィンドウを全画面表示で実行する
(x86システムのみ)STARTF_FORCEONFEEDBACK 起動時にカーソルを2秒間「矢印+砂時計」にする
2秒以内にGUI機能が呼び出されるとさらに5秒、その5秒以内にウィンドウが表示されるとさらに5秒与えられる
ただしGetMessage関数が呼び出された時点で残り時間に関係なくカーソルを元に戻すSTARTF_FORCEOFFFEEDBACK 起動時にカーソルを一切変更しない STARTF_USESTDHANDLES hStdInput
、hStdOutput
、hStdError
メンバを使用する
このフラグを使用する場合、プロセス作成関数で親プロセスのハンドルの継承を有効にする必要があるまた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関数の引数bInheritHandles
にTRUE
を指定する必要があります。dwFlags
メンバにSTARTF_USEHOTKEY
フラグが含まれている場合、この引数は新しいプロセスのホットキーとなるキーを指定します。
ホットキーを入力すると、ウィンドウがアクティブになります。
下位バイトは仮想キーコード、上位バイトには修飾キーを上位バイトに指定できます。
(MAKEWORD
マクロを使用します)
修飾キーはCommCtrl.h
をインクルードすると以下の定数が使用できます。定数 説明 HOTKEYF_SHIFT Shiftキー HOTKEYF_CONTROL Ctrlキー HOTKEYF_ALT Altキー HOTKEYF_EXT 拡張キー 上記以外の場合、このメンバは無視されます。
- hStdOutput
-
新しいプロセスの標準出力ハンドルを指定します。
このフラグを使用しない場合はメンバは無視され、コンソールウィンドウバッファが既定値となります。
ハンドルは継承可能で、CreateProcess関数の引数bInheritHandles
にTRUE
を指定する必要があります。 - hStdError
-
新しいプロセスの標準エラーハンドルを指定します。
このフラグを使用しない場合はメンバは無視され、コンソールウィンドウバッファが既定値となります。
ハンドルは継承可能で、CreateProcess関数の引数bInheritHandles
にTRUE
を指定する必要があります。
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
メンバに格納されています。