Ian Branch 128 Posted March 2, 2021 (edited) Hi Team, For years, up to and including D10.4.1, I have been using the following in my App, and it has been running without issue via RDP on a Customer's Win 2012 R2 Server.. {Main Form code} procedure TMainForm.mnuRemovePartsClick(Sender: TObject); begin // TRemovePartsForm.Create(Self).ShowModal; // end; {code} and in RemovePartsForm I have this at Close.. {RemovePartsForm code} procedure TRemovePartsForm.FormClose(Sender: TObject; var Action: TCloseAction); begin dmS.DBC1.CloseDataSets; Action := caFree; end; {code} Closing RemovePartsForm takes me back to the Main Form. Rebuilding the App in D10.4.2, and running it on the Server, when the Close button is clicked and the form closes the App crashes to the OS effectively at the Action := caFree! 😞 It does not happen on my Dev Win 10 PC. I have a work around.. {Main Form code} procedure TMainForm.mnuRemovePartsClick(Sender: TObject); var RemovePartsForm: TRemovePartsForm; begin // RemovePartsForm := TRemovePartsForm.Create(Self); // try // RemovePartsForm.ShowModal; // finally RemovePartsForm.Free; end; // end; {code} and .... {RemovePartsForm code} procedure TRemovePartsForm.FormClose(Sender: TObject; var Action: TCloseAction); begin dmS.DBC1.CloseDataSets; end; {code} And it all works as it used to. Unless someone can enlighten me otherwise, I suspect an issue here.. Regards, Ian Edited March 2, 2021 by Ian Branch Share this post Link to post
David Heffernan 2353 Posted March 2, 2021 Just a comment. Explicitly destroying the form is objectively better. The form should not take the decision that when closed it must die. That's best done by the form's owner. Share this post Link to post
FPiette 385 Posted March 2, 2021 Using the debugger, you should see where your application crashes. This would probably give a hint about why it crashes. Did you tried with a minimal program? If not, do it and tell use if it works as expected. If it doesn't, show the code for that minimal program so that we can verify it. Share this post Link to post
Ian Branch 128 Posted March 2, 2021 Hi Francois, It doesn't fail on my PC. Only when run via RDP at the Client's site on their Server. Just to totally verify, I am going back to 10.4.1 to test further. Ian Share this post Link to post
Ian Branch 128 Posted March 2, 2021 I have reverted back to 10.4.1, with the code reverted to pre 10.4.2. Same IDE Plug-ins, same libraries. All testing so far has not produced any crashes. Testing continues. Share this post Link to post
FPiette 385 Posted March 2, 2021 1 hour ago, Ian Branch said: Only when run via RDP at the Client's site on their Server. So this is related to RDP or to the server (What is it?) where Delphi is installed. Do you have a chance to ask someone at the remote site to check if it works locally without any RDP but directly on the machine? Share this post Link to post
Ian Branch 128 Posted March 2, 2021 Hi Francois, The Server is a Win 2012 R2. My apologies, incomplete information. They have checked via RDP, on the Server PC itself, and on their Win7 & Win 10 workstations with the same result. Very frustrating. But, as I said, I can't make it happen here on my Win 10 Dev PC. I am giving up on it att until the update for 10.4.2 arrives and I will retry it then. I have spent two full days on it now and the Customer is rightly teed off. Restoring to 10.4.1 has placated him att. Ian Share this post Link to post
Cristian Peța 107 Posted March 2, 2021 6 hours ago, Ian Branch said: when the Close button is clicked and the form closes the App crashes to the OS effectively at the Action := caFree! How do you know that it crashes exactly at that line of code? Do you have a stack-trace? A message? Share this post Link to post
Ian Branch 128 Posted March 2, 2021 Yes. I put a message in before and after the Action line. Got both but it never back to the main form where I had another message. Share this post Link to post
Cristian Peța 107 Posted March 2, 2021 (edited) deleted Edited March 2, 2021 by Cristian Peța Share this post Link to post
David Heffernan 2353 Posted March 2, 2021 8 minutes ago, Cristian Peța said: If you got the message after that line then it doesn't crash at that line. I recommend MadExcept or EurekalLog. But why are you calling Action := caFree; when you do also RemovePartsForm.Free; ??? He isn't. He has tried one or the other, but never both. 1 Share this post Link to post
Lajos Juhász 295 Posted March 2, 2021 (edited) deleted Edited March 2, 2021 by Lajos Juhász 1 Share this post Link to post
FPiette 385 Posted March 2, 2021 55 minutes ago, Ian Branch said: They have checked via RDP, on the Server PC itself, and on their Win7 & Win 10 workstations with the same result. Very frustrating. When you said "on the server PC itself", is it using RDP or not? (I'm thinking of what was once name "terminal server"). If this happens on ALL their PC whatever OS they have, this is maybe related to something the have in all enterprise such as a security product, or a remote management or something like that. Share this post Link to post
FPiette 385 Posted March 2, 2021 3 hours ago, FPiette said: Using the debugger, you should see where your application crashes. This would probably give a hint about why it crashes. Did you tried with a minimal program? If not, do it and tell use if it works as expected. If it doesn't, show the code for that minimal program so that we can verify it. You should really do the above. Using the debugger, grab the call stack at the moment of crash. Share this post Link to post
Guest Posted March 2, 2021 (edited) 20 hours ago, Ian Branch said: Rebuilding the App in D10.4.2, and running it on the Server, when the Close button is clicked and the form closes the App crashes to the OS effectively at the Action := caFree! 😞 It does not happen on my Dev Win 10 PC. as I said in another post: if you use "SELF", use ONLY "caFree" if you use "APPLICATION", let for application this task if you use "NIL", YOU is the responsable to destroy it (any class not Interfaced) DONT MIX THE TECHnic's game!!! my tip: if you use "close your DataSets or some connection (???)" later close your "form", my preference always is: before create a form, try open the DataSets if ok, then create your forms --> if any code needs access yours tables, this will be the way ok for that! else, dont worry! if ok, use your form as you need when, all ended, close your DataSets "later"... when returned to procedure that call your form that way, if any error when using your form, then, it will not compromise the closing of the DataSets, of course, if you have protected your code in an appropriate way so as not to leak exceptions. Of course! my preference, if you would like to know (if not ok), is ALWAYS create on-the-fly forms with "nil" owner! then, any error, is mine! And I know that! procedure TfrmFormMain.mnuXXXXClick(Sender: TObject); //var // frmMyFormSecond: TfrmMyFormSecond; // this can be unnecessary, because you can use the "declaration" that is on "FormSecond" by default { unit uFormSecond; ... var frmMyFormSecond := TfrmMyFormSecond; } begin frmMyFormSecond := TfrmMyFormSecond.Create(nil); try frmMyFormSecond.ShowModal; // for FreeAndNil() use, or similar // if needs a "Show" (-> SELF), dont use "FreeAndNil() or similar" and, prefere use "caFree" on "OnClose event"!!! // dont MIX the two ways! for good control of your app! finally FreeAndNil(frmMyFormSecond); // or frmMyFormSecond.Free; frmMyFormSecond := nil; ect... end; end; hug Edited March 2, 2021 by Guest Share this post Link to post
Ian Branch 128 Posted March 3, 2021 Hi Emailx45, Message received. :-) So here's a scenario.. In the project file I have the following.... ... ... begin // Application.Initialize; // UseLatestCommonDialogs := True; // // 7 6 5 4 3 2 1 0 // 1 // Bits 0 -> 1 = 1 if TLogInForm.Execute('DBiUsers', BinToInt('1')) then begin // Application.Title := 'DBiUsers Utility for DBWorkflow'; Application.MainFormOnTaskbar := True; Application.CreateForm(TMainForm, MainForm); Application.Run; // end; end. In the Loginform OnClose event I have the following... .... ..... public { Public declarations } class function Execute(const sApplication: string; const iBits: SmallInt): Boolean; end; .... .... implementation uses UsersData; {$R *.dfm} class function TLogInForm.Execute(const sApplication: string; const iBits: SmallInt): Boolean; begin // sApp := sApplication; iBitsSet := iBits; // with TLogInForm.Create(nil) do try Result := ShowModal = mrOk; finally Free; end; // end; .... .... ... procedure TLogInForm.FormClose(Sender: TObject; var Action: TCloseAction); begin // DBC1.CloseDataSets; // DBC1.Close; DBSLogin.Close; DBE1.Close; // Action := caFree; // end; Based on your description, Free; in the Execute procedure should be FreeAndNil? And the Action := caFree; in the OnClose removed. I'm guessing yes to both. Regards, Ian Share this post Link to post
Guest Posted March 3, 2021 (edited) 51 minutes ago, Ian Branch said: Message received. 🙂 hi @Ian Branch your logic is stranger, but it's your, ok! try this program prjProjeto12; uses Vcl.Forms, Vcl.Dialogs, Vcl.Controls, {TModalResult} uFormMain12 in 'uFormMain12.pas' {frmFormMain} , uFormLogin12 in 'uFormLogin12.pas' {frmFormLogin12}; {$R *.res} begin Application.Initialize; // if TfrmFormLogin12.Create(Application).ShowModal = mrOK then begin Application.MainFormOnTaskbar := True; Application.CreateForm(TfrmFormMain, frmFormMain); //or // if TfrmFormLogin12.Create(Application).ShowModal = mrOK then // See the difference on end it! Application.Run; end; end. FormMain unit uFormMain12; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls; type TfrmFormMain = class(TForm) Button1: TButton; procedure FormDestroy(Sender: TObject); procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var frmFormMain: TfrmFormMain; implementation {$R *.dfm} procedure prcHowManyFormsOnMemory; var sText: string; i : integer; begin for i := 0 to Pred(Screen.FormCount) do sText := sText + Screen.Forms[i].Name + sLineBreak; // ShowMessage(sText); end; procedure TfrmFormMain.Button1Click(Sender: TObject); begin prcHowManyFormsOnMemory; end; procedure TfrmFormMain.FormDestroy(Sender: TObject); begin prcHowManyFormsOnMemory; // ShowMessage('Destroying...'); end; initialization ReportMemoryLeaksOnShutdown := True; finalization end. FormLogin unit uFormLogin12; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls; type TfrmFormLogin12 = class(TForm) Button1: TButton; procedure FormClose(Sender: TObject; var Action: TCloseAction); procedure Button1Click(Sender: TObject); private public end; var frmFormLogin12: TfrmFormLogin12; implementation {$R *.dfm} { TfrmFormLogin12 } procedure TfrmFormLogin12.Button1Click(Sender: TObject); begin Self.ModalResult := mrOK; end; procedure TfrmFormLogin12.FormClose(Sender: TObject; var Action: TCloseAction); begin // close datasets! // Action := caFree; // im using "Application" as my "owner", then, it's not necessary this... end; end. Screenshot hug Edited March 3, 2021 by Guest Share this post Link to post
Ian Branch 128 Posted March 3, 2021 Hi emailx45, The Login form is generic across 18 Apps. In this piece of code.. if TLogInForm.Execute('DBiUsers', BinToInt('1')) then begin // I am passing parameters to the Login form that will indicate the App Name and the permission(s) the User needs for the respective App based on their role in the organisation. Regards, Ian Share this post Link to post
Guest Posted March 3, 2021 Just now, Ian Branch said: I am passing parameters try use a new "constructor" and into it call "default constructor" then you can pass your params for your form, you see? hug Share this post Link to post
FredS 138 Posted March 3, 2021 Aren't you freeing the form twice? caFree calls Release, Release posts a CM_Release message and that calls Free.. 1 Share this post Link to post
FPiette 385 Posted March 3, 2021 5 hours ago, Ian Branch said: with TLogInForm.Create(nil) do try Result := ShowModal = mrOk; finally Free; end; You are freeing twice the form: Once from the OnClose because you use caFree caFree, and once when you call Free in the finally block. The second call will crash. 1 Share this post Link to post
Ian Branch 128 Posted March 3, 2021 Ahhhh Ha! Tks Francois. I have eliminated the caFree in the OnClose event. Regards, Ian Share this post Link to post
David Heffernan 2353 Posted March 3, 2021 @Ian Branch I didn't understand the advice in the post that you appear to be using as your reference. I'm not at all sure thelat you will end up with better code or better understanding by following that advice. Share this post Link to post
balabuev 102 Posted March 3, 2021 (edited) 1 hour ago, FPiette said: You are freeing twice the form: Once from the OnClose because you use caFree caFree, and once when you call Free in the finally block. The second call will crash. Good observation, but wrong. Setting Action to caFree effectively executes form's Release method, which posts CM_RELEASE mesage to the message queue to perform async destroying of the form later (look at VCL source code). So, since the form including its Handle is freed with an explicit Free method in the TLogInForm.Execute, the subsequent CM_RELEASE message is ignored. This can be tested by overriding the message handler: type TLogInForm = class(TForm) //... procedure CMRelease(var Message: TMessage); message CM_RELEASE; //... end; procedure TLogInForm.CMRelease(var Message: TMessage); begin ShowMessage('Ooops.'); inherited; end; Edited March 3, 2021 by balabuev Share this post Link to post
Lajos Juhász 295 Posted March 3, 2021 12 minutes ago, balabuev said: Good observation, but wrong. No it's not wrong. Sometimes it can result an Access Violation, it will cause no problem if in the meanwhile the handle is not used otherwise you cannot predict the result. Share this post Link to post