压缩和发送 TFDDataset 的数据后释放表单时的 AV
AV when releasing a Form after compressing and sending a TFDDataset's Data
我在发布表单时有一个 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 +
:00000000007F1123 TParamObject.Destroy +
:000000000041A155 TInterfacedObject._Release +
:000007FEFF2211CE ; C:\Windows\system32\oleaut32.dll
:0000000000459DAB VarClearDeep + B
:0000000000459E6B @VarClear + B
:0000000000459E7D @VarClr + $D
:00000000004149F4 @VarClr +
:0000000000414ACC @FinalizeArray + $BC
:00000000004162F1 @DynArrayClear +
:0000000000414B58 @FinalizeArray + 8
:0000000000414985 @FinalizeRecord +
:000000000040E82E TObject.CleanupInstance + E
:000000000040E450 TObject.FreeInstance +
:000000000040F1C1 @ClassDestroy +
:000000000051ED43 TCollectionItem.Destroy +
:000000000040E738 TObject.Free +
:000000000051F40A TCollection.Clear + A
:000000000051F1CD TCollection.Destroy + D
:000000000084A858 TFDParams.Destroy +
:0000000000838FD8 FDFree +
:000000000084A8BB TFDParams.RemRef + B
:0000000000B8C907 TFDCustomCommand.Destroy +
:000000000040E738 TObject.Free +
:00000000005419F3 TComponent.DestroyComponents +
:000000000054117F TComponent.Destroy + F
:0000000000B92A66 TFDCustomTableAdapter.Destroy +
:0000000000B9BE02 TFDRdbmsDataSet.Destroy + $C2
:000000000040E738 TObject.Free +
:00000000005419F3 TComponent.DestroyComponents +
:000000000054117F TComponent.Destroy + F
:00000000006039C2 TControl.Destroy + 2
:000000000060AA91 TWinControl.Destroy + B1
:0000000000797273 TScrollingWinControl.Destroy +
:0000000000798EB7 TCustomForm.Destroy + E7
:000000000040E738 TObject.Free +
:00000000007A1389 TCustomForm.CMRelease +
:000000000040EE81 TObject.Dispatch +
:0000000000607D56 TControl.WndProc + 6
:000000000060EC07 TWinControl.WndProc + E7
:000000000079ADB0 TCustomForm.WndProc + 0
:000000000060DE4C TWinControl.MainWndProc + C
:0000000000545056 StdWndProc +
:00000000777D9BBD ; C:\Windows\system32\USER32.dll
:00000000777D98C2 ; C:\Windows\system32\USER32.dll
:00000000007A8E84 TApplication.ProcessMessage + 4
:00000000007A8EF8 TApplication.HandleMessage +
:00000000007A9364 TApplication.Run + $F4
Impuestos.Impuestos
:00000000776B59CD ; C:\Windows\system32\kernel32.dll
:00000000778EA561 ; ntdll.dll
尝试释放执行对 Datasnap 服务器的远程调用的 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;
现在表单发布没有问题,但是这是正确的吗?,我不会有内存泄漏吗?。
谢谢。
来自 the manual page :
Setting the AsStream property sets the DataType property to ftStream if it is not one of the character string / byte string / BLOB data types. The assigned TStream object will be owned by this TFDParam. To explicitly control the ownership, use SetStream method.
强调我的。所以是的,将流分配给参数会赋予该流的参数所有权,并且当它本身被释放时它负责释放它(当它被拥有数据集组件的表单释放时由数据集完成)。
当您在这里释放流时:
Stream := CompressDataset(qryFacturacion_Desglose);
try
spActualizaDesglose.ParamByName('AStream').AsStream := Stream;
spActualizaDesglose.ExecProc;
finally
Stream.Free;
end;
您正在破坏参数持有引用的对象,并且当参数对象再次尝试释放它时它会引发 AV。
我在发布表单时有一个 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 +
:00000000007F1123 TParamObject.Destroy +
:000000000041A155 TInterfacedObject._Release +
:000007FEFF2211CE ; C:\Windows\system32\oleaut32.dll
:0000000000459DAB VarClearDeep + B
:0000000000459E6B @VarClear + B
:0000000000459E7D @VarClr + $D
:00000000004149F4 @VarClr +
:0000000000414ACC @FinalizeArray + $BC
:00000000004162F1 @DynArrayClear +
:0000000000414B58 @FinalizeArray + 8
:0000000000414985 @FinalizeRecord +
:000000000040E82E TObject.CleanupInstance + E
:000000000040E450 TObject.FreeInstance +
:000000000040F1C1 @ClassDestroy +
:000000000051ED43 TCollectionItem.Destroy +
:000000000040E738 TObject.Free +
:000000000051F40A TCollection.Clear + A
:000000000051F1CD TCollection.Destroy + D
:000000000084A858 TFDParams.Destroy +
:0000000000838FD8 FDFree +
:000000000084A8BB TFDParams.RemRef + B
:0000000000B8C907 TFDCustomCommand.Destroy +
:000000000040E738 TObject.Free +
:00000000005419F3 TComponent.DestroyComponents +
:000000000054117F TComponent.Destroy + F
:0000000000B92A66 TFDCustomTableAdapter.Destroy +
:0000000000B9BE02 TFDRdbmsDataSet.Destroy + $C2
:000000000040E738 TObject.Free +
:00000000005419F3 TComponent.DestroyComponents +
:000000000054117F TComponent.Destroy + F
:00000000006039C2 TControl.Destroy + 2
:000000000060AA91 TWinControl.Destroy + B1
:0000000000797273 TScrollingWinControl.Destroy +
:0000000000798EB7 TCustomForm.Destroy + E7
:000000000040E738 TObject.Free +
:00000000007A1389 TCustomForm.CMRelease +
:000000000040EE81 TObject.Dispatch +
:0000000000607D56 TControl.WndProc + 6
:000000000060EC07 TWinControl.WndProc + E7
:000000000079ADB0 TCustomForm.WndProc + 0
:000000000060DE4C TWinControl.MainWndProc + C
:0000000000545056 StdWndProc +
:00000000777D9BBD ; C:\Windows\system32\USER32.dll
:00000000777D98C2 ; C:\Windows\system32\USER32.dll
:00000000007A8E84 TApplication.ProcessMessage + 4
:00000000007A8EF8 TApplication.HandleMessage +
:00000000007A9364 TApplication.Run + $F4
Impuestos.Impuestos
:00000000776B59CD ; C:\Windows\system32\kernel32.dll
:00000000778EA561 ; ntdll.dll
尝试释放执行对 Datasnap 服务器的远程调用的 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;
现在表单发布没有问题,但是这是正确的吗?,我不会有内存泄漏吗?。
谢谢。
来自 the manual page :
Setting the AsStream property sets the DataType property to ftStream if it is not one of the character string / byte string / BLOB data types. The assigned TStream object will be owned by this TFDParam. To explicitly control the ownership, use SetStream method.
强调我的。所以是的,将流分配给参数会赋予该流的参数所有权,并且当它本身被释放时它负责释放它(当它被拥有数据集组件的表单释放时由数据集完成)。
当您在这里释放流时:
Stream := CompressDataset(qryFacturacion_Desglose);
try
spActualizaDesglose.ParamByName('AStream').AsStream := Stream;
spActualizaDesglose.ExecProc;
finally
Stream.Free;
end;
您正在破坏参数持有引用的对象,并且当参数对象再次尝试释放它时它会引发 AV。