Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AV when releasing a Form after compressing and sending a TFDDataset's Data

I have an AV when releasing a Form, it appears when I compress and send a FireDAC Dataset's data to a remote server.

This is the code I use to compress the TFDDataset's Data:

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;

And this is the code to send that compressed data to the remote call (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;

This code leaves something unstable, most probably the TFDDataset qryFacturacion_Desglose, and raises an AV when releasing the form. But I don't get what could possible be wrong.

PS: Thanks to @J... suggestion to check the Call Stack I have found the source of the problem. This is the Call Stack :

: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

The AV occurs when trying to free the Parameter AStream of the spActualizaDesglose TFDStoredProc that executes the remote call to the Datasnap Server.

I have changed the call, so it doesn't frees the original data Stream after executing the remote call.

 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;

Now the form is released without problems, but is this correct ?, won't I have a memory leak ?.

Thank you.

like image 299
Marc Guillot Avatar asked Mar 23 '17 15:03

Marc Guillot


1 Answers

From 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.

Emphasis mine. So yes, assigning the stream to the parameter gives the parameter ownership of that stream and it becomes responsible for freeing it when it is itself freed (which is done by the dataset when it is freed by the form which owns the dataset component).

When you free the stream here :

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

you are destroying the object that the parameter is holding a reference to and it raises an AV when the parameter object tries to free it a second time.

like image 107
J... Avatar answered Oct 15 '22 06:10

J...