Home › Forums › Konopka Signature VCL Controls (formerly Raize Components) › Why is Application.ProcessMessages necessary for TRzLauncher.Running?
- This topic has 5 replies, 2 voices, and was last updated 2023-01-29 at 10:49 am by David Marcus.
-
AuthorPosts
-
-
January 24, 2023 at 2:48 pm #3423
I don’t whether this is a bug, but regardless I would like to understand why this works the way that it does. I have RzLauncher1.WaitUntilFinished = False. In my app, I do
while RzLauncher1.Running do
sleep( 2000 );But, this loop never ends. I need to do
while RzLauncher1.Running do begin
sleep( 2000 );
Application.ProcessMessages;
end;Why is this? That is, why do I need to call Application.ProcessMessages?
I’m using Delphi 11.2.
To see this in action, create two apps. LaunchTest is a console app:
{$apptype console}
program LaunchTest;
uses
System.SysUtils;
begin
System.SysUtils.Sleep( 10000 );
end.Project1 is a VCL app with one unit:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, RzLaunch, Vcl.StdCtrls;
type
TForm1 = class(TForm)
RzLauncher1: TRzLauncher;
Label1: TLabel;
Button1: TButton;
Label2: TLabel;
procedure Button1Click(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
J: integer;
begin
Label2.Caption := ”;
RzLauncher1.Launch;
for J := 0 to 20 do begin
sleep( 1000 );
Label1.Caption := IntToStr( J ) + ‘ ‘ + BoolToStr( RzLauncher1.Running, true );
//Application.ProcessMessages;
end;
Application.ProcessMessages;
Label2.Caption := BoolToStr( RzLauncher1.Running, true );
end;
end.object Form1: TForm1
Left = 0
Top = 0
Caption = ‘Form1’
ClientHeight = 433
ClientWidth = 622
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -12
Font.Name = ‘Segoe UI’
Font.Style = []
TextHeight = 15
object Label1: TLabel
Left = 48
Top = 48
Width = 34
Height = 15
Caption = ‘Label1’
end
object Label2: TLabel
Left = 48
Top = 80
Width = 34
Height = 15
Caption = ‘Label2’
end
object Button1: TButton
Left = 56
Top = 112
Width = 75
Height = 25
Caption = ‘Button1’
TabOrder = 0
OnClick = Button1Click
end
object RzLauncher1: TRzLauncher
Action = ‘Open’
FileName = ‘C:\Mine\Test\LaunchTest.exe’
Timeout = -1
Left = 240
Top = 32
end
endChange RzLauncher1.FileName to point to the LaunchTest.exe. Run Project1. Click the button. After 20 seconds, the labels will display
20 True
FalseUncomment the Application.ProcessMessages statement in the loop. Run Project1, and click the button. After 10 seconds, the first label displays
11 False
-
January 24, 2023 at 11:10 pm #3428
Hi David,
I’m a little confused by what you are trying to accomplish given your example. When using the Launch method to launch another app, you have the choice to wait for the launched app to finished (WaitUntilFinished := True), which means that the call to Launch blocks and only returns when the launched app terminates. If WaitUntilFinished is set to False, the Launch method returns immediately and your app continues to run, *and* when the launched app terminates the OnFinished event is fired.
In your sample code, you are launching the secondary app and then trying to use a loop to essentially wait for the launched app to terminate. I would suggest writing an OnFinished event handler instead. If you need to update your UI while the launched app is running, for example to prevent the user from doing certain activities, you can set that up before calling Launch. Then when OnFinished is invoked, you can update the various flags so that the user will be able to continue.Ray
-
January 25, 2023 at 2:50 pm #3432
Ray,
I just tried the OnFinished event. I had it set a boolean field of the form
when it fired. It has the same behavior: I need to call
Application.ProcessMessages for the boolean field to be set.My app launches the secondary app, then continues to do other stuff. But,
there are some things I don’t want the app to do while the launched process
is still running.The way you outlined it, the main app would be idle while the launched
process finishes, so the VCL would call ProcessMessages. If my app already
knows what it wants to do next, but doesn’t want to start doing it until
the secondary app finishes, it needs to wait. That’s why I have the loop.
Maybe I can rearrange things so my app doesn’t need to wait.Regardless, I’d still like to understand why threads work this way. Is the
following correct? The main app is thread 1. The TRzLaunchThread object is
thread 2. The launched process is thread 3. The TRzLauncher object is
passed to TRzLaunchThread, so both threads 1 and 2 can read/write the
FRunning field. But, something doesn’t happen unless ProcessMessages is
called; I don’t know what.David
-
January 29, 2023 at 12:25 am #3435
Hi David,
I created test a project that I think may help explain things a bit. I dropped two TRzButton controls (btnLaunch and btnContinue) and a TRzLauncher onto the form. I also created a new enum called TAppState and created a corresponding private field, FAppState, to keep track of the current state of the app. The TRzLauncher has its FileName property set to ‘C:\Windows\Notepad.exe’. When the app starts, FAppState defaults to appInit. The btnLaunchClick event handler looks at the FAppState field and only launches Notepad if FAppState is not equal to appWaiting. Before calling RzLauncher1.Launch, FAppState is set to appWaiting.
The btnContinueClick event handler also looks at the FAppState field, but only calls ShowMessage if the state is appContinue. The FAppState field is set to appContinue in the OnFinished event handler.
When you start the app, clicking the Continue button does nothing since FAppState is appInit. Clicking the Launch button launches Notepad. While Notepad is running, clicking Launch or Continue does nothing. Once Notepad is closed, then clicking Continue will show the message box.
The point is that it is not necessary to do any looping to accomplish what you want. In fact, it is the looping (in the UI thread) that is forcing you to call Application.ProcessMessages.
Ray
unit Unit26; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, RzButton, RzLaunch, Vcl.StdCtrls, RzLabel; type TAppState = ( appInit, appWaiting, appContinue ); TForm26 = class(TForm) btnLaunch: TRzButton; RzLauncher1: TRzLauncher; btnContinue: TRzButton; procedure btnLaunchClick(Sender: TObject); procedure btnContinueClick(Sender: TObject); procedure RzLauncher1Finished(Sender: TObject); private { Private declarations } FAppState: TAppState; public { Public declarations } end; var Form26: TForm26; implementation {$R *.dfm} procedure TForm26.btnLaunchClick(Sender: TObject); var J: integer; begin if FAppState <> appWaiting then begin FAppState := appWaiting; RzLauncher1.Launch; end; end; procedure TForm26.btnContinueClick(Sender: TObject); begin if FAppState = appContinue then begin ShowMessage( 'Allowed to Continue' ); end; end; procedure TForm26.RzLauncher1Finished(Sender: TObject); begin FAppState := appContinue; end; end.
-
January 29, 2023 at 9:12 am #3436
Ray,
Thank you. Your test project is what I was thinking of when I wrote “The
way you outlined it, the main app would be idle while the launched process
finishes, so the VCL would call ProcessMessages.”Currently, my app does more in the main thread than it should. So, I’d need
to do a good bit of recoding to make it work like your test app. I have
this on my to-do list.David
-
January 29, 2023 at 10:49 am #3437
P.S. I’m still wondering why, in my test app, thread 1 needs to call ProcessMessages for thread 2 to update FRunning.
-
-
AuthorPosts
- You must be logged in to reply to this topic.