はじめまして。
初心者ですのでとんちんかんなことを書いているかもしれませんが
よろしくご教授ください。
shellexecuteから他ソフトのインストーラー(setup.exe)を起動し、
セットアップ画面が起動したことを検知しようとしています。
以下のように記述しました。
hWindow:=0;
intN:=0;
shellexecute(handle,'open','R:\Setup.exe',nil,nil,SW_SHOW);
repeat
sleep(1000);
hWindow:=findwindow(nil,'セットアップ')
intN:=intN+1;
until(hWindow<>0)or(intN>5000);
showmessage(inttostr(hWindow));
で、実行しますと、待ち時間(intN>5000)をどんなに長くしても、
インストーラーは起動せず、showmessageが「起動した直後に」起動します。
ちなみにこのインストーラーのかわりにコマンドプロンプトで試すと、
ちゃんと取得できます。
何か間違っていますでしょうか?
どなたか教えてください。
宜しくお願いいたします。
Halbow です。
> で、実行しますと、待ち時間(intN>5000)をどんなに長くしても、
> インストーラーは起動せず、showmessageが「起動した直後に」起動します。
うーむ、日本語がわかりません。ShowMessage() が表示されるということは、
repeat until ループを抜けたことを意味していますので、上のコードでは、
intN が 5000 を越えるのは、1時間以上かかるわけですから、hWindow が
ゼロでなくなった、と思いますが?
CreateProcessで起動した方がよろしいかと思います。
ShellExecuteは、プロセスの管理に向きません。
返答ありがとうございます。
>Halbow様
(intN>10)としても、(intN>5000)としても、ループを抜けた直後に
起動するのです。showmessageで表示させて確認しましたが、
かならずintNの条件でループを抜けています。
until(hWindow<>0)だけだとずっと固まってます。
>にしの様
CreateProcess、見よう見まねで作成したため、まちがっている部分も
あったかもしれないのですが、現象は変わりませんでした。
いろいろ試した結果、起動するものを「cmd」にすると、上手くいきました。
また、起動したいインストーラーを起動しても、ShellExecuteの次の行に
Showmessage('TEST');
を記述すると、メッセージ表示直後にインストーラーが起動しました。
これで満足のいく「結果」は得られるのですが、できれば余分な
メッセージは表示したくありません。
自分自身を非アクティブにすればいいのかな?とも思いましたが、
そんなことはあるのでしょうか?
ちなみにウィンドウをアクティブにする方法は分かるのですが
非アクティブにする方法が良く分かりません・・・。
よい解決法があれば教えてください。
CreateProcessは、プロセス情報を返すので、わざわざウィンドウを探す必要はないですよ。
Processが有効かどうかを見るだけでよいのです。
なんとなく、ShellExecuteを実行している自分自身が、Sleepでスリープ状態になり、スリープ直後にShellExecuteが有効になってループを素通りしているような気もします。
Sleepでなく、
> hWindow:=findwindow(nil,'セットアップ')
でhWindowが0で無くなってから、0になるのを待てばよいかと思います。
# もちろん、セットアップのウィンドウが全くでなかった場合も考慮して。
ちなみに、CreateProcessの場合はこんなかんじ。
procedure TForm1.Button3Click(Sender: TObject);
var
si:TStartupInfo;
pi:TProcessInformation;
begin
ZeroMemory(@si,SizeOf(si));
ZeroMemory(@pi,SizeOf(pi));
si.cb := SizeOf(si);
CreateProcess(nil, 'notepad', nil,
nil, FALSE, 0, nil, nil, si, pi);
WaitForSingleObject(pi.hProcess, INFINITE);
ShowMessage('OK');
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
end;
>にしの様
返答ありがとうございます。
返答にありましたCreateProcessにて実施することにより、インストーラーが
ちゃんと起動しました。
ただ、実はここまで申し上げていませんでしたが、このインストーラーは実行すると、
まず「セットアップは〜準備中です」というウインドウを表示し、その後、
本当の?セットアップ画面が表示されます。
自分が検知したいのは、この、インストーラーにて呼び出される画面です。
教えていただいたCreateProcessの後に
repeat
sleep(100);
hWindow:=findwindow(nil,'セットアップ')
intN:=intN+1;
until(hWindow<>0)or(intN>500);
showmessage(inttostr(hWindow));
とやってみましたが、ご指摘の通り,自分自身がSleepになってしまうようで、
うまく結果が得られません。
ループからSleepを外してもだめでした。
CreateProcessで起動したインストーラーが、起動したセットアップ画面を
WaitForInputIdleで待つことができればよいのでしょうが・・・。
聞いてばかりで申し訳ありませんが、良い案がありましたらご教授ください。
Halbow です。
> hWindow:=findwindow(nil,'セットアップ');
ここのところですけど、本当にほしい画面のタイトルは 'セットアップ' に
なっていますか? 半角のカタカナである可能性はないですか?
>Halbow様
返答ありがとうございます。
画面のタイトルは間違えてないです。
(実際はとても長いため、ここでは「セットアップ」としていますが。)
ShellExecuteやCreateProcessを使用せずに、手動でインストーラーを
起動させた状態で試すと、この画面の、指定したボタンに
フォーカスを当てることまで実現できてますので。
自分自身が固まらずに、起動を待つことはできないのでしょうか・・・。
Halbow です。
> 自分自身が固まらずに、起動を待つことはできないのでしょうか・・・。
べつに固まっているわけではないですよね。ループを抜け出せないだけで。
なぜ、抜け出せないかといえば、
> hWindow:=findwindow(nil,'セットアップ');
が失敗しているからですよね。手動でうまくいくのに不思議ですね。
うまくいったときの FindWindow() のところをコピペして試す価値が
あるかもしれませんね。
あと、ShowMessage() でうまくいくのをヒントにして、待ちループに
Application.ProcessMessages; を入れるのも有効かもしれません。
>Halbow様
ループにApplication.ProcessMessagesを入れることで解決しました!
ありがとうございました。
ただ、最初に起動させるインストーラーは表示が0%からスタートして
100%までバーが進み、その後セットアップが起動するのですが、
通常起動する時はバーがスムーズに伸びていきますが、自分のプログラム
から起動するとカクカク進んでいきます。
ループ中のSleepでプロセスを占有?してしまい、ループして再びSleepに
たどり着くまでにバーがちょっとずつ進んでいる感じです。
ループからSleep外せばスムーズに進むのですが、FindWindowが失敗したら
永久ループなので、カクカクでがまんします。
Halbow様、にしの様、いろいろとアドバイスありがとうございました。
ツイート | ![]() |