プロセス


プロセスを作る

プロセスには主スレッドがあり、そこからさらにスレッドを作る方法はわかりましたね
さらに、スレッドはプロセスを生成することも可能なのです
プロセスを生成するということは、同時に主スレッドを作成することでもあります
すなわち、スレッドは他の実行ファイルを実行することができます

Windows OS の元では、実行ファイルを開くことはプロセスを生成することに等しいのです
自分のプログラムのなかで、他のプログラムを実行させたければ
Windows に子プロセスを作るように指定すれば良いということになります

子プロセスの生成には CreateProcess() 関数を使います
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 
);
lpApplicationName には、実行ファイル名を
lpCommandLine には、実行ファイルに渡すコマンドライン引数を指定します

lpApplicationName で指定した実行ファイル名は
ファイル名だけならカレントディレクトリを、完全修飾パスなら、そのパスを実行します
lpCommandLine が NULL ならば、lpApplicationName がコマンドラインと認識されます

逆に lpApplicationName が NULL ならば
lpCommandLine の最初のトークンが実行ファイル名であると認識されます
この場合、Windows は次の順で実行ファイルの位置を検索します

1.アプリケーションがロードされたディレクトリ
2.親プロセスのカレントディレクトリ
3.Windows のシステムディレクトリ
4.Windows ディレクトリ
5.環境変数 PATH に記述されている各ディレクトリ

lpProcessAttributes には、セキュリティ属性構造体へのポインタを指定します
取得したハンドルの子プロセスへの継承を許可するかどうかを決めます
NULL を指定すれば、ハンドルは継承されません

lpThreadAttributes は lpProcessAttributes と同じ意味で
指定したセキュリティ情報が、おそらく、主スレッドに割り当てられます
これら二つの引数は、Windows 9x 系では NULL となります

bInheritHandles は、新しいプロセスが親プロセスの
継承可能なハンドルを継承する時は TRUE、しないならば FALSE を指定します
継承したハンドルは、親プロセスのハンドルを同じ値、同じアクセス権を持つことになります

dwCreationFlags は、プロセスの実行方法をフラグで指定します
フラグは、作成フラグと優先順位フラグに分類されます
この引数に指定する定数は、下記のリファレンスを参照してください

lpEnvironment には、プロセスの環境ブロックへのポインタを指定します
NULL を指定すれば、親プロセスの環境ブロックが渡されます
環境ブロックとは、プロセスに渡されるシステム情報です
これについては、後ほど詳しく解説することにします

lpCurrentDirectory は、新しいプロセスのカレントディレクトリのパスが格納されている
カレントドライブ名も含まれた、文字列へのポインタを指定します

lpStartupInfo は、新しいプロセスの表示状態が格納されている
STARTUPINFO 構造体へのポインタを指定します
lpProcessInfomation には、新しいプロセスの情報を格納する
PROCESS_INFOMATION 構造体へのポインタを指定します

関数が成功すると 0 以外、失敗すると 0 が返ります

STARTUPINFO 構造体は次のように定義されています
typedef struct _STARTUPINFO { // si 
    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;
cb メンバは、この構造体のサイズを指定します
lpReserved は、予約されているメンバで NULL を指定します

lpDesktop メンバは、Windows NT でのみ、プロセスを起動するデスクトップの名前を指定します
指定されたデスクトップが無ければ、その名前でデフォルト属性のデスクトップが作成されます
NULL を指定すれば、カレントデスクトップが指定されたと認識します

lpTitle には、コンソールアプリケーションのウィンドウタイトルを指定します
NULL を指定すると、実行ファイル名がウィンドウタイトルになります

dwX と dwY には、ウィンドウの初期 X 座標、Y 座標を指定します
プロセスが CreateWindow() で CW_USERDEFAULT を指定した時、これが使われます

dwXSize と dwYSize にはウィンドウの幅と高さを指定します
dwX、dwY 同様に、プロセスが CW_USERDEFAULT を指定した時、これが使われます

dwXCountChars と dwYCountChars は、コンソールウィンドウの高さと幅を指定します
ここで指定する単位は、キャラクタ単位です

dwFillAttribute には、コンソールの文字色と背景色を指定します
このメンバには、次の定数を組み合わせて指定することができます

定数
FOREGROUND_BLUE
FOREGROUND_GREEN
FOREGROUND_RED
FOREGROUND_INTENSITY
BACKGROUND_BLUE
BACKGROUND_GREEN
BACKGROUND_RED
BACKGROUND_INTENSITY

dwFlags には、プロセスがウィンドウを作る時
この構造体の、どのメンバを使うかを以下の定数の組み合わせで指定します

定数メンバ
STARTF_USESHOWWINDOW wShowWindow
STARTF_USEPOSITION dwX 及び dwY
STARTF_USESIZE dwXSize 及び dwYSize
STARTF_USECOUNTCHARS dwXCountChars 及び dwYCountChars
STARTF_USEFILLATTRIBUTE dwFillAttribute
STARTF_FORCEONFEEDBACK プロセス起動時に、カーソルを砂時計に変更する
STARTF_FORCEOFFFEEDBACK プロセス起動時に、カーソルを変更しない
STARTF_USESTDHANDLES hStdInput、hStdOutput、及び hStdError

wShowWindow には、ウィンドウをどのように表示するかを表す定数を指定します
これは、ShowWindow() 関数で SW_SHOWDEFAULT を指定した時に使われます
このメンバには、次の定数のいずれかを指定します

定数解説
SW_HIDE ウィンドウを非表示にし、他のウィンドウをアクティブにします
SW_MAXIMIZE ウィンドウを最大化します
SW_MINIMIZE ウィンドウを最小化し、Z 順位が次のトップレベルウィンドウをアクティブにします
SW_RESTORE ウィンドウをアクティブにし、表示します
ウィンドウが最小化されていたり最大化されていたりすると
元の位置とサイズに戻ります
SW_SHOW ウィンドウをアクティブにして、現在の位置とサイズで表示します
SW_SHOWMAXIMIZED ウィンドウをアクティブにして、最大化します
SW_SHOWMINIMIZED ウィンドウをアクティブにして、最小化します
SW_SHOWMINNOACTIVE ウィンドウを最小化します
アクティブなウィンドウは、アクティブな状態を維持します
非アクティブなウィンドウは、非アクティブなままです
SW_SHOWNA ウィンドウを現在の状態で表示します
アクティブなウィンドウはアクティブな状態を維持します
SW_SHOWNOACTIVATE ウィンドウを直前の位置とサイズで表示します
アクティブなウィンドウはアクティブな状態を維持します
SW_SHOWNORMAL ウィンドウをアクティブにして、表示します
ウィンドウが最小化または最大化されているときは、位置とサイズを元に戻します

cbReserved2 と lpReserved2 は予約されているメンバで、常に 0 です
hStdInput には、コンソールの標準入力のハンドルを
hStdOutpu は標準出力、hStdError には標準エラーのハンドルをそれぞれ指定します

この構造体には、いくつかコンソールについて設定するものがありました
Win32 アプリケーションからコンソールを表示する方法については、後ほど説明します

PROCESS_INFORMATION 構造体は、次のように定義されています
この構造体は、CreateProcess() 関数が初期化してくれます
typedef struct _PROCESS_INFORMATION { // pi 
    HANDLE hProcess; 
    HANDLE hThread; 
    DWORD dwProcessId; 
    DWORD dwThreadId; 
} PROCESS_INFORMATION;
hProcess は、作成したプロセスのハンドル
hThread は、プロセスの主スレッドのハンドルが格納されます
dwProcessId は、プロセスの ID、dwThreadId 主スレッドの ID を表します
この構造体で、作成した子プロセスにアクセスすることができます

STARTUPINFO 構造体は GetStartupInfo() 関数で得ることもできます
この関数は、呼び出したプロセスが作成された時に使用された
STARTUPINFO 構造体の内容を取得します

VOID GetStartupInfo(LPSTARTUPINFO lpStartupInfo);

lpStartupInfo には、STARTUPINFO 構造体へのポインタを指定します
ここに、プロセス起動時の STARTUPINFO の内容が格納されます
これを使えば、CreateProcess() で親プロセスの STARTUPINFO 構造体を渡せます
#include <windows.h>

#define BUTTON_OK 1

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	TCHAR strExe[1024];
	PROCESS_INFORMATION ps;
	static STARTUPINFO si;
	static HWND hEditExe , hButton;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		hEditExe = CreateWindow(
			TEXT("EDIT") , TEXT("") ,
			WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL ,
			10 , 10 , 200 , 25 , hWnd , NULL ,
			((LPCREATESTRUCT)(lp))->hInstance , NULL
		);
		hButton = CreateWindow(
			TEXT("BUTTON") , TEXT("OK") ,
			WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON ,
			230 , 10 , 100 , 25 , hWnd , (HMENU)BUTTON_OK ,
			((LPCREATESTRUCT)(lp))->hInstance , NULL
		);
		GetStartupInfo(&si);
		return 0;
	case WM_COMMAND:
		if (LOWORD(wp) == BUTTON_OK) {
			GetWindowText(hEditExe , strExe , 1024);
			if (!CreateProcess(NULL , strExe , NULL , NULL , FALSE , 
						0 , NULL , NULL , &si , &ps))
				MessageBox(hWnd , TEXT("実行できません") , NULL , MB_OK);
		}
		return 0;
	}
	return DefWindowProc(hWnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow) {
	HWND hWnd;
	MSG msg;
	WNDCLASS winc;

	winc.style 		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return 1;

	hWnd = CreateWindow(
			TEXT("KITTY") , TEXT("Kitty on your lap") ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);

	if (hWnd == NULL) return 1;

	while (GetMessage(&msg , NULL , 0 , 0 )) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return msg.wParam;
}


このプログラムを実行すると、ウィンドウにエディットボックスとボタンがあるので
エディットボックスにコマンドを入力して、OK ボタンを押してください
すると、入力した実行ファイル名でプロセスが作成され、実行されます

子プロセスを作ることによって、待機関数を使って同期をとることも可能になります
例えば、子プロセスが実行中は、親プロセスが停止するというようなプログラムです
それは、部分的なスレッドでも良いですし、主スレッドを停止させてビジーにすることもできます


CreateProcess()

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 
);
新しいプロセスを生成します

lpApplicationName - 実行ファイル名を指定します
lpCommandLine - 実行ファイルに渡すコマンドライン引数を指定します
lpProcessAttributes - セキュリティ属性構造体へのポインタを指定します
lpThreadAttributes - セキュリティ属性構造体へのポインタを指定します
bInheritHandles - ハンドルを継承する時は TRUE、しないならば FALSE を指定します
dwCreationFlags - 作成フラグと優先順位フラグを指定します
lpEnvironment - プロセスの環境ブロックへのポインタを指定します
lpCurrentDirectory - カレントディレクトリ名が入った文字列へのポインタを指定します
lpStartupInfo - STARTUPINFO 構造体へのポインタを指定します
lpProcessInfomation - PROCESS_INFOMATION 構造体へのポインタを指定します

dwCreationFlags には、以下の定数を組み合わせて指定します

定数解説
作成フラグ
CREATE_DEFAULT_ERROR_MODE 新しいプロセスに、呼び出し側プロセスのエラーモードを継承させません
新しいプロセスには、デフォルトのエラーモードを適用します
CREATE_NEW_CONSOLE 新しいプロセスに、親のコンソールを継承させず、新しいコンソールを持たせます
DETACHED_PROCESS と同時に指定することはできません
CREATE_NEW_PROCESS_GROUP 新しいプロセスを、新しいプロセスグループのルートプロセスにします
CREATE_SEPARATE_WOW_VDM Windows NT:
新しいプロセスを、プライベートな仮想 DOS マシン上で実行させます
このフラグは、16 ビットのアプリケーションを起動するときにだけ有効です
CREATE_SHARED_WOW_VDM Windows NT:
WIN.INI ファイルの Windows セクション内の
DefaultSeparateVDM スイッチが TRUE のとき
そのスイッチを無効にし、新しいプロセスを共有された
仮想 DOS マシン上で実行させます
このフラグは、16 ビットのアプリケーションを起動するときにだけ有効です
CREATE_SUSPENDED 新しいプロセスを、プライマリースレッドをサスペンド状態にして起動します
スレッドを実行するには、ResumeThread() 関数を使います
CREATE_UNICODE_ENVIRONMENT lpEnvironment パラメータが指す環境ブロックが
Unicode 文字を使用していることを示します
デフォルトでは、ANSI 文字が使用されているものとみなされます
DEBUG_PROCESS 呼び出し側プロセスがデバッガーのときに指定します
新しいプロセスはデバッグされるプロセスとして扱われ
そのプロセス内で起きるすべてのデバッグイベントが
呼び出し側スレッドに通知されます
DEBUG_ONLY_THIS_PROCESS 呼び出し側プロセスがデバッガーのときに指定します
このフラグを指定しない場合
新しいプロセスは呼び出し側プロセスのデバッガーで
デバッグされるほかのプロセスになります
DETACHED_PROCESS 新しいプロセスに、親プロセスのコンソールへのアクセスを持たせません
新しいプロセスは、AllocConsole() 関数を使って新しいコンソールを作成できます
CREATE_NEW_CONSOLE フラグと同時に指定することはできません
優先順位フラグ
HIGH_PRIORITY_CLASS タイムクリティカルなタスクを実行するプロセスであることを示します
IDLE_PRIORITY_CLASS システムがアイドル状態のときにだけ実行するプロセスであることを示します
NORMAL_PRIORITY_CLASS 特別なスケジューリングを必要としない
一般的なプロセスであることを示します
REALTIME_PRIORITY_CLASS 最も高い優先順位クラスを持つプロセスであることを示します
このクラスのスレッドは、重要なタスクを行うオペレーティングシステムの
プロセスを含むほかのすべてのプロセスのスレッドよりも先に実行されます
少しでも長い時間実行すると、ディスクキャッシュがフラッシュされなくなったり
マウスが応答しなくなったりします

GetStartupInfo()

VOID GetStartupInfo(LPSTARTUPINFO lpStartupInfo);

呼び出し側プロセスが起動した時に使われた
STARTUPINFO 構造体の内容を取得します

lpStartupInfo - 内容を保存する STARTUPINFO 構造体へのポインタを指定します




前のページへ戻る次のページへ