压缩和发送TFDDataset数据后释放表格时的AV

时间:2017-03-23 15:08:14

标签: delphi memory-management stream firedac delphi-10.1-berlin

我在发布表单时有一个AV,当我压缩并将FireDAC数据集的数据发送到远程服务器时会出现。

这是我用来压缩TFDDataset数据的代码:

function CompressDataset(Dataset: TFDDataset): TMemoryStream;
var Data: TMemoryStream;
    Compress: TZCompressionStream;
begin
  Result := TMemoryStream.Create;   
  Data := TMemoryStream.Create;
  try
    Compress := TZCompressionStream.Create(Result);
    Dataset.SaveToStream(Data, TFDStorageFormat.sfBinary);
    Data.Position := 0;
    Compress.CopyFrom(Data, Data.Size);
  finally
    Data.Free;
    Compress.Free;
  end;
  Result.Position := 0;
end;

这是将压缩数据发送到远程调用(Datasnap)的代码。

procedure TfrmRentFacturacion_Facturar.btnSendDesgloseClick(Sender: TObject);
var Stream: TMemoryStream;
begin
  if qryFacturacion_Desglose.State = dsEdit then qryFacturacion_Desglose.Post;

  Stream := CompressDataset(qryFacturacion_Desglose);
  try
    spActualizaDesglose.ParamByName('AStream').AsStream := Stream;
    spActualizaDesglose.ExecProc;
  finally
    Stream.Free;
  end;
end;

此代码留下了一些不稳定的东西,很可能是TFDDataset qryFacturacion_Desglose,并在释放表单时引发AV。但我没有得到可能出错的东西。

PS:感谢@J ...建议检查调用堆栈我找到了问题的根源。这是调用堆栈:

:000000000040E735 TObject.Free + $15
:00000000007F1123 TParamObject.Destroy + $43
:000000000041A155 TInterfacedObject._Release + $55
:000007FEFF2211CE ; C:\Windows\system32\oleaut32.dll
:0000000000459DAB VarClearDeep + $1B
:0000000000459E6B @VarClear + $1B
:0000000000459E7D @VarClr + $D
:00000000004149F4 @VarClr + $14
:0000000000414ACC @FinalizeArray + $BC
:00000000004162F1 @DynArrayClear + $61
:0000000000414B58 @FinalizeArray + $148
:0000000000414985 @FinalizeRecord + $75
:000000000040E82E TObject.CleanupInstance + $4E
:000000000040E450 TObject.FreeInstance + $10
:000000000040F1C1 @ClassDestroy + $11
:000000000051ED43 TCollectionItem.Destroy + $43
:000000000040E738 TObject.Free + $18
:000000000051F40A TCollection.Clear + $5A
:000000000051F1CD TCollection.Destroy + $2D
:000000000084A858 TFDParams.Destroy + $88
:0000000000838FD8 FDFree + $18
:000000000084A8BB TFDParams.RemRef + $2B
:0000000000B8C907 TFDCustomCommand.Destroy + $57
:000000000040E738 TObject.Free + $18
:00000000005419F3 TComponent.DestroyComponents + $93
:000000000054117F TComponent.Destroy + $2F
:0000000000B92A66 TFDCustomTableAdapter.Destroy + $86
:0000000000B9BE02 TFDRdbmsDataSet.Destroy + $C2
:000000000040E738 TObject.Free + $18
:00000000005419F3 TComponent.DestroyComponents + $93
:000000000054117F TComponent.Destroy + $2F
:00000000006039C2 TControl.Destroy + $192
:000000000060AA91 TWinControl.Destroy + $1B1
:0000000000797273 TScrollingWinControl.Destroy + $73
:0000000000798EB7 TCustomForm.Destroy + $1E7
:000000000040E738 TObject.Free + $18
:00000000007A1389 TCustomForm.CMRelease + $9
:000000000040EE81 TObject.Dispatch + $41
:0000000000607D56 TControl.WndProc + $386
:000000000060EC07 TWinControl.WndProc + $8E7
:000000000079ADB0 TCustomForm.WndProc + $910
:000000000060DE4C TWinControl.MainWndProc + $2C
:0000000000545056 StdWndProc + $26
:00000000777D9BBD ; C:\Windows\system32\USER32.dll
:00000000777D98C2 ; C:\Windows\system32\USER32.dll
:00000000007A8E84 TApplication.ProcessMessage + $134
:00000000007A8EF8 TApplication.HandleMessage + $18
:00000000007A9364 TApplication.Run + $F4
Impuestos.Impuestos
:00000000776B59CD ; C:\Windows\system32\kernel32.dll
:00000000778EA561 ; ntdll.dll

当尝试释放执行对Datasnap Server的远程调用的spActualizaDesglose TFDStoredProc的参数AStream时,会发生AV。

我已经更改了呼叫,因此在执行远程呼叫后它不会释放原始数据流。

 procedure TfrmRentFacturacion_Facturar.btnSendDesgloseClick(Sender: TObject);
    var Stream: TMemoryStream;
    begin
      if qryFacturacion_Desglose.State = dsEdit then qryFacturacion_Desglose.Post;

      Stream := CompressDataset(qryFacturacion_Desglose);
      spActualizaDesglose.ParamByName('AStream').AsStream := Stream;
      spActualizaDesglose.ExecProc;
    end;

现在表单发布没有问题,但是这是正确的吗?,我没有内存泄漏?

谢谢。

1 个答案:

答案 0 :(得分:7)

来自the manual page

  

如果不是字符串/字节字符串/ BLOB数据类型之一,则设置AsStream属性会将DataType属性设置为ftStream。 分配的TStream对象将由此TFDParam拥有。要显式控制所有权,请使用SetStream方法。

强调我的。所以是的,将流分配给参数会给出该流的参数所有权,并且当它自身被释放时,它负责释放它(当数据集由拥有数据集组件的表单释放时由数据集完成)。 / p>

当你在这里释放流时:

Stream := CompressDataset(qryFacturacion_Desglose);
try
  spActualizaDesglose.ParamByName('AStream').AsStream := Stream;
  spActualizaDesglose.ExecProc;
finally
  Stream.Free;
end;

您正在销毁参数持有引用的对象,并且当参数对象尝试第二次释放它时它会引发AV。