Follow-up to #27297. TMS VCL UI Pack 13.6.9.2 / DBAdvGrid 2.8.3.13, Delphi 13.1, Win64, FireDAC TFDMemTable, grid settings: PageMode = False, AutoCreateColumns = True, AutoRemoveColumns = False.
Usage pattern: create a TFDMemTable, fill it, bind the grid (DataSource.DataSet := MemTable; Grid.DataSource := DataSource), detach (DataSource.DataSet := nil), close and free the MemTable; on the next load call Grid.RemoveAllColumns and bind a new MemTable.
Comparing 2.8.3.11 and 2.8.3.13, the fix consists of two lines:
(a) In TDBAdvGrid.Notification the new if Items[I].Field = AComponent then Items[I].Field := nil sits inside if (Operation = opRemove) and (AComponent is TField) and FAutoRemoveColumns then. With AutoRemoveColumns = False it is never executed, so TDBGridColumnItem.FField still dangles after the dataset's fields are destroyed – the original defect is unchanged for this configuration.
(b) In RemoveAllColumns the new Columns[Columns.Count - 1].Field := nil before Free now dereferences that dangling pointer: SetField(nil) → FField.RemoveFreeNotification(Grid) → TComponent.RemoveNotification reads FFreeNotifies from freed memory. 2.8.3.11 merely dropped the pointer, so the corruption there was layout-dependent; 2.8.3.13 fails deterministically: every load now raises Access violation … Read of address 0000000200000010. The two surviving column items (Columns.Count > cnt) are not touched at all, because SetFieldName('') intentionally skips SetField.
Suggested fix: in Notification, handle opRemove of a TField independent of AutoRemoveColumns – for every column with FField = AComponent set FField := nil directly (not via the Field property, whose getter may re-resolve via FindField during destruction), as VCL TCustomDBGrid.Notification does. Once that is in place, the Field := nil in RemoveAllColumns is safe and should be applied to all columns, including the ones that survive. TDBGridColumnItem.Destroy should also release FField via SetField(nil).
Call stack of the access violation with 2.8.3.13 (our workaround disabled):
System.Classes.TComponent.RemoveNotification
System.Classes.TComponent.RemoveFreeNotification
DBAdvGrid.TDBGridColumnItem.SetField(nil)
DBAdvGrid.TDBAdvGrid.RemoveAllColumns
<application: load routine, called on tree selection change>
For reference, the stack with 2.8.3.11 (same scenario) was TDBAdvGrid.ActiveChange → TDBGridColumnItem.SetFieldName → SetField → TComponent.RemoveFreeNotification → TObject.Free → FireDAC.DatS.TFDDatSObject.Destroy → TFDDatSColumnList.RemoveAt → TFDDatSTable.CheckLayoutChanging → [FireDAC][DatS]-38 – the same dangling FField, dereferenced later and on reused memory.
Our workaround (setting Columns[i].Field := nil while the fields are still alive) remains in place; with it, 2.8.3.13 works.