ShellExecuteで起動した子プロセスの終了を知る方法はありますでしょうか?
以前から、CreateProcessで起動させるルーチンは紹介されていますが、
事情により(ショートカットを起動させるため)、CreateProcessは使えず
ShellExecuteで起動させています。
子プロセスの起動中は、親フォームを表示せず、
子プロセスの終了を検知して親フォームを可視状態にしようと思っています。(どちらか片方しか表示されない状態)
以下のソースでは子プロセスの起動はできますが、
終了をうまく検知しないためか、両方のフォームが表示されてしまいます。
無理やりShellExecuteの戻り値を取得しようと思ったのがいけないのでしょうか?
どなたか知恵を貸してください。
procedure TMastMenu.SpeedButton1Click(Sender: TObject);
var
s : string;
ProgName : PChar;
handle : THandle;
tpi : TProcessInformation;
begin
s := 'C:\abcへのショートカット.lnk';
ProgName := PChar(sWorks);
//ショートカットプログラムを起動
Handle := ShellExecute(Application.MainForm.Handle, nil,ProgName, nil, nil,SW_SHOW);
tpi.hProcess := handle;
//プロセスが終了するのを待つ
while WaitForSingleObject(tpi.hProcess, INFINITE) = WAIT_TIMEOUT do
begin
Application.ProcessMessages;
Self.Visible := false;
end;
Self.Visible := true;
end;
GetExitCodeProcessでStillActiveの間待たせるとか?
いや、ShellExecute では、そもそもプロセスハンドルが取得できません。
素直に、ショートカットなときは、実行ファイルを取得して CreateProcess を
使ってください。
りおりおさんのいうように
こんなので
ショートカットファイルの実行ファイルを取得して
CreateProcessを呼びましょう。
type
TShowCommand = (scNormalWindow, scMinimize, scMaxmize, scUnknown);
TShortcurFile = class(TObject)
private
ShellLink:IShellLink;
PersistFile:IPersistFile;
FilePath:array[0..MAX_PATH] of char;
WorkDir:array[0..MAX_PATH] of char ;
wsz:Array[0..MAX_PATH] of WideChar;
pwHotKey:Word;
pShowCmd:integer;
pfd:TWin32FindData;
FSourceFileName: TFileName;
FFileName: TFileName;
FShowCommand: TShowCommand;
FHotKey: Word;
protected
public
constructor Create(FileLink: TFileName);
destructor Destroy; override;
published
property FileName: TFileName read FFileName;
property SourceFileName: TFileName read FSourceFileName;
property HotKey: Word read FHotKey;
property ShowCommand: TShowCommand read FShowCommand;
end;
{ TShortcurFile }
constructor TShortcurFile.Create(FileLink: TFileName);
begin
if not FileExists(ChangeFileExt(FileLink,'.lnk')) then
raise Exception.Create('ファイルが存在しません');
inherited Create;
ShellLink := CreateComObject(CLSID_ShellLink) As IShellLink;
try
OleCheck(ShellLink.QueryInterface(IPersistFile,PersistFile));
Assert(Assigned(PersistFile));
MultiByteToWideChar(CP_ACP,0,PChar(FileLink),-1,wsz,MAX_PATH);
OleCheck(PersistFile.Load(wsz,0));
OleCheck(ShellLink.GetPath(FilePath,MAX_PATH,pfd,SLGP_SHORTPATH));
FFileName := FilePath;
OleCheck(ShellLink.GetWorkingDirectory(WorkDir,MAX_PATH));
FSourceFileName := WorkDir;
OleCheck(ShellLink.GetHotKey(pwHotkey));
FHotKey := pwHotKey;
OleCheck(ShellLink.GetShowCmd(pShowCmd));
case pShowCmd of
1: FShowCommand := scNormalWindow;
7: FShowCommand := scMinimize;
3: FShowCommand := scMaxmize;
else
FShowCommand := scUnknown;
end;
except on EOleSysError do
raise Exception.Create('OLEエラーが発生しました');
end;
end;
ShellExecuteでの終了待ちは出来ません。
ShellExecuteExとWaitForSingleObjectを使いましょう。
以下はボタン(Button1)をクリックするとメモ帳が起動され、ユーザーがそのメモ帳を終了するまで待機する例です。
参考になるでしょうか?
procedure TForm1.Button1Click( Sender: TObject);
var
SXInfo: TShellExecuteInfo;
begin
with SXInfo do//TShellExecuteInfo構造体の初期化
begin
cbSize := SizeOf( SXInfo);
fMask := SEE_MASK_NOCLOSEPROCESS;//これがないと終了待ち出来ない
Wnd := Application.Handle;
lpVerb := 'open';
lpFile := PChar( 'notepad.exe');
lpParameters := Nil;
lpDirectory := Nil;
nShow := SW_SHOW;
end;
ShellExecuteEx( @SXInfo);
Button1.Enabled := False;//メモ帳がいくつも起動されるとまずいので...
//起動したアプリケーションの終了待ち
while WaitForSingleObject( SXInfo.hProcess, 0) = WAIT_TIMEOUT do
Application.ProcessMessages;
ShowMessage( '終わったよ!');
Button1.Enabled := True;
end;
元はhttp://www.asahi-net.or.jp/~GV4J-SGUR/narik/stp001.htm#SHGetSpecialFolderLocationです。
ツイート | ![]() |