下の某掲示板でのレスに関連してのネタです.
[ShellExecute()後のフォーカスを取り戻すには]
http://hpcgi1.nifty.com/MADIA/DelphiBBS/wwwlng.cgi?print+201309/13090015.txt
こういうのは,ShellExecuteEx か CreateProcess 関数を使用すると制御しやすいです.
起動するアプリのウィンドウハンドルが取得できたら,起動完了ということになります.
すると,起動したアプリも自アプリの操作も可能な状態になります.
例えば以下のように.ただし,コードは長くなります.
新規プロジェクトにボタンを 2 つ配置します.
某掲示板にあるスレッドアタッチのコードもテストできるようになっています.
usesにShellAPIが必要です.
動作確認環境は,Windows 7 U64(SP1) + Delphi XE Pro です.
//----------------------------------------------------------------------------- // EnumWindowsのコールバック関数 // 以下の条件の時に終了とし,引数のlpにその時のウインドウハンドルを返す // (1) 引数のlpとウィンドウハンドルが同じ // (2) オーナーを持たないウィンドウ // (3) 可視状態のウィンドウである //----------------------------------------------------------------------------- function EnumTopWindow(hTopWnd: HWND; lp: LPARAM): BOOL; stdcall; var p : LPDWORD; ProcessId : DWORD; begin Result := True;
GetWindowThreadProcessId(hTopWnd, @ProcessId); p := LPDWORD(lp); if (ProcessId = p^) and (GetWindow(hTopWnd, GW_OWNER) = 0) and IsWindowVisible(hTopWnd) then begin p^ := hTopWnd; Result := False; end; end;
//----------------------------------------------------------------------------- // ShellExecuteExでファイル表示またはアプリを起動 // 戻り値は対象のウィンドウハンドル.起動に失敗すると0 // // AFile : 起動プログラムのフルパスまたはファイルのフルパス // AParameters : コマンドラインオプション.表示するファイル等 // Verb : Open,Show,Print等の動作内容 // vShow : SW_SHOW等の表示フラグ // // Windows Vistaでの注意 // Windows Vistaではファイルを開く際に管理者権限が必要なことがある //----------------------------------------------------------------------------- function RunExeFile(hWnd: HWND; AFile: String; AParameters: String; Verb: String; vShow: Integer): Cardinal; var AInfo : TShellExecuteInfo; hProcess : THandle; ret : DWORD; dwIDOrigin : DWORD; dw : DWORD; WaitCount : integer; begin Result := 0; dw := 0; dwIDOrigin := 0;
FillChar(AInfo, SizeOf(AInfo), 0); AInfo.cbSize := SizeOf(AInfo); AInfo.Wnd := hWnd; AInfo.fMask := SEE_MASK_NOCLOSEPROCESS or SEE_MASK_FLAG_NO_UI; AInfo.lpVerb := PChar(Verb);
AInfo.lpFile := PChar(AFile); AInfo.lpParameters := PChar(AParameters); AInfo.nShow := vShow;
ShellExecuteEx(@AInfo);
//ShellExecuteExは成功するとhInstAppに33以上の値を返す if AInfo.hInstApp >= 33 then begin repeat ret := WaitForInputIdle(AInfo.hProcess, 50); Application.ProcessMessages; until ret <> WAIT_TIMEOUT;
//EnumWindowsにプロセスIDを渡してウィンドウを検索 //EnumWindowsの第2引数にウィンドウハンドルが戻ってくる //ウィンドウの起動完了までは時間がかかるのでループ if ret = 0 then begin WaitCount := 0; dwIDOrigin := GetProcessId(AInfo.hProcess); repeat Sleep(50); dw := dwIDOrigin; EnumWindows(@EnumTopWindow, LPARAM(@dw)); if dw <> dwIDOrigin then begin Result := dw; break; end; Inc(WaitCount); until IsWindow(dw) or (WaitCount > 100); end;
//プロセスの起動に失敗したらプロセスを閉じる if (ret <> 0) or (dw = dwIDOrigin) then begin hProcess := AInfo.hProcess; if hProcess > 0 then begin TerminateProcess(hProcess, 0); CloseHandle(hProcess); end; end; end; end;
//============================================================================= // Windows フォトビューアの起動 // 起動後に自アプリにフォーカス移動する例 // ShellExecuteExを使用した例 // // 起動オプション(起動パラメータ)の区切りには空白が必要なことに注意 // usesにShellAPIが必要 //============================================================================= procedure TForm1.Button1Click(Sender: TObject); var AppStr : String; AppCmd : String; AppWnd : HWND; begin AppStr := 'Rundll32.exe'; AppCmd := 'C:\Windows\system32\shimgvw.dll, ImageView_Fullscreen ' + ExpandFileName('IMG_0256.JPG');
AppWnd := RunExeFile(0, AppStr, AppCmd, 'Open', SW_SHOWNORMAL);
//対象アプリの起動が完了したので,このフォームをアクティブにしてみる SetForegroundWindow(Handle); end;
//============================================================================= // 某掲示板のコード // http://hpcgi1.nifty.com/MADIA/DelphiBBS/wwwlng.cgi?print+201309/13090015.txt // usesにShellAPIが必要 //============================================================================= procedure TForm1.Button2Click(Sender: TObject);
procedure SetForceForegroundWindow; var dwTargetID: DWORD; dwActiveID: DWORD; begin dwTargetID := GetWindowThreadProcessId(GetForegroundWindow); //現在の前面ウィンドウのプロセスIDを取得 dwActiveID := GetCurrentThreadId; //スレッドのアタッチ AttachThreadInput(dwActiveID, dwTargetID, True); //アタッチしたハンドルのウィドウを前面に SetForegroundWindow(Handle); //終了したらアタッチを切り離す AttachThreadInput(dwActiveID, dwTargetID, False); end;
begin ShellExecute(Handle, 'OPEN', 'IMG_0256.JPG', nil, nil, SW_SHOWNORMAL); Sleep(300);
SetForceForegroundWindow; Application.ProcessMessages; SetForegroundWindow(Form1.Handle); end;
参考リンク
[起動したプロセスのトップレベルウィンドウのハンドルを取得]
http://mrxray.on.coocan.jp/Halbow/Notes/N002.html
[266_位置とサイズを指定してアプリを起動] [04_Windows フォトビューアを位置とサイズを設定して起動]
http://mrxray.on.coocan.jp/Delphi/plSamples/266_App_CreateOpen.htm#04
|