You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多次调用FireDac FDQuery触发访问违例问题求助

解决FDQuery多次调用触发访问违例的问题

问题根源分析

你的访问违例大概率来自以下几个未正确处理的资源管理问题:

1. BlobStream未释放

在ConvertData中,通过queryObj.CreateBlobStream创建的commentsBlob、dataBlob、attrBlob没有被手动释放。这些流对象由FDQuery实例持有引用,若不释放,会导致内存泄漏,且后续循环中创建新FDQuery时,旧的无效引用会干扰新对象的操作,引发AV。

2. 提前Exit导致Query未关闭

在ConvertData的查询执行块中,当Recordcount=0或捕获到异常时直接调用Exit,此时FDQuery仍处于打开状态。后续在外部循环中释放FDQuery时,关闭未完成的查询会触发对象状态异常。

3. 异常处理的覆盖问题

ConvertData内部的异常捕获仅弹出提示就Exit,没有确保资源被清理,也没有将异常传递到外部,导致外部循环无法感知内部错误,继续执行后引发更严重的AV。

修复方案

针对上述问题,修改ConvertData的关键代码部分:

修复BlobStream的释放

在复制Blob数据后,立即释放对应的BlobStream:

// Copy blob data to memory streams
commentsBlob := queryObj.CreateBlobStream(queryObj.FieldByName('col4'), bmRead);
if assigned(commentsBlob) then
try
  commentsStream.CopyFrom(commentsBlob, queryObj.FieldByName('col1').AsInteger);
finally
  commentsBlob.Free; // 释放BlobStream
end;

dataBlob := queryObj.CreateBlobStream(queryObj.FieldByName('col5'), bmRead);
if assigned(dataBlob) then
try
  dataStream.CopyFrom(dataBlob, queryObj.FieldByName('col2').AsInteger);
finally
  dataBlob.Free;
end;

attrBlob := queryObj.CreateBlobStream(queryObj.FieldByName('col6'), bmRead);
if assigned(attrBlob) then
try
  attrStream.CopyFrom(attrBlob, queryObj.FieldByName('col3').AsInteger);
finally
  attrBlob.Free;
end;

确保Query始终被关闭

调整ConvertData中查询执行的逻辑,无论是否有记录或异常,都确保Query被关闭:

with queryObj do
begin
  SQL.Text := 'SELECT col1, col2, col3, col4, col5, col6 FROM ' + tableName + ' WHERE dateColumn=:param1 AND siteColumn=:param2';
  ParamByName('param1').AsDate := dateParam;
  ParamByName('param2').AsInteger := siteID;

  try
    Open;
    if (Recordcount = 0) then
      Exit; // 提前退出前确保后续会执行Close
  except
    on E: Exception do
    begin
      ShowMessage('Error executing query: ' + E.Message);
      Exit;
    end;
  end;
end;

// 用try-finally包裹后续处理,确保Query最终被关闭
try
  // 原有的流处理、数组转换等代码放这里
finally
  queryObj.Close;
end;

优化异常处理

将ConvertData内部的异常改为重新抛出,让外部循环能够捕获并终止,避免无效的后续执行:

except
  on E: Exception do
  begin
    ShowMessage('Error in ConvertData: ' + E.Message);
    raise; // 重新抛出异常,让外部循环处理
  end;
end;

额外建议

  • 避免在循环中频繁创建和释放FDQuery,可以复用一个FDQuery实例,每次循环前清空SQL、参数并重新执行,减少对象创建销毁的开销和潜在的资源问题。
  • 所有资源操作都用try-finally包裹,确保任何情况下资源都能被正确释放。

内容的提问来源于stack exchange,提问作者Surox

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 17:44:54