0

Delphi XE7 (または XE8) では、TjvProgressDialog (JVCL から) をフォームに配置し、 dlgProgress1という名前を付けます。TButton をbtnProgressDialogTestという名前にします。

このコードは、最初に別のスレッド (ShellExecAndWaitTask) からメモ帳を起動し、次に無限進行ループで進行状況ダイアログ (dlgProgress1) を開きます。

var
  ShellExecAndWaitTask: System.Threading.ITask;

procedure TForm1.btnProgressDialogTestClick(Sender: TObject);
begin
  ShellExecAndWaitTask := TTask.Create(
    procedure
    begin
      JclShell.ShellExecAndWait('notepad'); // BTW, is this thread-safe?
      CodeSite.Send('Notepad has been closed');
    end);
  ShellExecAndWaitTask.Start;

  dlgProgress1.Caption := 'ProgressDialog Test';
  dlgProgress1.Text := 'Close Notepad to automatically close this progress dialog';
  dlgProgress1.Tag := 0;
  dlgProgress1.Position := 0;
  dlgProgress1.ShowCancel := True;
  dlgProgress1.ShowModal;
  CodeSite.Send('Progress dialog has been closed');
end;

procedure TForm1.dlgProgress1Progress(Sender: TObject; var AContinue: Boolean);
begin
  if dlgProgress1.Tag = 0 then
  begin
    if dlgProgress1.Position < dlgProgress1.Max then
      dlgProgress1.Position := dlgProgress1.Position + 1;
    if dlgProgress1.Position = dlgProgress1.Max then
      dlgProgress1.Tag := 1;
  end
  else
  begin
    if dlgProgress1.Position > 0 then
      dlgProgress1.Position := dlgProgress1.Position - 1;
    if dlgProgress1.Position = 0 then
      dlgProgress1.Tag := 0;
  end;

  AContinue := Assigned(ShellExecAndWaitTask); // why this never becomes false?
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  if Assigned(ShellExecAndWaitTask) then
    ShellExecAndWaitTask.Cancel;
end;

Assigned(ShellExecAndWaitTask)メモ帳を閉じると、dlgProgress1Progressイベント ハンドラーで false になり、false に設定して進行状況ダイアログを閉じるべきではありませんAContinueか? ShellExecAndWaitTask代わりに、タスクが終了しても常に true のままです! なんで?

編集:

David のアドバイスに従って、コードを変更しました。今は動作しますが、スレッドセーフですか?

var
  ShellExecAndWaitTask: System.Threading.ITask;
  ShellExecAndWaitTaskTerminated: Boolean;

procedure TForm1.btnProgressDialogTestClick(Sender: TObject);
begin
  ShellExecAndWaitTask := TTask.Create(
    procedure
    begin
      JclShell.ShellExecAndWait('notepad'); // BTW, is this thread-safe?
      CodeSite.Send('Notepad has been closed');
      TThread.Queue(TThread.CurrentThread,
      procedure
      begin
        ShellExecAndWaitTaskTerminated := True;
      end);
    end);
  ShellExecAndWaitTaskTerminated := False;
  ShellExecAndWaitTask.Start;

  dlgProgress1.Caption := 'ProgressDialog Test';
  dlgProgress1.Text := 'Close Notepad to automatically close this progress dialog';
  dlgProgress1.Tag := 0;
  dlgProgress1.Position := 0;
  dlgProgress1.ShowCancel := True;
  dlgProgress1.ShowModal;
  CodeSite.Send('Progress dialog has been closed');
end;

procedure TForm1.dlgProgress1Progress(Sender: TObject; var AContinue: Boolean);
begin
  if dlgProgress1.Tag = 0 then
  begin
    if dlgProgress1.Position < dlgProgress1.Max then
      dlgProgress1.Position := dlgProgress1.Position + 1;
    if dlgProgress1.Position = dlgProgress1.Max then
      dlgProgress1.Tag := 1;
  end
  else
  begin
    if dlgProgress1.Position > 0 then
      dlgProgress1.Position := dlgProgress1.Position - 1;
    if dlgProgress1.Position = 0 then
      dlgProgress1.Tag := 0;
  end;
  AContinue := not ShellExecAndWaitTaskTerminated;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  if Assigned(ShellExecAndWaitTask) then
    ShellExecAndWaitTask.Cancel;
end;
4

1 に答える 1

1

メモ帳を閉じるときに、dlgProgress1Progress イベント ハンドラーの Assigned(ShellExecAndWaitTask) が false になり、AContinue を false に設定して進行状況ダイアログを閉じるべきではありませんか?

いいえ、それは物事がどのように機能するかではありません。たとえば、タスクを参照するさまざまな変数を多数持つことができます。これらのすべての変数が に設定されることをどのように期待できますかnil

いいえ、インターフェイス参照変数は、スコープを離れるか、に設定するまで割り当てられたままですnil。余談ですが、これにグローバル変数を使用するというあなたの選択に真剣に疑問を呈します。なぜその決断を下したのかはわかりませんが、間違った決断のようです。

最も簡単なことは、への呼び出しがShellExecAndWait戻ったときにメッセージを送信することです。

ShellExecAndWaitTask := TTask.Create(
  procedure
  begin
    JclShell.ShellExecAndWait('notepad');
    CodeSite.Send('Notepad has been closed');
    // notify the main thread that the external process has completed
  end);

たとえば、PostMessageまたはTThread.QueueまたはTThread.Synchronizeを使用して、メインスレッドに通知できます。

于 2015-04-22T13:13:23.350 に答える