フォーラム


ゲスト  

ようこそ ゲスト さん。このフォーラムに投稿するには 登録が必要です。

ページ: [1]
トピック: ShellExecuteEx 関数でアプリ起動後のフォーカス移動
Mr.XRAY
メンバー
投稿数: 192
ShellExecuteEx 関数でアプリ起動後のフォーカス移動
on: 2013/09/18 00:53 Wed

下の某掲示板でのレスに関連してのネタです.
[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

Mr.XRAY
メンバー
投稿数: 192
Re: ShellExecuteEx 関数でアプリ起動後のフォーカス移動
on: 2013/09/18 00:58 Wed

先のコードは,私が勝手に Windows フォトビューアで画像を表示するものとしています.
某掲示板での質問文章では,そのことには触れていません.ご了承ください.

実際にやってみないと,違いは実感できませんが,参考として.

Mr.XRAY
メンバー
投稿数: 192
Re: ShellExecuteEx 関数でアプリ起動後のフォーカス移動
on: 2013/09/21 07:06 Sat

ShellExecute 関数あるいは ShellExecuteEx 関数は,起動するプログラムを指定しなくても,
ファイルを表示することができます.
しかし,実際には,そのファイルの拡張子に関連付けされているプログラムが起動して,
そのプログラムがファイルを表示します.

ですから,この記事の元となった某掲示板の発言

>Vキーを押したら、画像を表示し、最前画面にFormをだす。

は「Vキーを押したら、Windows フォトビューアを起動 (表示) し、最前面 (最前画面) に Form1 を表示する (Formをだす)。」
と書くことができます.これなら状況がより明確になります.

昨日,実際には今日ですが,遅くまでサイトの記事の誤字脱字のチェックをしていて,突然気付きました.
ShellExecute 関数あるいは ShellExecuteEx 関数がファイルを表示していると,信じている方がいたようです.
過去に,メールでのやり取りで,話がかみ合わない理由がこれで理解できました.
こちらが,当たり前だと思っていることが,質問された方には,理解できていないことがあるということです.

後,できれば質問の文章の中の用語は,できる限り一般的なのがいいですね.
例えば,検索エンジンで検索に使用する用語とか.
「どこどこに放り込みたい」(TImage に描画あるいは表示の場合等) というのは,日常会話で使用はしても,
こちらが状況を把握するのに手間がかかりますし,精神的にもよくありません.ハッキリ言っていらつきます.
「TImage に画像を放り込み方法」なんていうのは,その方独特の言い方だとは思うのですが.
「Form をだす」というのも,ほとんど見たことがありません.個人的には.

ページ: [1]
WP Forum Server by ForumPress | LucidCrew
バージョン: 1.7.5 ; ページロード: 0.049 sec.