Delphi Programming / Object Pascal
[掲載 2005年02月15日] [更新 2019年07月15日] Delphi サンプルプログラム集
500_コマンドプロンプトを CreateProcess 関数で起動
動作確認等
Windows 7 U64(SP1) + Delphi XE Pro
500_CmdPrompt2.zip [3,474 KB] 2019年07月15日版 (EXE 同梱)
2005年03月27日
表示サイズの変更コードを追加
これは CreateProcess の TProcessInformation 構造体のパラメータの設定を追加しただけ
2015年03月21日
管理者権限での起動の記事を追加
コマンドプロンプトを CreateProcess 関数で起動
CreateProcess 関数でコマンドプロンプトを起動するサンプルです。単なるCreateProcess 関数使用例とも言えます。
当サイトの、CreateProcess 関数を使用した他のサンプルとの違いはほとんどありません。違いは、起動するアプリケーションがコマンドプロセッサということです。
コマンドプロンプト (コマンドプロセッサ) もアプリケーションですから、コマンドラインとしての引数を使用することがあります。コマンドライン引数の扱いについては、アプリケーションの起動関係の記事も参考にしてください。
[備考]
cmd.exe (コマンドプロセッサ) を起動する際、作業ディレクトリを指定すると、その作業ディレクトリがカレントディレクトリになります。CreateProcess 関数では、8 番目 (最後から 3 番目) の引数で作業ディレクトリを指定します。
01_ コマンドラインオプションとコマンドを指定して起動
CreateProcess 関数は、コードを追加して、起動したアプリが終了するまで待つことができます。そこで、ここでは、起動したコマンドプロンプトを EXIT コマンド、または、閉じる [X] ボタンで終了するまで、メインフォームに制御が戻らないようにしています。
また、CrateProcess 関数の引数の TStartupInfo 構造体の値を設定することにより、表示位置を画面の左上にして、タイトルも変更しています。
図1
設計時画面
図2
ボタンクリック
コマンドプロンプトが起動
表示位置はデスクトップの左上隅
表示サイズも変更
終了しないとフォームには戻らない
図3
コマンドプロンプトの起動画面
ウィンドウのタイトルを [コマンドプロンプト] に変更
カレントディレクトリを C:\ に変更
リスト1
CreateProcess 関数でコマンドプロンプトを、位置とサイズを指定して起動
コマンドプロンプトが終了するまで待つ例
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class (TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
{$R *.DFM}
// -----------------------------------------------------------------------------
// CreateProcess関数を使用して、指定したファイルを実行する
// 起動したアプリが終了するまでこの関数が終了しないコードになっている
// -----------------------------------------------------------------------------
function WinExecAndWait32V2(FileName: String ; Visibility: Integer): DWORD;
var
LStartupInfo : TStartupInfo;
LProcessInfo : TProcessInformation;
begin
Result := 0 ;
FillChar(LStartupInfo, SizeOf(LStartupInfo), #0 );
LStartupInfo.cb := SizeOf(TStartupInfo);
// 以下の4行とdwFlagsにSTARTF_USERPOSITION,STARTF_USESIZEを追加
// 位置とサイズを指定する必要がなければこれらの設定は不要
LStartupInfo.dwX := 0 ;
LStartupInfo.dwY := 0 ;
LStartupInfo.dwXSize := 500 ;
LStartupInfo.dwYSize := 600 ;
LStartupInfo.lpTitle := PChar('コマンドプロンプト' );
LStartupInfo.dwFlags := STARTF_USESHOWWINDOW or
STARTF_USEPOSITION or
STARTF_USESIZE;
LStartupInfo.wShowWindow := Visibility;
SetLength(FileName, Length(FileName));
// 参照カウンタ対策
UniqueString(FileName);
if not CreateProcess(nil ,
PChar(FileName),
nil ,
nil ,
False,
NORMAL_PRIORITY_CLASS,
nil ,
nil ,
LStartupInfo,
LProcessInfo) then begin
Result := WAIT_FAILED;
end else begin
// 起動したプロセスが終了するまで待つ
while WaitForSingleObject(LProcessInfo.hProcess, 100 ) = WAIT_TIMEOUT do
Application.ProcessMessages;
GetExitCodeProcess(LProcessInfo.hProcess, Result);
CloseHandle(LProcessInfo.hProcess);
CloseHandle(LProcessInfo.hThread);
end ;
end ;
// =============================================================================
// コマンドプロンプトを表示
// コマンドプロンプトを終了しないと、このアプリは終了できない
// そこで、このフォームにフォーカス移動できないようにしている
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
Self.Enabled := False;
WinExecAndWait32V2('Cmd.exe /k cd /d c:\' , SW_SHOWNORMAL);
Self.Enabled := True;
end ;
end .
上のサンプルコードでは、起動したアプリを終了しない限り、起動元のプログラムは終了できません。
起動したアプリ起動後、自アプリを操作可能にするには、以下のように、CreateProcess 関係のコードを修正すれば可能です。
タイトルは起動するまで待つとなっていますが、これは実際にはコマンドプロンプトのプロセスが起動するまで待つのであって、コマンドプロンプトが入力可能な状態になるわけではありません。
このサンプルでは、自アプリを終了しても、起動したコマンドプロンプトは終了しません。
リスト2
指定したアプリ、またはファイルが起動するまで待つ例
copy code
// -----------------------------------------------------------------------------
// CreateProcess関数を使用して、指定したファイルを実行する
// -----------------------------------------------------------------------------
function WinExecAndWait32V2(FileName: String ; Visibility: Integer): DWORD;
var
LStartupInfo : TStartupInfo;
LProcessInfo : TProcessInformation;
LiRet : DWORD;
begin
Result := 0 ;
FillChar(LStartupInfo, SizeOf(LStartupInfo), #0 );
LStartupInfo.cb := SizeOf(TStartupInfo);
// 以下の4行とdwFlagsにSTARTF_USERPOSITION,STARTF_USESIZEを追加
// 位置とサイズを指定する必要がなければこれらの設定は不要
LStartupInfo.dwX := 0 ;
LStartupInfo.dwY := 0 ;
LStartupInfo.dwXSize := 500 ;
LStartupInfo.dwYSize := 600 ;
LStartupInfo.lpTitle := PChar('コマンドプロンプト' );
LStartupInfo.dwFlags := STARTF_USESHOWWINDOW or
STARTF_USEPOSITION or
STARTF_USESIZE;
LStartupInfo.wShowWindow := Visibility;
SetLength(FileName, Length(FileName));
// 参照カウンタ対策
UniqueString(FileName);
if not CreateProcess(nil ,
PChar(FileName),
nil ,
nil ,
False,
NORMAL_PRIORITY_CLASS,
nil ,
nil ,
LStartupInfo,
LProcessInfo) then begin
Result := WAIT_FAILED;
end else begin
// プロセスが起動するまで待つ
repeat
LiRet := WaitForInputIdle(LProcessInfo.hProcess, 50 );
Application.ProcessMessages;
until LiRet <> WAIT_TIMEOUT;
GetExitCodeProcess(LProcessInfo.hProcess, Result);
CloseHandle(LProcessInfo.hProcess);
CloseHandle(LProcessInfo.hThread);
end ;
end ;
// =============================================================================
// コマンドプロンプトを表示
// このアプリを終了しても、起動したコマンドプロンプトは終了しないので注意
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
WinExecAndWait32V2('Cmd.exe /k cd /d c:\' , SW_SHOWNORMAL);
end ;
03_ 管理者権限でコマンドプロンプトや他のアプリを起動
CreateProcess 関数で他のプログラムを起動する場合、そのプログラムだけを管理者権限で起動することはできません。対象のプログラムを管理者権限で起動したい場合は、自分自身を管理者権限で実行することになります。
自分自身を管理者権限として実行するには、アプリケーションマニフェストを組み込みます。アプリケーションマニフェストに、EXE 実行開始時に管理者への昇格を確認する「ユーザアカウント制御」のダイアログを表示するための記述をします。
当然ですが、起動するプログラム側にアプリケーションマニフェストを組み込んで、管理者権限で起動するようにしておけば、管理者への昇格ダイアログを表示することができます。
ShellExecute 関数または ShellExecuteEx 関数で他のプログラムを起動する場合は、アプリケーションマニフェストを使用しなくても、管理者への昇格ダイアログを表示して、そのプログラムを管理者権限で実行することができます。
図4
[ユーザーアカウント制御] ダイアログを表示する
CreateProcess 関数を使用して、コマンドプロンプトを非表示で起動するには、cmd.exe の起動オプションと CreateProcess 関数の TStartupInfo 構造体のメンバーに対して以下の設定をします。
cmd.exe の起動オプションを、/k ではなく /c にする
TStartupInfo.wShowWindow メンバの値を SW_HIDE にする
SW_HIDE を有効にするために TStartupInfo.dwFlags に STARTF_USESHOWWINDOW を追加
ただし、最初のサンプルコードで単純にこの設定をしてしまうと、プログラムが永久に終了しなくなってしまいます。
リスト3
コマンドプロンプトを非表示で起動してコマンドを実行する
copy code
// -----------------------------------------------------------------------------
// CreateProcess関数を使用して、指定プログラムを実行
// -----------------------------------------------------------------------------
function WinExecAndWait32V2(FileName: String ; Visibility: Integer): DWORD;
var
LStartupInfo : TStartupInfo;
LProcessInfo : TProcessInformation;
LiRet : DWORD;
begin
Result := 0 ;
FillChar(LStartupInfo, SizeOf(LStartupInfo), #0 );
LStartupInfo.cb := SizeOf(TStartupInfo);
LStartupInfo.dwFlags := STARTF_USESHOWWINDOW;
LStartupInfo.wShowWindow := Visibility;
SetLength(FileName, Length(FileName));
// 参照カウンタ対策
UniqueString(FileName);
if not CreateProcess(nil ,
PChar(FileName),
nil ,
nil ,
False,
NORMAL_PRIORITY_CLASS,
nil ,
nil ,
LStartupInfo,
LProcessInfo) then begin
Result := WAIT_FAILED;
end else begin
// プロセスが起動するまで待つ
repeat
LiRet := WaitForInputIdle(LProcessInfo.hProcess, 50 );
Application.ProcessMessages;
until LiRet <> WAIT_TIMEOUT;
GetExitCodeProcess(LProcessInfo.hProcess, Result);
CloseHandle(LProcessInfo.hProcess);
CloseHandle(LProcessInfo.hThread);
end ;
end ;
// =============================================================================
// cmd.exeでコマンドを実行
// コマンドプロンプトを表示しない
// cmd.exeの起動オプションを /c にする
// CreateProcessのTStartupInfo.dwFlagsにSTARTF_USESHOWWINDOWが必要
// CreateProcessのTStartupInfo.wShowWindowをSW_HIDEにする
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LCmdLine : string ;
begin
// カレントディレクトリをWindowsのシステムフォルダとしてdirコマンドを実行
// 結果は000.txtにリダイレクト(保存)
LCmdLine := 'Cmd.exe /c cd /d c:\windows\system32\'
+ '&dir>' + ExtractFilePath(Application.ExeName) + '000.txt' ;
WinExecAndWait32V2(LCmdLine, SW_HIDE);
end ;
05_ CreateProcess 関数で起動したプロセスを強制終了
CreateProcess 関数でプロセスやプログラムを起動した場合は TProcessInformation.hProcess の値から、GetProcessId 関数でプロセス ID を取得することができます。このプロセス ID の値を使用して起動中のプロセスを強制終了させることができます。
以下のサンプルは、自分自身が CreateProcess 関数を使用して起動したコマンドプロンプトを強制終了するサンプルコードです。
リスト4
自分自身が起動したコマンドプロンプトを強制終了する
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
type
TForm1 = class (TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
FProcessID : DWORD;
function CheckValidProcess(APID: Cardinal; ATerminate: Boolean): Boolean;
end ;
var
Form1: TForm1;
implementation
{$R *.DFM}
// -----------------------------------------------------------------------------
// CreateProcess関数を使用して、指定したファイルを実行する
// この関数の戻り値はプロセスIDの値
// -----------------------------------------------------------------------------
function WinExecAndWait32V2(FileName: String ; Visibility: Integer): DWORD;
var
LStartupInfo : TStartupInfo;
LProcessInfo : TProcessInformation;
LiRet : DWORD;
begin
FillChar(LStartupInfo, SizeOf(LStartupInfo), #0 );
LStartupInfo.cb := SizeOf(TStartupInfo);
// 以下の4行とdwFlagsにSTARTF_USERPOSITION,STARTF_USESIZEを追加
// 位置とサイズを指定する必要がなければこれらの設定は不要
LStartupInfo.dwX := 0 ;
LStartupInfo.dwY := 0 ;
LStartupInfo.dwXSize := 500 ;
LStartupInfo.dwYSize := 600 ;
LStartupInfo.lpTitle := PChar('コマンドプロンプト' );
LStartupInfo.dwFlags := STARTF_USESHOWWINDOW or
STARTF_USEPOSITION or
STARTF_USESIZE;
LStartupInfo.wShowWindow := Visibility;
SetLength(FileName, Length(FileName));
// 参照カウンタ対策
UniqueString(FileName);
if not CreateProcess(nil ,
PChar(FileName),
nil ,
nil ,
False,
NORMAL_PRIORITY_CLASS,
nil ,
nil ,
LStartupInfo,
LProcessInfo) then begin
Result := 0 ;
end else begin
// プロセスが起動するまで待つ
repeat
LiRet := WaitForInputIdle(LProcessInfo.hProcess, 50 );
Application.ProcessMessages;
until LiRet <> WAIT_TIMEOUT;
Result := GetProcessId(LProcessInfo.hProcess);
CloseHandle(LProcessInfo.hProcess);
CloseHandle(LProcessInfo.hThread);
end ;
end ;
// =============================================================================
// コマンドプロンプトを表示
// このアプリを終了しても、起動したコマンドプロンプトは終了しない
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
// 複数起動させない場合
if CheckValidProcess(FProcessID, False) then Exit;
FProcessID := WinExecAndWait32V2('Cmd.exe /k cd /d c:\' , SW_SHOWNORMAL);
end ;
// =============================================================================
// このアプリで生成したプロセスを強制終了
// 本サンプルではコマンドプロンプトを終了
// =============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
CheckValidProcess(FProcessID, True);
FProcessID := 0 ;
end ;
// -----------------------------------------------------------------------------
// 指定したプロセス ID のプロセスが起動しているかを検出
// 強制終了するかは引数で指定
//
// 戻り値が False の場合
// 対象のプロセスは存在しない
// 他の方法で終了していることもある
// 目的の処理が終了すると自動的に終了するプログラムもある
// -----------------------------------------------------------------------------
function TForm1.CheckValidProcess(APID: Cardinal; ATerminate: Boolean): Boolean;
var
LhProcHandle : Cardinal;
begin
if APID = 0 then begin
Result := False;
Exit;
end ;
// Terminate(プロセスの終了)アクセスモードでOpenProcess
LhProcHandle := OpenProcess(PROCESS_TERMINATE, False, APID);
Result := LhProcHandle <> 0 ;
if Result and ATerminate then begin
try
TerminateProcess(LhProcHandle, 0 );
finally
CloseHandle(LhProcHandle);
end ;
end ;
end ;
end .
プロセス ID の値からウィンドウハンドルを取得して。起動したアプリケーションを WM_CLOSE メッセージを使用して終了させる例です。ウィンドウハンドルの値は、EnumWindows 関数のコールバック関数内で検出しています。
リスト5
自分自身が起動したコマンドプロンプトを、ウィンドウハンドルを使用して終了する
copy code
// -----------------------------------------------------------------------------
// EnumWindowsのコールバック関数
// プロセス ID に相当するゥインドウハンドルを取得
// -----------------------------------------------------------------------------
function EnumWndProc(hWindow: HWND; lPar: PNativeUInt):
Boolean; Stdcall ;
var
LdwProcessID : Cardinal;
begin
Result := True;
GetWindowThreadProcessId(hWindow, LdwProcessID);
if LPar^ = LdwProcessID then begin
LPar^ := hWindow;
Result := False;
end ;
end ;
// -----------------------------------------------------------------------------
// 指定したプログラムが起動しているかを検出
// 強制終了するかは引数で指定
//
// 戻り値が False の場合
// 対象のプログラムは起動していない
// 別の方法で終了しているかも知れない
// 処理が終了すると自動的に終了するプログラムもある
// -----------------------------------------------------------------------------
function TForm1.CheckValidProcess(APID: Cardinal; AClose: Boolean): Boolean;
var
LhWnd : NativeUInt;
begin
if APID = 0 then begin
Result := False;
Exit;
end ;
// プロセス ID に相当するウィンドウハンドルを取得
LhWnd := APID;
EnumWindows(@EnumWndProc, LPARAM(@LhWnd));
Result := LhWnd <> APID;
if Result and AClose then begin
PostMessage(LhWnd, WM_CLOSE, 0 , 0 );
end ;
end ;
本ページの最初のサンプルのように、アプリケーションを起動したら、そのアプリが終了するまで待ちたいことがあります。また、アプリケーションによってはそのアプリ内での処理が終了するまで処理を戻さないことがあります。
そのような場合にプロセス ID の値が必要な時は、プロセスが生成されたらプロセスの ID の値を取得してグローバル変数等に代入しておきます。その後に起動したアプリが終了するまでの処理を実行します。以下のコードはその例です。
リスト6
自分自身が起動したアプリの終了を待つ場合
アプリのプロセスが起動したらプロセス ID の値を取得しておく
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
type
TForm1 = class (TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
FProcessID : DWORD;
function CheckValidProcess(APID: Cardinal; ATerminate: Boolean): Boolean;
end ;
var
Form1: TForm1;
implementation
{$R *.DFM}
// -----------------------------------------------------------------------------
// CreateProcess関数を使用して、指定したファイルを実行する
// 起動したプロセスが終了するまで待つ処理コード
//
// プロセスが起動したらそのプロセスID の値を取得して引数に返す
// この値を使用して起動して実行中のプロセスの強制終了に使用する
// -----------------------------------------------------------------------------
function WinExecAndWait32V2(FileName: String ; Visibility: Integer;
var AProcessID: Cardinal): DWORD;
var
LStartupInfo : TStartupInfo;
LProcessInfo : TProcessInformation;
LiRet : DWORD;
begin
FillChar(LStartupInfo, SizeOf(LStartupInfo), #0 );
LStartupInfo.cb := SizeOf(TStartupInfo);
// 以下の4行とdwFlagsにSTARTF_USERPOSITION,STARTF_USESIZEを追加
// 位置とサイズを指定する必要がなければこれらの設定は不要
LStartupInfo.dwX := 0 ;
LStartupInfo.dwY := 0 ;
LStartupInfo.dwXSize := 500 ;
LStartupInfo.dwYSize := 600 ;
LStartupInfo.lpTitle := PChar('コマンドプロンプト' );
LStartupInfo.dwFlags := STARTF_USESHOWWINDOW or
STARTF_USEPOSITION or
STARTF_USESIZE;
LStartupInfo.wShowWindow := Visibility;
SetLength(FileName, Length(FileName));
// 参照カウンタ対策
UniqueString(FileName);
if not CreateProcess(nil ,
PChar(FileName),
nil ,
nil ,
False,
NORMAL_PRIORITY_CLASS,
nil ,
nil ,
LStartupInfo,
LProcessInfo) then begin
Result := WAIT_FAILED;
end else begin
// プロセスが起動するまで待つ
repeat
LiRet := WaitForInputIdle(LProcessInfo.hProcess, 50 );
Application.ProcessMessages;
until LiRet <> WAIT_TIMEOUT;
// 起動したらプロセス ID を取得
AProcessID := GetProcessId(LProcessInfo.hProcess);
// 起動したプロセスが終了するまで待つ
while WaitForSingleObject(LProcessInfo.hProcess, 100 ) = WAIT_TIMEOUT do
Application.ProcessMessages;
GetExitCodeProcess(LProcessInfo.hProcess, Result);
CloseHandle(LProcessInfo.hProcess);
CloseHandle(LProcessInfo.hThread);
end ;
end ;
// =============================================================================
// コマンドプロンプトを表示
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
// 複数起動させない場合
if CheckValidProcess(FProcessID, False) then Exit;
Button1.Enabled := False;
WinExecAndWait32V2('Cmd.exe /k cd /d c:\' , SW_SHOWNORMAL, FProcessID);
// 起動したアプリが終了すると以下が実行される
FProcessID := 0 ;
Button1.Enabled := True;
end ;
// =============================================================================
// このアプリで生成したプロセスを強制終了
// 本サンプルではコマンドプロンプトを終了
// =============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
CheckValidProcess(FProcessID, True);
FProcessID := 0 ;
end ;
// -----------------------------------------------------------------------------
// 指定したプロセス ID のプロセスが起動しているかを検出
// 強制終了するかは引数で指定
//
// 戻り値が False の場合
// 対象のプロセスは存在しない
// 他の方法で終了していることもある
// 目的の処理が終了すると自動的に終了するプログラムもある
// -----------------------------------------------------------------------------
function TForm1.CheckValidProcess(APID: Cardinal; ATerminate: Boolean): Boolean;
var
LhProcHandle : Cardinal;
begin
if APID = 0 then begin
Result := False;
Exit;
end ;
// Terminate(プロセスの終了)アクセスモードでOpenProcess
LhProcHandle := OpenProcess(PROCESS_TERMINATE, False, APID);
Result := LhProcHandle <> 0 ;
if Result and ATerminate then begin
try
TerminateProcess(LhProcHandle, 0 );
finally
CloseHandle(LhProcHandle);
end ;
end ;
end ;
end .