Here is some simplified code context to my question:
TSomeone = record
FirstName: String;
LastName: String;
Picture: TGraphic;
end;
TSomeoneHelper = record helper for TSomeone
public
procedure Clear();
procedure LoadFromFile(const Filename: String);
end;
procedure TSomeoneHelper.Clear();
begin
Self.FirstName := '';
Self.LastName:= '';
try
if Assigned(Self.Picture) then
FreeAndNil(Self.Picture); // <---- Crash here in 64-bit release
except
Self.Picture := nil;
end;
end;
Normally someone would declare a TSomeone
variable and then call myVar.LoadFromFile('myfile.blah')
to fill the record. In the LoadFromFile
procedure a TJPEGImage
(TGraphic
descendant) is created and then assigned to Picture
.
Since I'm not in a class (no constructor in record helper
) I have no way to initialize Picture
to nil
. Because of that FreeAndNil
crashes. What's weird is that on 32-bit builds, it seems initialized to nil
but in 64 bit builds it's not (it's "Inaccessible value"). I added the try except
for that reason. But even weirder in 64-bit release I get an access violation that is not catched by the try except
.
To sum up:
Picture
is initialized to nil
because magic (?), so no exception - all is goodPicture
is initialized "Inaccessible value" which trigger Assigned
then FreeAndNil
does an access violation but the try
catches it so all is goodtry
don't catch the access violation and the error is thrown to the user so badWhat can I do to fix this?
The fact that it works differently between build type and architecture is just luck. This should not work anywhere (the OP comments just confirmed what I thought).
If using Delphi 10.4+ you use Custom Managed Records to initialize the (unmanaged) var to nil
and the Assigned()
call will not do access violations.
In the end I just refactored my code to use classes instead of records. In the constructor I initialize the var to nil
. It's cleaner and work with any version of Delphi.