14

I have a small application that most of the time have an action behind a Start-button that should be triggered from the commandline parameter /AUTORUN. If that parameter is missing the user could also press it manually.

My question is where should I place this check for commandline so when it is given the GUI is still updated. The current solution is this, but the GUI is not updated until the action is finished.

procedure TfrmMainForm.FormShow(Sender: TObject);
begin
  if FindCmdLineSwitch('AUTORUN') then
    btnStart.Click;
end;
Roland Bengtsson
  • 5,058
  • 9
  • 58
  • 99
  • related: https://stackoverflow.com/questions/11679235/which-is-the-best-place-to-initialize-code – Gabriel Jun 08 '20 at 14:42
  • Does this answer your question? [Show a splash screen while a database connection (that might take a long time) runs](https://stackoverflow.com/questions/10678503/show-a-splash-screen-while-a-database-connection-that-might-take-a-long-time-r) – Gabriel Jun 08 '20 at 14:43

3 Answers3

23

Post yourself a message from your OnShow event handler. This will be processed as soon as your application starts servicing its message queue. That only happens when the application is ready to receive input. Which matches your my understanding of your requirements.

const
  WM_STARTUP = WM_USER;
....
procedure TfrmMainForm.FormShow(Sender: TObject);
begin
  PostMessage(Handle, WM_STARTUP, 0, 0);
  OnShow := nil;//only ever post the message once
end;

Add a message handler to deal with the message:

procedure WMStartup(var Msg: TMessage); message WM_STARTUP;

You'd implement that like this:

procedure TfrmMainForm.WMStartup(var Msg: TMessage);
begin
  inherited;
  if FindCmdLineSwitch('AUTORUN') then
    btnStart.Click;
end;
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • 1
    What does `inherited;` in this message handler do? – user1580348 May 04 '17 at 15:26
  • It performs any default message handling, or anything implemented in parent classes. In this case there is probably little point because it's a user defined message. However, I generally write this as good practice for message handlers. – David Heffernan May 04 '17 at 15:47
  • From my understanding of `TCustomForm.ShowModal: Integer;` it already sends a message `CM_ACTIVATE` just before it enters a blocking loop. Couldn't he use that? – nurettin Apr 04 '18 at 06:51
  • So, what is better to send that message to myself in OnShow or in OnCreate? (as shown here https://stackoverflow.com/questions/11679235/which-is-the-best-place-to-initialize-code ). I used until now, OnCreate, but I tend to change my code and go for OnShow since it is later. – Gabriel Jun 08 '20 at 14:08
5

In the FormShow post yourself a message. In the message handler run your btnStart.

TfrmMainForm = class(TForm)
// snip
private
  procedure AutoStart(var Message: TMessage); message wm_user;
// snip
end

procedure TfrmMainForm.FormShow(Sender: TObject);
begin
  if FindCmdLineSwitch('AUTORUN') then
    PostMessage(Handle, wm_user, 0, 0);
end;

procedure TfrmMainForm.AutoStart(var Message: TMessage);
begin
  btnStart.Click;
end;
Keith Miller
  • 1,718
  • 1
  • 13
  • 22
-2

An easy way would be a timer, with an event like this:

begin
  Timer1.Enabled := False;
  if FindCmdLineSwitch('AUTORUN') then
    btnStart.Click;
end;

And an interval of a few thousand milliseconds.

iMan Biglari
  • 4,674
  • 1
  • 38
  • 83
  • Don't use a timer for this. Instead post a message to the form that causes the btnStart to run. – Keith Miller Jan 14 '13 at 13:02
  • @KeithMiller I'm lost when it comes to messages. Perhaps you could post it as another answer? – iMan Biglari Jan 14 '13 at 13:13
  • 1
    Don't do this. Now you lose control over when the thing runs. – David Heffernan Jan 14 '13 at 13:15
  • I think this is a fine, easy solution to the problem. It ensures all the normal startup is done, and needs no knowledge about more arcane things. If you need something more, then solve that problem when you get to it. – mj2008 Jan 14 '13 at 13:17
  • 1
    @mj2008 It allows the user to invoke actions before this one runs. – David Heffernan Jan 14 '13 at 13:18
  • 1
    @David If the user is able to get in within a few hundred milliseconds, then they are doing well. Set the time value to 1 if you think it is that important, or disable the interface until this code has run. But the usual use of this sort of thing is to have the standard action carried out automatically, instead of the user pressing the button. While it may not be perfect, it works for most cases and is easy to implement. – mj2008 Jan 14 '13 at 13:20
  • 5
    @mj2008 Depending on how long it takes to startup, the user could have a few seconds. Timer messages are low priority and are only ever synthesised when the message queue is empty. A timer is completely inappropriate here. – David Heffernan Jan 14 '13 at 13:22
  • -1 This is not only a messy solution but can also cause issues, while not resolving OP's question. How can anyone assume that it will take "a few seconds" before the app is ready? For all we know, it might take 10 seconds on a slow machine for this app to start. – Jerry Dodge Jan 16 '13 at 01:30
  • Given a 1ms interval, I think this solution is fine. The only difference with posting a custom message is that the timer message (which is also posted) has lower priority. – Sertac Akyuz Jun 27 '20 at 14:50