Delphi: App initialization - best practices / approach
Asked Answered
C

7

3

I run into this regularly, and am just looking for best practice/approach. I have a database / datamodule-containing app, and want to fire up the database/datasets on startup w/o having "active at runtime" set to true at design time (database location varies). Also run a web "check for updates" routine when the app starts up.

Given TForm event sequences, and results from various trial and error, I'm currently using this approach:

I use a "Globals" record set up in the main form to store all global vars, have one element of that called Globals.AppInitialized (boolean), and set it to False in the Initialization section of the main form.

At the main form's OnShow event (all forms are created by then), I test Globals.AppInitialized; if it's false, I run my "Initialization" stuff, and then finish by setting Globals.AppInitialized := True.

This seems to work pretty well, but is it the best approach? Looking for insight from others' experience, ideas and opinions. TIA..

Coughlin answered 19/12, 2008 at 23:13 Comment(3)
A video from 10 years ago: youtu.be/_PJdZjM2oTw which covers this a bit.Kira
#2076339Joub
#13412296Joub
O
12

I generally always turn off auto creation of all forms EXCEPT for the main form and possibly the primary datamodule.

One trick that I learned you can do, is add your datamodule to your project, allow it to auto-create and create BEFORE your main form. Then, when your main form is created, the onCreate for the datamodule will have already been run.

If your application has some code to say, set the focus of a control (something you can't do on creation, since its "not visible yet") then create a user message and post it to the form in your oncreate. The message SHOULD (no guarantee) be processed as soon as the forms message loop is processed. For example:

const
  wm_AppStarted = wm_User + 101;


type
  Form1 = class(tForm)
    :
    procedure wmAppStarted(var Msg:tMessage); message wm_AppStarted;
  end; 

// in your oncreate event add the following, which should result in your wmAppStarted event firing.
PostMessage(handle,wm_AppStarted,0,0);

I can't think of a single time that this message was never processed, but the nature of the call is that it is added to the message queue, and if the queue is full then it is "dropped". Just be aware that edge case exists.

Orenorenburg answered 20/12, 2008 at 3:42 Comment(3)
+1 for the hint on disabling form auto creation. It's one of the reasons that Delphi apps start slower than they would have to. Also Windows messages for delayed processing are worth looking into.Aegyptus
What should be in the body of the wmAppStarted procedure?Crossjack
@Crossjack whatever logic you want to perform considering at this point the application is up and running and processing messages.Orenorenburg
H
6

You may want to directly interfere with the project source (.dpr file) after the form creation calls and before the Application.Run. (Or even earlier in case.)

This is how I usually handle such initialization stuff:

...
Application.CreateForm(TMainForm, MainForm);    
...
MainForm.ApplicationLoaded; // loads options, etc..
Application.Run;
...
Headsman answered 20/12, 2008 at 0:37 Comment(3)
Yes! It's important to know that the "main form" is not the entry point of the program, and OnShow is not the entry point of a form.Burn
you are executing code BEFORE the program (TApplication) has the chance to enter into the message loop! skamradt's answer is much better/safe!Joub
Even worst, you mess up with the default exception handling mechanism of Delphi: docwiki.embarcadero.com/RADStudio/Rio/en/… I quote: "There are certain circumstances where HandleException does not get called. Exceptions that occur before or after the execution of the application's Run method are not caught and handled by HandleException"Joub
P
3

I don't know if this is helpful, but some of my applications don't have any form auto created, i.e. they have no mainform in the IDE.

The first form created with the Application object as its owner will automatically become the mainform. Thus I only autocreate one datamodule as a loader and let this one decide which datamodules to create when and which forms to create in what order. This datamodule has a StartUp and ShutDown method, which are called as "brackets" around Application.Run in the dpr. The ShutDown method gives a little more control over the shutdown process.

This can be useful when you have designed different "mainforms" for different use cases of your application or you can use some configuration files to select different mainforms.

Pul answered 20/12, 2008 at 11:0 Comment(3)
This is pretty creative, and I can think of a few instances it would be useful for. Thanks for posting it!Coughlin
Can you describe the "brackets": "This datamodule has a StartUp and ShutDown method, which are called as "brackets" around Application.Run in the dpr"Joub
@Gravity Application.CreateForm(TdmLoader, dmLoader); dmLoader.StartUp; Application.Run; dmLoader.ShutDown;Pul
R
2

There actually isn't such a concept as a "global variable" in Delphi. All variables are scoped to the unit they are in and other units that use that unit.

Just make the AppInitialized and Initialization stuff as part of your data module. Basically have one class (or datamodule) to rule all your non-UI stuff (kind of like the One-Ring, except not all evil and such.)

Alternatively you can:

  • Call it from your splash screen.
  • Do it during log in
  • Run the "check for update" in a background thread - don't force them to update right now. Do it kind of like Firefox does.
Renteria answered 20/12, 2008 at 0:26 Comment(2)
Thanks Jim - helpful. Point noted too re: "global variable" distinction. Timing-wise, do you see any problems w/"firing" the init stuff from the main form's OnShow event? (I like your idea of housing the actual code for it in the data module, though).Coughlin
The problem with doing it from OnShow is that showing the form has no relation to the things you want to do. Not everything needs to happen in the context of a form.Burn
A
1

I'm not sure I understand why you need the global variables? Nowadays I write ALL my Delphi apps without a single global variable. Even when I did use them, I never had more than a couple per application.

So maybe you need to first think why you actually need them.

Abubekr answered 19/12, 2008 at 23:39 Comment(1)
In general, I avoid them, but in some instances, I've found there are a few variables that seem like they best belong to the "app itself." This is one example. Sometimes I check it before running some routine in another form. It could also be set up as a "property" of a form or DM instead.Coughlin
L
1

I use a primary Data Module to check if the DB connection is OK and if it doesn't, show a custom component form to setup the db connection and then loads the main form:

Application.CreateForm(TDmMain, DmMain);

  if DmMain.isDBConnected then
    begin
      Application.CreateForm(TDmVisualUtils, DmVisualUtils);
      Application.CreateForm(TfrmMain, frmMain);
    end;

  Application.Run;
Leucocytosis answered 8/5, 2009 at 12:32 Comment(0)
E
-1

One trick I use is to place a TTimer on the main form, set the time to something like 300ms, and perform any initialization (db login, network file copies, etc). Starting the application brings up the main form immediately and allows any initialization 'stuff' to happen. Users don't startup multiple instances thinking "Oh..I didn't dbl-click...I'll do it again.."

Eyrir answered 21/12, 2008 at 13:42 Comment(8)
And, if your timer is not long enough? Do your initialization and then send your main for a Windows Message.Ideal
@Rigel. Cheap is right. Got apps running for nearly 20 years (started with Delphi 2 all the way to Delphi 10) with that trick without a single problem with reliability. Only problems I ever saw were the devs who try to be clever with threads and the like.Eyrir
So, BEING CLEVER is a problem for you? Would you recommend to all Delphi programmers to be stupid? This is the new smart now?Joub
And @Orenorenburg is the most stupid of us because he provided the best solution?Joub
And how would you answer Mawg's question?Joub
@Rigal Ouch, someone got up on the wrong side of the bed. Might want to read up on SO 's new code-of-conduct before throwing the word 'stupid' around. Its inflammatory and doesn't make you look smarter than anyone else. No problem with clever-ness, got a problem with cleverness that serves the developer's ego and not people. That's why we're here and writing code, to solve problems and serve people.Eyrir
As for @Ideal Delphi's Timer event is single threaded, so once the event fires, do whatever and however long it takes between the begin...end. No threading, no obscure low-level window message handling. Code addiction is a serious disease. As the great Marco Cantu said/says "If you are writing a lot of code to do something, you are probably doing it wrong."Eyrir
@Rigal As far as skamradt , no, not stupid. His solution is fine, but it is how a code-addict would solve such a problem. In my experience, the less code you write, the less crap for the next developer to make fun of. No matter how good/clever you think your code is, it is going to be crap to another developer and likely why you chose to reply to a SO post that was nearly 10 years old.Eyrir

© 2022 - 2024 — McMap. All rights reserved.