フォーム


やまたか  2013-08-27 23:18:59  No: 45150

はじめまして、こんにちは。
今回、メインフォームに貼り付けたボタンを選択すると
外部アプリが実行されるという簡単なプログラムを作っています。
(以下コーディング)

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  2013-08-28 00:31:25  No: 45151

処理前に、フォームに入力を受け付けない様にすればどうですか。
処理終了後に再度受け付けるようにすれば。


やまたか  2013-08-28 01:43:18  No: 45152

HOtaさん、ご回答ありがとうございます。
>処理前に、フォームに入力を受け付けない様にすればどうですか。
フォームのEnableプロパティをFalseにするということですか?


deldel  2013-08-28 18:49:07  No: 45153

こんな感じで。

  btnEnter.Enabled := False;
  Application.ProcessMessages;
  try
    //いろいろ処理
  finally
    Application.ProcessMessages; //必須
    btnEnter.Enabled := True;
  end;


やまたか  2013-08-28 21:15:17  No: 45154

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;

なにが、おかしいのかな・・・?


Harry  2013-08-28 22:00:45  No: 45155

本題に入る前に…
  ・ 環境を書きましょう。
  ・ 質問のタイトルをきちんと書きましょう。端折り過ぎです。
       →例:起動した外部プログラムがメインフォームの裏に隠れないようにするには?
  ・ 提示するコードには明らかに余計な部分が無いようにすると分かりやすいです。

常に最前面(または常にメインフォームの前面かな?)とか、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;


やまたか  2013-08-28 23:48:20  No: 45156

Harryさん、ご回答ありがとうございます。
そして、説明不足で申しわけありません。

実行環境ですが、Windows7 Proです。
Delphiは2010です。

Harryさんのおっしゃる通り「クリック+マウスダウン」の動作で
再現されます。

教えて頂いた通りコーディングしたところ、少し改善されました。
Sleep(100)の時間が大切のようです。

ところで、初心者で何も判らないのですが、
他アプリを「常に最前面」にするのは、UI的に好ましくないですか?
(調整が難しい・・・)


じゃむ派  2013-08-29 20:35:02  No: 45157

WindowswApiを使用すればできます。
起動したプログラムのプロセスIDからウィンドハンドルを取得し
そのウインドウハンドルを元に前面に出すという流れです。
vbfunc.pas というソースが検索すれば出てきますので
そのAppActive関数を使用すればお望みのことはできますが、
ソースの内容も見ていただくと良いと思います。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加