Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Things to be aware in Delphi 12 (where EMB broke lots of code out there)
#1
After some more time playing with Delphi 12 and supporting different applications

1) Indexes in lists are now NativeInt. It means that in 32 bits, nothing changes, but in 64 bits it becomes a Int64.

Whatever TList, TStringList, TObjectList, etc. descendants that you have in your code where you have new Get() and Put() methods for the list you will need to rewrite and (the worst part) probably declare a new alias for the NativeInt type if you need to code to keep compatibility with older Delphi versions.
Warning: The compiler doesn't give you clear messages for this case.

I have really no idea why Embarcadero decided to do that, really. I asked Marco Cantu on his blog but seems that he didn't like/approve my question. Funny.  Tongue

2) OnCreate/OnDestroy events of Forms are triggered from AfterContruction/BeforeDestruction methods, not from Create/Destroy as before. It means that code where you override the contructor of the form and then initializes data inside OnCreate may start failing. Check this example:

Code:
type
  TMyForm = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    FMyList: TStringList;
  public
    constructor CreateEx(AOwner: TComponent; aSomeItem: string);
  end;
 
implementation

constructor TMyForm.CreateEx(aOwner: TComponent; aSomeItem: string);
begin
  inherited Create(aOwner);
  FMyList.Add(aSomeItem);
end;

procedure Tv6LayerEditor.FormCreate(Sender: TObject);
begin
  inherited;
  FMyList := TStringList.Create;
end;
 
The code above works perfectly fine since Delphi 1 but now on Delphi 12 it fails with an AV inside the CreateEx() constructor. This happens because FMyList is created only inside Form's OnCreate event (FormCreate above) which will only happen after the AfterConstruction has been executed. Until Delphi 11, the event would fire when calling the inherited Create() constructor.

Although I believe that this is "correct", especially because exceptions inside OnCreate event wouldn't cause the constructor to  fail, the fact that there is no way to keep the old behavior is problematic.
There is no simple way to detect this problem unless through code inspection. In a huge code base it can take a long time and it's very error prone.
This is another change that I see no reason for and don't know how it got approved...
Reply
#2
(02-14-2024, 01:04 AM)Alexandre Machado Wrote: 2) OnCreate/OnDestroy events of Forms are triggered from AfterContruction/BeforeDestruction methods, not from Create/Destroy as before.

As it should be. That has been the default behavior for over 25 years. If you wanted the events triggered during the (base class) constructor/destructor, you had to set the OldCreateOrder property to True (it was False by default). That property was first introduced in Delphi 4 or 5 (not sure which one, but definitely one of them), and was (finally!) removed in Delphi 11.

(02-14-2024, 01:04 AM)Alexandre Machado Wrote: It means that code where you override the contructor of the form and then initializes data inside OnCreate may start failing.

If you are overriding the constructor, why are you also using the OnCreate event? Seems counter-productive to me. Use one or the other, never both together.

(02-14-2024, 01:04 AM)Alexandre Machado Wrote: The code above works perfectly fine since Delphi 1

In Delphi 1-3 yes, because there was no other option back then. But that changed once OnCreate was moved to AfterConstruction() and OldCreateOrder was introduced to let people go back to the old behavior if they wanted it. But the default behavior since Delphi 4/5 has always been to trigger OnCreate in AfterConstruction() (and trigger OnDestroy in BeforeDestruction()).

(02-14-2024, 01:04 AM)Alexandre Machado Wrote: but now on Delphi 12 it fails with an AV inside the CreateEx() constructor.

As it should be, because that code you showed is just bonkers. It should NEVER have worked in the first place, unless you had OldCreateOrder set to True (which you never should).

(02-14-2024, 01:04 AM)Alexandre Machado Wrote: Until Delphi 11, the event would fire when calling the inherited Create() constructor.

The only way that could ever have happened is if you had set OldCreateOrder to True. That is no longer an option in Delphi 11 onward.

Reply
#3
(02-14-2024, 05:29 PM)rlebeau Wrote: As it should be.  That has been the default behavior for over 25 years.  If you wanted the events triggered during the (base class) constructor/destructor, you had to set the OldCreateOrder property to True (it was False by default).  That property was first introduced in Delphi 4 or 5 (not sure which one, but definitely one of them), and was (finally!) removed in Delphi 11.

Aha! You are "kind of" right here, but not entirely... OldCreateOrder was false by default for new forms. For existing forms it was considered to be True if the property didn't exist in the DFM. It means in practice that any DFM file that has been around for some time has a considerable chance to end up with OldCreateOrder = True in the DFM file. This is a grep search result in a single project:

   

336 files need to be inspected, including 3rd party ones like ReportBuilder forms, shown in the picture.

That's a gigantic effort for absolutely no gain. Removing a boolean property and a couple of lines of code from Forms.pas unit has zero benefit for any Delphi developer, except a minor imperceptible benefit for EMB devs themselves. 

Please notice that I said in my initial post that I consider the behavior of OnCreate/OnDestroy events to be correct. But the decision to break backwards compatibility removing the OldCreateOder property is definitely not.

(02-14-2024, 05:29 PM)rlebeau Wrote: If you are overriding the constructor, why are you also using the OnCreate event? Seems counter-productive to me. Use one or the other, never both together.

It should be clear to any Delphi developer by now that whenever we talk about existing Delphi code, we are probably talking about code that may be around for 20+ years.
I'm not saying that the code is correct or it should be written that way. I'm talking about code that *exists* in a real world project and, believe it or not, works as expected since Delphi 3 when it was first written. I didn't write it, I didn't review it 20 years ago, but I have to live with the consequences of EMB breaking backwards compatibility.

Fixing *real code* demands *real people* to review/fix possibly broken code and it is risky. That's the whole point.

More importantly here, when the Product Owner (very likely someone with no development background) asks your professional assessment about the *risks* involved in migrating a project from, say, Delphi 10.4 to Delphi 12 you can't just ignore this.

Guess who loses if the PO decides not to update????
Reply
#4
Searching within the previous results, I decided to check how many of those forms have a Form's OnCreate event set. Check it out:

   

268 forms need to be reviewed and possibly fixed in order to have this app migrated to D12. Anything less than this is just wishful thinking. I guess it's safe to assume that Marco Cantú doesn't have an application such as this.

In a very optimistic scenario: 268 forms x 5 minutes / form = 1340 minutes = 22 hours of a tedious, very error prone work. Basically 3 days of a skilled deveoper to review this crap. I bet that didn't take EMB more than 1 hour do drop this.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)