Can I guarantee the execution of custom code for final completion AFTER CHANGE of the form?

I have a multi-threaded application with many forms, but I need to instantiate some classes and call some initialization elements before creating the forms. Of course, I have to execute the appropriate exit code.

Here is a simplified .dpr file:

begin // .dpr project file LoadDlls; try Config := TConfig.Create; try Application.Initialize; Application.Title := 'Foo'; Application.CreateForm(TMainForm, MainForm); Application.CreateForm(TOtherForm, OtherForm); //...other forms... Application.Run; finally Config.Free; end; finally UnloadDlls; end; end; 

The problem is that the code inside the finally blocks will execute before the OnDestroy / destructor my forms. This becomes clear by looking at the finalization section of the Form block:

 finalization if Application <> nil then DoneApplication; 

And DoneApplication calls Application.DestroyComponents , which effectively frees all Application owned Forms.

So, forms created using Application.CreateForm will be destroyed after any code inside the main begin..end block.

I want all forms to be destroyed after Application.Run so that their OnDestroy event handlers can see the Config object and the external functions defined in my DLLs. The same if an exception occurs. But I also want to have standard application exception handling if Config.Free or UnlodDlls raise (the application should still exist).

Note that:

  • I would prefer not to use the finalization block (is this possible in .dpr?) To clear the code and debug the code;
  • Currently, I prefer not to change too much code (for example, dynamically create forms)

I think the easiest solution is to explicitly call Application.DestroyComponents after Application.Run . Do you think there are any flaws? Is there a more elegant solution?

thanks

+4
source share
2 answers

The purest way to achieve what you want is to control the destruction of forms.

The only form that should belong to Application is your main form. This should be so because the first form created by calling Application.CreateForm is designated as the main form. So my advice is that you should make one call and only call Application.CreateForm in order to create the main form. For all your other forms, create them by calling their constructors. Let the remaining forms belong to the main form. When it is time to close, destroy the main form and let it take with it all the forms belonging to it.

You can write your .dpr code like this:

 begin LoadDlls; try Config := TConfig.Create; try Application.Initialize; Application.Title := 'Foo'; Application.CreateForm(TMainForm, MainForm); try OtherForm := TOtherForm.Create(MainForm); YetAnotherForm := TYetAnotherForm.Create(MainForm); Application.Run; finally FreeAndNil(MainForm); // will destroy the other forms since they are owned by the main form end; finally Config.Free; end; finally UnloadDlls; end; end; 

Another point that you may not need to unload DLLs. Since this is an explicitly executable file, the system will unload them anyway. Why do you need this?

+6
source

Another option is to prevent your forms from implicitly referencing the global configuration.
Make explicit dependency explicit by providing each form with its own link to the IConfig interface.
When the RefCount of the reference instance drops to zero (after all the forms that use it have been destroyed), it can self-destruct.

Defining the dependency of the form (and another object) on the explicit configuration will have other advantages.

  • It will be much easier to test.
  • Forms that do not need IConfig will not have dependencies, and still will not.
  • Therefore, these forms will easily (and obviously) move to other applications with a slightly different structure.
+4
source

Source: https://habr.com/ru/post/1501497/


All Articles