Incus J 10 Posted June 17, 2022 I have a TDataModule unit in an FMX app, which is auto-created when the app start up (I'm not creating it manually). When the app exits, I'd like a cleanup routine to execute. I've added this in an OnDestroy event handler for the Data Module, like this: procedure TDataModule1.DataModuleDestroy(Sender: TObject); begin Cleanup; end; But as far as I can tell my Cleanup routine is not called. So I've placed a breakpoint on the 'Cleanup;' line above. When I run the app the breakpoint red circle gets a green tick - so I think the breakpoint itself is valid and live. When I quit the app, it simply closes - the IDE does not stop at the breakpoint. Does that imply that the Data Module OnDestroy event is never triggered? Do I need to set anything special for the OnDestroy event to be called on an auto-created TDataModule? (I've had a look through the source for TDataModule in System.Classes - there is a call to FOnDestroy in a DoDestroy routine, but I'm not sure of the destruction sequence, as there's also BeforeDestruction and Destroy methods) Share this post Link to post
David Heffernan 2345 Posted June 17, 2022 Perhaps the data module isn't being destroyed. If you showed a minimal reproduction then we'd be able to tell. Share this post Link to post
Incus J 10 Posted June 17, 2022 (edited) OK I'll try - the project file looks like this: program ProgramName; uses System.StartUpCopy, FMX.Forms, uiMain in 'Code\uiMain.pas' {MainForm}, … dm1 in 'Code\dm1.pas' {DataModule1: TDataModule}, …; {$R *.res} begin Application.Initialize; Application.CreateForm(TMainForm, MainForm); Application.CreateForm(TDataModule1, DataModule1); Application.Run; end. So a main form and the data module are created at startup. That's it really. I'm not manually freeing the data module - I think it is owned by the Application, so will be freed automatically when the Application is terminated (?) The data module itself has two event handlers assigned: OnCreate DataModuleCreate OnDestroy DataModuleDestroy The OnCreate (DataModuleCreate) is working OK. The code in DataModuleDestroy is shown in my initial post above. I can work around it by calling my CleanUp routine via the main form's OnClose event instead, but puzzled. Edited June 17, 2022 by Incus J Share this post Link to post
SwiftExpat 65 Posted June 19, 2022 2 basics that might help take this thread further. What platform are you debugging on? Just to be sure, are you compiling in Debug not Release? Share this post Link to post
Incus J 10 Posted June 20, 2022 Thanks SwiftExpat - the app is running on macOS (deployed by the IDE when I click run). The IDE is running in a Windows VM on the same system (Fusion). Yes, compiling is set to Debug - and I can place breakpoints in other methods, such as DataModuleCreate, and that works OK - the app pauses at that point. So breakpoints in general are working. But when I place a breakpoint in DataModuleDestroy - and then Exit, the app simply closes without pausing at the breakpoint. The breakpoint marker gets a green tick when the app is run, which I think is an indication of a valid breakpoint. So my guess is the DataModuleDestroy procedure is not entered during Exit - so perhaps the associated OnDestroy event isn't occurring (speculation). I posted thinking I must have missed something obvious - and that may well be the case. I'll try setting up the same thing in a new empty project next, see whether I encounter the same behaviour again. Share this post Link to post
SwiftExpat 65 Posted June 20, 2022 On MacOS the process is killed, so the app never gets to shutdown correctly. I opened a support ticket with Embarcadero and was not given a resolution to this. I ended up with a timer to save state every x seconds 😞 Maybe someone else has a better solution. 1 Share this post Link to post
Incus J 10 Posted June 21, 2022 Instant termination sounds like a (fairly major?) bug. If Exiting an app simply kills the process, nothing will get freed, preferences won't save etc. - so I'd need to free everything manually in the main form's Close event perhaps. Did you submit a bug report on the quality site? If yes, and you have a link to the report I can vote for it I think. Share this post Link to post
SwiftExpat 65 Posted June 21, 2022 I thought about this one quite hard today, a bit of fun learning, and here is my suggestion. FMX has the following mapped: procedure TPlatformCocoa.Terminate; begin FRunning := False; FTerminating := True; TMessageManager.DefaultManager.SendMessage(nil, TApplicationTerminatingMessage.Create); NSApp.terminate(nil); end; So in my app I implemented it as this , similar to what they recommend for android. unit dmDestroyMe; interface uses System.SysUtils, System.Classes, System.Messaging; type TDestroyVersion = class public VersionString: string; constructor Create; destructor Destroy; override; end; TDestroyMeDM = class(TDataModule) procedure DataModuleCreate(Sender: TObject); procedure DataModuleDestroy(Sender: TObject); private FVersion1, FVersion2: TDestroyVersion; v1, v2: string; msgSubId: integer; procedure ApplicationTerminatingHandler(const Sender: TObject; const Msg: TMessage); public { Public declarations } end; var DestroyMeDM: TDestroyMeDM; implementation {%CLASSGROUP 'FMX.Controls.TControl'} {$R *.dfm} { TDestroyVersion } uses FMX.Forms; constructor TDestroyVersion.Create; begin VersionString := '1.2.3.4'; end; destructor TDestroyVersion.Destroy; begin VersionString := '0.0.0.0'; inherited; end; procedure TDestroyMeDM.ApplicationTerminatingHandler(const Sender: TObject; const Msg: TMessage); begin v1 := '0.0.1.0'; Fversion1.Free; v2 := '0.1.0.0'; end; procedure TDestroyMeDM.DataModuleCreate(Sender: TObject); begin v1 := '2.0.1.0'; v2 := '3.1.0.0'; FVersion1 := TDestroyVersion.Create; FVersion2 := TDestroyVersion.Create; msgSubId := TMessageManager.DefaultManager.SubscribeToMessage(TApplicationTerminatingMessage, ApplicationTerminatingHandler); end; procedure TDestroyMeDM.DataModuleDestroy(Sender: TObject); begin FVersion2.Free; TMessageManager.DefaultManager.Unsubscribe(TApplicationTerminatingMessage, msgSubId, true); end; end. I will keep looking for the QP issue, but honestly this is one they expect you to implement. I just did not have the experience when I saw this the first time around. DestroyMe.zip 1 Share this post Link to post
Incus J 10 Posted June 22, 2022 Thank you! It looks a little bit over my head at first glance - but I will have a good look through it. Perhaps I also need to have a look at the equivalent TPlatformWindows Terminate procedure and compare with the macOS Cocoa one to see how they differ. Naive question: If they expect you to implement the form destruction event manually, what is the actual purpose of the OnDestroy event property in the object inspector? Share this post Link to post
Lajos Juhász 293 Posted June 22, 2022 13 minutes ago, Incus J said: Naive question: If they expect you to implement the form destruction event manually, what is the actual purpose of the OnDestroy event property in the object inspector? It's a bit different when the form is destroyed while application is running and when the application is closed / terminated. (The OS should/will clean up anything that is not freed by application.) Share this post Link to post
SwiftExpat 65 Posted June 22, 2022 9 hours ago, Incus J said: It looks a little bit over my head at first glance Many of those lines are just there just to be able to place break points and verify what lines are actually executed (or not executed ) when the application terminates. 9 hours ago, Incus J said: what is the actual purpose of the OnDestroy event property in the object inspector Think of the situation where you need a child window, when that window is destroyed ( destroyed and closed are not the same ). In the parent window you would call free on the child window to have the destructor called. Share this post Link to post
Incus J 10 Posted June 24, 2022 OK, I think I've understood (hopefully :). If I free a form or data module manually in code, then its associated OnDestroy event fires. But if the user closes the application, then OnDestroy events for auto-created forms and data modules are bypassed: The application simply terminates and relies on the OS to clean up any allocated memory. On 6/20/2022 at 6:12 PM, SwiftExpat said: I ended up with a timer to save state every x seconds 😞 Maybe someone else has a better solution. I've noticed that a form (but not a data module) has an OnSaveState event in the object inspector. Documentation indicates it is fired when a form is about to close. If that completes before the app terminates, it might be a good place to save preferences/state etc. and clean up. Share this post Link to post
SwiftExpat 65 Posted June 24, 2022 OnSaveState did not fire when I tested 😞 Share this post Link to post