はじめまして、こんにちは。
今回、メインフォームに貼り付けたボタンを選択すると
外部アプリが実行されるという簡単なプログラムを作っています。
(以下コーディング)
procedure TfrmMenu.btnEnterClick(Sender: TObject);
const
exeName = 'C:\EXE\\Auth.exe';
var
SI : TStartupInfo;
PI : TProcessInformation;
command : String;
CSVFILE : TextFile;
s : String;
SQL1, SQL2, SQL4 : String;
flg : Integer;
begin
//Exe呼び出し
command := exeName;
GetStartupInfo(SI);
if Not CreateProcess(pchar(command), nil, nil, nil, False, CREATE_DEFAULT_ERROR_MODE, nil, nil, SI, PI) then
Raise Exception.Create('Exec Error' + IntToStr(GetLastError));
//戻りを待つ
try
Enabled := False;
while WaitForSingleObject(PI.hProcess, 0) = WAIT_TIMEOUT do
Application.ProcessMessages;
finally
Enabled := True;
end;
//ハンドルをクローズ
CloseHandle(PI.hProcess);
ボタンをシングルクリックすればイメージ通りに外部プログラムが表示されるのですが、
ボタンを間違ってダブルクリックすると、外部プログラムのフォームがメインフォームの裏に隠れてしまいます。
外部プログラムの処理が終わるまで、常に最表面にすることはできますか。
処理前に、フォームに入力を受け付けない様にすればどうですか。
処理終了後に再度受け付けるようにすれば。
HOtaさん、ご回答ありがとうございます。
>処理前に、フォームに入力を受け付けない様にすればどうですか。
フォームのEnableプロパティをFalseにするということですか?
こんな感じで。
btnEnter.Enabled := False;
Application.ProcessMessages;
try
//いろいろ処理
finally
Application.ProcessMessages; //必須
btnEnter.Enabled := True;
end;
deldelさん、早速のお返事ありがとうございます。
以下のようにコーディングして、試させていただきましたが
変化はありませんでした。
procedure TfrmMenu.btnEnterClick(Sender: TObject);
const
exeName = 'C:\EXE\\Auth.exe';
var
SI : TStartupInfo;
PI : TProcessInformation;
command : String;
CSVFILE : TextFile;
s : String;
SQL1, SQL2, SQL4 : String;
flg : Integer;
brgin
btnEnter.Enabled := False;
Application.ProcessMessages;
try
//Exe呼び出し
command := exeName;
GetStartupInfo(SI);
if Not CreateProcess(pchar(command), nil, nil, nil, False, CREATE_DEFAULT_ERROR_MODE, nil, nil, SI, PI) then
Raise Exception.Create('Exec Error' + IntToStr(GetLastError));
//戻りを待つ
try
Enabled := False;
while WaitForSingleObject(PI.hProcess, 0) = WAIT_TIMEOUT do
Application.ProcessMessages;
finally
Enabled := True;
end;
//ハンドルをクローズ
CloseHandle(PI.hProcess);
finally
Application.ProcessMessages; //必須
btnEnter.Enabled := True;
end;
end;
なにが、おかしいのかな・・・?
本題に入る前に…
・ 環境を書きましょう。
・ 質問のタイトルをきちんと書きましょう。端折り過ぎです。
→例:起動した外部プログラムがメインフォームの裏に隠れないようにするには?
・ 提示するコードには明らかに余計な部分が無いようにすると分かりやすいです。
常に最前面(または常にメインフォームの前面かな?)とか、30秒問題とかあるようですが…
とりあえず、他アプリを「常に最前面」にするのは難しいのと、UI的に好ましくないのでスルーします。
「裏に隠れてしまう」の再現条件はダブルクリックというより1.5クリック(クリック+マウスダウン)
ですよね。これだと、新しく立ちあがるウインドウが最前面に出てこれないみたい?
この状態で自アプリが前面に居座らないようするために各種Enabledでなんとかするのは難しいようです。
で、、、なんとなくSleep(100);を入れてみたら改善したような?? (テクノロジー不明)
以下、いくつか気になった点を修正、ついでとして勝手に自分好みにしてみたコードです。
なお、CreateProcess周辺はよく分かってませんので適当です。あしからず。
procedure TForm1.Button1Click(Sender: TObject);
const
// exeName = 'C:\EXE\\Auth.exe'; // ←なぜ \\ ?
exeName = 'C:\WINDOWS\NOTEPAD.EXE';
var
SI : TStartupInfo;
PI : TProcessInformation;
command : String;
begin
Self.Enabled := False; // まず最初にフォームをDisableにする
Sleep(100); // ミソ?
//Exe呼び出し
// command := exeName;
command := '"'+exeName+'"'; // 一応ダブルコーテーションで囲んでみる
// command := command+' "C:\Program Files\Borland\Delphi6\readme.txt"'; // 引数も付けられる
GetStartupInfo(SI);
if Not CreateProcess(nil, PChar(command), nil, nil, False, CREATE_DEFAULT_ERROR_MODE, nil, nil, SI, PI) then
Raise Exception.Create('Exec Error' + IntToStr(GetLastError));
//戻りを待つ
try
while WaitForSingleObject(PI.hProcess, 0) = WAIT_TIMEOUT do begin
Application.ProcessMessages;
Sleep(50); // 全力空回り(CPU100%)を防止!
end;
finally
Self.Enabled := True;
//ハンドルをクローズ
CloseHandle(PI.hProcess); // 不測の事態が起きてもCloseHandleしたいならこの位置かと
end;
end;
Harryさん、ご回答ありがとうございます。
そして、説明不足で申しわけありません。
実行環境ですが、Windows7 Proです。
Delphiは2010です。
Harryさんのおっしゃる通り「クリック+マウスダウン」の動作で
再現されます。
教えて頂いた通りコーディングしたところ、少し改善されました。
Sleep(100)の時間が大切のようです。
ところで、初心者で何も判らないのですが、
他アプリを「常に最前面」にするのは、UI的に好ましくないですか?
(調整が難しい・・・)
WindowswApiを使用すればできます。
起動したプログラムのプロセスIDからウィンドハンドルを取得し
そのウインドウハンドルを元に前面に出すという流れです。
vbfunc.pas というソースが検索すれば出てきますので
そのAppActive関数を使用すればお望みのことはできますが、
ソースの内容も見ていただくと良いと思います。
ツイート | ![]() |