Jump to content
Ian Branch

Possible D10.4.2 issue..

Recommended Posts

Posted (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 by Ian Branch

Share this post


Link to post

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

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

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

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
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

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
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

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
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. 

  • Thanks 1

Share this post


Link to post
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
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 (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 by Guest

Share this post


Link to post

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 (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

 

 


image.thumb.png.aa7ea4653e22660520a4db702d55e8f5.png
 

 

 

hug

Edited by Guest

Share this post


Link to post

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
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

Aren't you freeing the form twice?

 

caFree calls Release, Release posts a CM_Release message and that calls Free..

  • Like 1

Share this post


Link to post
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.

 

  • Like 1

Share this post


Link to post

Ahhhh Ha!

Tks Francois.

I have eliminated the caFree in the OnClose event.

 

Regards,

Ian

 

Share this post


Link to post

@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
Posted (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 by balabuev

Share this post


Link to post
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

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×