嵌套TClientDataSet克隆在OnBeforePost中触发C0000005访问冲突问题排查
嵌套TClientDataSet克隆在BeforePost事件中出现访问冲突的解决方案
问题背景
我有两个通过DataSetField关联的嵌套TClientDataSet,需要在父数据集的OnBeforePost事件中查询子数据集的其他记录,于是克隆了子数据集:
if not ASourceDataSet.Active then ASourceDataSet.Open; ACopyDataSet.CloneCursor(ASourceDataSet,true,false)
但克隆后的数据集虽处于激活状态,调试器的Evaluate/Modify显示RecordCount=0(实际有数据),单步执行GetRecordCount时,在FDSCursor.GetRecordCount处触发C0000005访问冲突。当前子数据集状态为dsBrowse,FieldCount属性正确(7个字段),所有数据集均为设计时创建,尝试克隆父数据集也出现同样问题,使用64位Delphi 12。
问题原因
直接克隆关联的子TClientDataSet在BeforePost事件中存在本质问题:
- 此时父数据集处于
dsEdit状态,子数据集的内部游标依赖父数据集的当前状态; CloneCursor操作后,克隆数据集的FParentDataSet仍指向处于编辑状态的父数据集,调用GetRecordCount时会执行FParentDataSet.UpdateCursorPos,而父数据集正处于Post的中间状态,内部结构不稳定,触发内存访问冲突;FieldCount正常是因为它仅读取数据集的结构元数据,不涉及游标与父数据集的交互。
解决方案
方案1:使用独立数据副本替代克隆
避免共享游标依赖,将子数据集的数据导出到内存流再加载到新数据集,创建完全独立的副本:
var Stream: TMemoryStream; begin Stream := TMemoryStream.Create; try ASourceDataSet.SaveToStream(Stream, dfBinary); Stream.Position := 0; ACopyDataSet.LoadFromStream(Stream, dfBinary); // 此时ACopyDataSet可正常访问RecordCount finally Stream.Free; end; end;
方案2:临时切换父数据集状态(谨慎使用)
若必须使用克隆,可临时将父数据集切换到dsBrowse状态,克隆完成后恢复原状态:
var ParentPrevState: TDataSetState; begin ParentPrevState := ASourceDataSet.FParentDataSet.State; try // 临时切换父数据集到浏览状态 ASourceDataSet.FParentDataSet.State := dsBrowse; if not ASourceDataSet.Active then ASourceDataSet.Open; ACopyDataSet.CloneCursor(ASourceDataSet, true, false); finally // 恢复父数据集原状态 ASourceDataSet.FParentDataSet.State := ParentPrevState; end; end;
注意:此操作可能干扰Post流程,需充分测试。
方案3:安装Delphi 12最新补丁
64位Delphi 12可能存在嵌套TClientDataSet的相关bug,安装官方最新Update补丁,确认是否为已知问题的修复范畴。
内容的提问来源于stack exchange,提问作者Jan Doggen
相关产品推荐
相关产品推荐

