Link to home
Start Free TrialLog in
Avatar of Scay7
Scay7Flag for South Africa

asked on

Windows 7, Delphi Code, Drawcolumn ERROR

Bit stummped here...

Got a program which works great on WIN XP but on certian Win7 PC it just crashes:

Madexception 3.0m Disassembler:

 
00723ef9       push    $72483c                ; System.@HandleFinally
00723efe       push    dword ptr fs:[eax]
00723f01       mov     fs:[eax], esp
00723f04 524   mov     eax, edi
00723f06       call    -$7d273 ($6a6c98)      ; DBGrids.TColumn.GetField
00723f0b     > mov     eax, [eax+$38]
00723f0e       mov     edx, $724858
00723f13       call    -$19b2f0 ($588c28)     ; DB.TDataSet.FieldByName
00723f18       lea     edx, [ebp-$1c]
00723f1b       mov     ecx, [eax]
00723f1d       call    dword ptr [ecx+$74]

Open in new window


Ive also attached the full report below, thanks any ideas PLZ...

Below is the entire procedure that is causing this problem with Win7
 bugreport.txt

Procedure dw(rect: trect; imgl : integer);
//var
//bitmap : TBitmap;
begin
form1.ImgList.Draw(form1.DBGrid.Canvas,Rect.Left+4,Rect.Top+3, imgl );
{
  bitmap := TBitmap.Create;
  try
    form1.ImgList.GetBitmap(imgl,bitmap);
    form1.DBGrid.Canvas.Draw(Rect.left+3,rect.Top+3,bitmap);
  finally
  bitmap.Free;
  end;}
end;

procedure TForm1.DBGridDrawColumnCell(Sender: TObject; const Rect: TRect;
  DataCol: Integer; Column: TColumn; State: TGridDrawState);
var
countdos : byte;
dt1,dt2,dt3 : string;
dt4,dt5,dt6 : integer;
begin

if Column.Field.Dataset.FieldbyName('dbcs').AsString = '4' then dbgrid.canvas.brush.color := clyellow;
If Column.Field.Dataset.FieldbyName('dbcs').AsString = '6' then dbgrid.canvas.brush.color := clmoneygreen;
If Column.Field.Dataset.FieldbyName('dbcs').AsString = '7' then dbgrid.canvas.brush.color := clskyblue;
If Column.Field.Dataset.FieldbyName('dbcs').AsString = '12' then dbgrid.canvas.brush.color := cllime;
If Column.Field.Dataset.FieldbyName('dbcs').AsString = '14' then dbgrid.canvas.brush.color := clfuchsia;

If Column.Field.Dataset.FieldbyName('dbcs').AsString = '1' then
begin
dbgrid.canvas.Font.Color := clcream;
dbgrid.canvas.brush.color := clred;
end;
If Column.Field.Dataset.FieldbyName('dbcs').AsString = '2' then
begin
dbgrid.canvas.Font.Color := clcream;
dbgrid.canvas.brush.color := clgreen;
end;
If Column.Field.Dataset.FieldbyName('dbcs').AsString = '3' then
begin
dbgrid.canvas.Font.Color := clcream;
dbgrid.canvas.brush.color := clblue;
end;
If Column.Field.Dataset.FieldbyName('dbcs').AsString = '5' then
begin
dbgrid.canvas.Font.Color := clcream;
dbgrid.canvas.brush.color := clmaroon;
end;
If Column.Field.Dataset.FieldbyName('dbcs').AsString = '8' then
begin
dbgrid.canvas.Font.Color := clcream;
dbgrid.canvas.brush.color := clolive;
end;
If Column.Field.Dataset.FieldbyName('dbcs').AsString = '9' then
begin
dbgrid.canvas.Font.Color := clcream;
dbgrid.canvas.brush.color := clnavy;
end;
If Column.Field.Dataset.FieldbyName('dbcs').AsString = '10' then
begin
dbgrid.canvas.Font.Color := clcream;
dbgrid.canvas.brush.color := clpurple;
end;
If Column.Field.Dataset.FieldbyName('dbcs').AsString = '11' then
begin
dbgrid.canvas.Font.Color := clcream;
dbgrid.canvas.brush.color := clgray;
end;
If Column.Field.Dataset.FieldbyName('dbcs').AsString = '13' then
begin
dbgrid.canvas.Font.Color := clcream;
dbgrid.canvas.brush.color := clteal;
end;

dbgrid.DefaultDrawColumnCell(rect,DataCol,Column,State);

if Column.Field.Dataset.FieldbyName('dbrag').AsString = 'Normal' then
begin
countdos := 0;
if Column.Field.Dataset.FieldbyName('oasts').AsString = '' then
countdos := countdos + 1;
if Column.Field.Dataset.FieldbyName('cdate').AsString = '' then
countdos := countdos + 1;
if Column.Field.Dataset.FieldbyName('saoco').AsString = '' then
countdos := countdos + 1;

if countdos = 1 then
If Column.Field.Dataset.FieldbyName('dbcs').AsString <> '2' then
if column.FieldName = 'ico' then dw(rect,1);

if countdos = 2 then
If Column.Field.Dataset.FieldbyName('dbcs').AsString <> '2' then
if column.FieldName = 'ico' then dw(rect,2);

if countdos = 3 then
If Column.Field.Dataset.FieldbyName('dbcs').AsString <> '2' then
if column.FieldName = 'ico' then dw(rect,3);

if countdos <> 0 then
If Column.Field.Dataset.FieldbyName('dbcs').AsString = '2' then
if column.FieldName = 'ico' then dw(rect,5);

if countdos = 0 then
if column.FieldName = 'ico' then dw(rect,4);

If Column.Field.Dataset.FieldbyName('dbcs').AsString <> '2' then
if length(Column.Field.Dataset.FieldbyName('instrec').AsString) = 10 then
begin
dt1 := copy(Column.Field.Dataset.FieldbyName('instrec').AsString,9,2);
dt2 := formatdatetime('dd',now);
dt4 := strtoint(dt2) - strtoint(dt1);
if (dt4 = 3) or (dt4 = 4) or (dt4 = 5) or (dt4 = 6) then
if Column.Field.Dataset.FieldbyName('nstc').AsString = '' then
if column.FieldName = 'ico' then dw(rect,6);
if (dt4 = 7) or (dt4 = 8) or (dt4 = 9) or (dt4 >= 10) then
if Column.Field.Dataset.FieldbyName('oastc').AsString = '' then
if column.FieldName = 'ico' then dw(rect,7);
end;

if Column.Field.Dataset.FieldbyName('dnfee').AsString = '1' then                        //do not fee
if column.FieldName = 'ico' then dw(rect,0);

end;

if Column.Field.Dataset.FieldbyName('dbrag').AsString = 'DFC' then
begin
if column.FieldName = 'ico' then dw(rect,9);
end;
if Column.Field.Dataset.FieldbyName('dbrag').AsString = 'Resc' then
begin
if column.FieldName = 'ico' then dw(rect,8);
end;

end;

Open in new window

Avatar of Emmanuel PASQUIER
Emmanuel PASQUIER
Flag of France image

This is the error
Access violation at address 00723F0B in module 'sbsanew2.exe'. Read of address 00000038.
so by looking at ASM, it is obvious that EAX is nil (so the object on which you call the method is nil)
And that method being TColumn.GetField, I would then say that Column=nil in that DBGridDrawColumnCell function.

Please check that to be sure :

begin
 If Not Assigned(Column) Then
  begin
   ShowMessage('Column is NIL!');
   Exit;
  end;
 if Column...


oh, and by the way : you don't like local variables ?
like :

Var
 DataSet:TDataSet;
 DBCSField:TField;
 DBCSValue:Integer;
 Canvas:TCanvas;
 Brush:TBrush;
begin
 DataSet:=Column.Field.Dataset;
 DBCSField:=DataSet.FieldByName('dbcs');
 DBCSValue:=DBCSField.AsInteger;
 Canvas:=dbgrid.canvas;
 Brush:=Canvas.Brush;
 Case DBCSValue of
   4:Brush.Color := clyellow;
   6:Brush.Color := clmoneygreen;
   7:Brush.Color := clskyblue;
  12:Brush.Color := cllime;
  14:Brush.Color := clfuchsia;
etc....

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of Emmanuel PASQUIER
Emmanuel PASQUIER
Flag of France image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Scay7

ASKER

@epasquier
The field that is being used here is mostly strings, and will not stay as [1..100] but will start to vary from [1..100..a..z..a1..z100] etc etc
Also that snippet didnt stop the access violation at all noir did it bring up that a column was null (there are null fileds in this column)

@ewangoya
Very nice set out there also just wana add that im a amature delphi code designer, i learn by example then morph the examples to suit my needs, ive never gone on any delphi courses and have self taught all delphi code by my self...
imageindex has no relation to countdos so you cant assign one to the other... countdos is just a check to count the columns for blanks and display the right ico to the column
Doesnt mean to say i havent learnt anything from the above sort that youve shown me infact, from that ive learnt something new, and would like to thank you on that part...

I just want to add something here which might help, or so, i want to say sorry for not adding this earlier...

On my creating of the forum i connect to a mssql 2008 r2 server and load the following:
 
SELECT *  ,ISNULL(ndiary, '') + ISNULL(rec13, '') + ISNULL(dfc14, '') AS adiary FROM [tabledata]
ORDER BY fileref ASC

Open in new window


with this i load my dbgrid with custom columns
[0]ico [1]fileref [2]adiary

Now the above works VERY well on a WinXP machine, BUT on some Win7 it crashes with the above mentioned crash report.... soo i did some testing:

1. It doesnt crash on any winxp machine
2. if i remove the columns colours and leave in the ico's it works on all
3. if i remove the ico's and colours it works on all
4. if i remove the ico's and put in the colours it crashes on win7
5. if i replace the field [2]adiary with a non grouped one
eg:
SELECT * FROM [tabledata]
ORDER BY fileref ASC

and use a standard column [ndiary] in the db without joining 3 columns togeter to form *adiary*
with all the colours and ico's...  IT WORKS ON ALL OS'S

So from the above deduction win7 on some machine dont like the joining of 3 or more columns while trying to paint over them in the dbgrid....
Avatar of Scay7

ASKER

K i figured it out...

For some reason unknown to me:

It doesnt like this:
"Column.Field.Dataset.FieldbyName"
so i replaced with:
"query1.fieldvalues['dbcs']"

and added this to the top of the procedure:

if query1.fieldvalues['dbcs'] <> NULL then
dbcs := trim(query1.FieldValues['dbcs'])
else dbcs := '';
if query1.fieldvalues['oastc'] <> NULL then
oastc := trim(query1.FieldValues['oastc'])
else oastc := '';
if query1.fieldvalues['cdate'] <> NULL then
cdate := trim(query1.FieldValues['cdate'])
else cdate := '';
if query1.fieldvalues['saoco'] <> NULL then
saoco := trim(query1.FieldValues['saoco'])
else saoco := '';
if query1.fieldvalues['instrec'] <> NULL then
instrec := trim(query1.FieldValues['instrec'])
else instrec := '';
if query1.fieldvalues['nstc'] <> NULL then
nstc := trim(query1.FieldValues['nstc'])
else nstc := '';
if query1.fieldvalues['dbrag'] <> NULL then
dbrag := trim(query1.FieldValues['dbrag'])
else dbrag := '';

Open in new window


There by replace all the "Column.Field.Dataset.FieldbyName" and assigning variables [dbcs, oastc, cdate, saoco, instrec, nstc, dbrag]

With the above in place no violation access comes up anywhere from any OS or any Machine

If anyone can explain to me why:
1. Column.Field.Dataset.FieldbyName was working before
2. query1.fieldvalues[] is working now

The points are yours...
By looking better at ASM code, it would seem that it is not the Column that is not assigned, but the Column.Field.DataSet (the error is just after calling GetField, and before calling FieldByName)
I have checked, the adress of the inner field FDataSet of TField is $38, so that confirms it.

so just for testing purpose, add this :
If Not Assigned(Column.Field.DataSet)
 Then ShowMessage('DataSet is NIL in Field '+Column.Field.FieldName);

Now, the 100.000 points question : Why the field does not have a valid FDataSet ?

Scay7

According to your code, you are using countdos to determine the imagindex of the bitmap.

if countdos = 1 then
  If Column.Field.Dataset.FieldbyName('dbcs').AsString <> '2' then
    if column.FieldName = 'ico' then dw(rect,1)

I just cleaned it up a bit but its the same result

You need to assign dataset to a variable instead of referencing it using Column.Field.Dataset

  Grid := Sender as TDBGrid;
  Dataset := Grid.DataSource.DataSet;
  //check here
  if not Assigned(Dataset) then
    Exit;
 
well, if he knows that this DBGrid is used with query1, then why not use query1 directly ? I know, this is a question of re-usability with other queries.. But I doubt this code will be reused with that many projects.

Until its used, you never know LOL
you have made the first step toward re-usability by rewriting the code in a readable fashion, as I too asked for, and the second step with  Grid := Sender as TDBGrid; Dataset := Grid.DataSource.DataSet;
But I'm afraid there would be another 100 steps before that code is reusable for a more global use. It is far too specific, and not all code has to be written for global use. It tends to complexify the code readability, and this one is enough already.
Now that we have covered both point of view, Scay7 will be a happy learner : There is no best solution for all case, only best solution for one case, and you are the only one that can decide.

epasquire

This the reason why you are needed in EE Delphi zone, We can agree and disagree, hold discussions and give many points of view and I always get to learn stuff from you.

Only a handful of people ever do this including Geert, Aikimark, Ferrucio, just a handful
I hope other "experts" can learn from you
Avatar of Scay7

ASKER

@ewangoya:
if countdos <> 0 then
If Column.Field.Dataset.FieldbyName('dbcs').AsString = '2' then
if column.FieldName = 'ico' then dw(rect,5);

if countdos = 0 then
if column.FieldName = 'ico' then dw(rect,4);

Open in new window


If you look further downt the countdos has no relation to the imageindex :P im just using countdos to count up how many columns are emtpy/0/<> then assign a image to it using that referrence
I do how ever see the relation you are talking about, but further down it wouldnt beable to link the number to the imageindex

@epasquier:
As i said before i take examples from Experts whom share thier wealth of knowledge then apply my own ideas to that and morph the code to suit my needs. Before this procedure i neva knew how to write a bitmap to a dbgrid, but from here i now know alot more about the dbgrid, also applying more ideas which i would have never thought of before...

I would like to take a step back here and ask the question again.

Why before adding of the icons did Column.Field.Dataset.FieldbyName work and only after starting to apply the icons to the grid did i have to change the entire reading of it to query1.fieldvalues for it to start working again, this i cant wrap my head around... :P

Also would like to add that im grateful for the help and ideas that you both have posted here, because of the cleaning up of the code and the error checking idea i have learnt some more about delphi which ill apply to many more of my future projects :P hehe

Avatar of Scay7

ASKER

I undestand now what happened, but im still going to assign points as ive gotten good reponses within you answers for other issues