various assertions

Discussion of open issues, suggestions and bugs regarding SDAC (SQL Server Data Access Components) for Delphi, C++Builder, Lazarus (and FPC)
Post Reply
Ludek
Posts: 296
Joined: Thu 12 Oct 2006 09:34

various assertions

Post by Ludek » Wed 12 Aug 2009 18:53

HI, I created following application with newest sdac and d2009:
PAS:

Code: Select all

unit SDACTestU;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, DB, Grids, DBGrids, MemDS, VirtualTable, DBAccess, MSAccess, ExtCtrls;

type
  TForm1 = class(TForm)
    DBGrid1: TDBGrid;
    DataSource1: TDataSource;
    EinkaufQ: TMSQuery;
    MSConnection1: TMSConnection;
    EinkaufQbez: TStringField;
    EinkaufQStaffelPreisVorhanden: TSmallintField;
    Timer1: TTimer;

    procedure DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
      DataCol: Integer; Column: TColumn; State: TGridDrawState);
    procedure FormCreate(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
  DataCol: Integer; Column: TColumn; State: TGridDrawState);
begin
  if (Column.Field = EinkaufQStaffelPreisVorhanden) and EinkaufQStaffelPreisVorhanden.IsNull then begin
    if not (EinkaufQ.State in [dsEdit, dsInsert]) then
      EinkaufQ.Edit;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  EinkaufQ.Open;
  EinkaufQ.DisableControls;
  EinkaufQ.Append;
  EinkaufQBez.AsString := '423423423';
  EinkaufQ.Post;
  EinkaufQ.Append;
  EinkaufQBez.AsString := '423423423';
  EinkaufQ.Post;
  EinkaufQ.EnableControls;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  EinkaufQ.Prior;
  EinkaufQ.Next;
end;

end.
dfm:

Code: Select all

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 333
  ClientWidth = 676
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  OnCreate = FormCreate
  PixelsPerInch = 96
  TextHeight = 13
  object DBGrid1: TDBGrid
    Left = 80
    Top = 40
    Width = 409
    Height = 120
    DataSource = DataSource1
    TabOrder = 0
    TitleFont.Charset = DEFAULT_CHARSET
    TitleFont.Color = clWindowText
    TitleFont.Height = -11
    TitleFont.Name = 'Tahoma'
    TitleFont.Style = []
    OnDrawColumnCell = DBGrid1DrawColumnCell
    Columns = 
  end
  object DataSource1: TDataSource
    DataSet = EinkaufQ
    Left = 256
    Top = 176
  end
  object EinkaufQ: TMSQuery
    Connection = MSConnection1
    SQL.Strings = (
      
        'select cast(null as varchar(255)) as bez, cast(null as smallint)' +
        ' as StaffelPreisVorhanden')
    CachedUpdates = True
    Left = 320
    Top = 240
    object EinkaufQbez: TStringField
      FieldName = 'bez'
      FixedChar = True
      Size = 80
    end
    object EinkaufQStaffelPreisVorhanden: TSmallintField
      FieldName = 'StaffelPreisVorhanden'
      ReadOnly = True
    end
  end
  object MSConnection1: TMSConnection
    Database = 'doma'
    Username = 'sa'
    Server = 'DELO'
    Connected = True
    Left = 256
    Top = 240
  end
  object Timer1: TTimer
    OnTimer = Timer1Timer
    Left = 416
    Top = 216
  end
end
this program corrupts the string column and then fails with an assertion error AddRefStrFailed.
It's probably because of the editing in the drawcolumncell event - but i think, it should not fail so late and with such really hard-to-identify exception (i spent hours identifying it and creating this example :(, i have also seen disposebuf failed, some FastMM warnings, etc. )
please, could you provide a correction? or, if the editing in drawcolumncell event is not possible, make some human-readable error message directly at the ".edit" command?
thanks much, ludek.

Challenger
Devart Team
Posts: 925
Joined: Thu 17 Nov 2005 10:53

Post by Challenger » Tue 18 Aug 2009 12:56

We have reproduced the problem and now we are investigating it.

Dimon
Devart Team
Posts: 2885
Joined: Mon 05 Mar 2007 16:32

Post by Dimon » Tue 25 Aug 2009 13:05

We have investigated this problem.
This problem in connected with calling the TDataSet.Edit method in the OnDrawColumnCell event handler. In this case edition operation is executed for drawn record, but not for the current record.
The problem is that TDataSet has limitation and doesn't allow to check this situation to avoid the problem. Therefore the Cancel operation is called for another record then the Edit operation was called.

Ludek
Posts: 296
Joined: Thu 12 Oct 2006 09:34

Post by Ludek » Wed 26 Aug 2009 06:42

So it means for me, that such errors are unavoidable and always when I see such addrefstr/disposebuf/corrupted memory errors, I have to analyze my drawcolumncell events? :(
What is the REAL problem: the editing in drawcolumncell event or calling cancel on an other row than edit?
I use my own successor of the TDBGrid class, so i can change the virtual method Drawcolumncell. i can imageine setting the whole tmsdataset class readonly to false for that time. Or, do you have some better idea how to eliminate such memory corruptions?

Dimon
Devart Team
Posts: 2885
Joined: Mon 05 Mar 2007 16:32

Post by Dimon » Tue 01 Sep 2009 14:04

If you override the DrawColumnCell method then you should keep the current RecordNo value and call the Edit method only for this record.

Ludek
Posts: 296
Joined: Thu 12 Oct 2006 09:34

Post by Ludek » Wed 02 Sep 2009 06:35

You don't understand me. I'm looking for anything, that saves me from such memory corruptions (also in future code, in some year, when i again have no idea about this thing). so i programmed following:

Code: Select all

procedure TMyDBGrid.DrawColumnCell(const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
begin
  if (datasource.DataSet is TCustomMSDataset) and (Datasource.State = dsBrowse) and not (Datasource.DataSet as TCustomMSDataset).ReadOnly then begin
    (Datasource.DataSet as TCustomMSDataset).ReadOnly := true;
    inherited DrawColumnCell(Rect2, DataCol, Column, State);
    (Datasource.DataSet as TCustomMSDataset).ReadOnly := false;
  end else begin
    inherited DrawColumnCell(Rect2, DataCol, Column, State);
  end;
end;
this generates an exception directly at the .edit in ondrawcolumncell. but i'm looking for some better solution.

Dimon
Devart Team
Posts: 2885
Joined: Mon 05 Mar 2007 16:32

Post by Dimon » Wed 02 Sep 2009 07:25

Unfortunally, we can't suggest any universal solution this problem, because TDataSet doesn't allow to check such situations to avoid the problem.

Post Reply