Index was out of range on an ADOQuery - Very Random - delphi

I am getting Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index randomly emailed through to me on my website. I cannot reproduce this error either by force or general testing and it is somewhat confusing.
From what I can see in the stack trace, it happens randomly when opening a dataset and trying to get a value. The stack trace showed I got the error when calling this function:
function TDB.FGetLastInsertID: Integer;
const
selSQL = 'select scope_identity() as LastID';
var
selCursor: TDataSet;
begin
selCursor := Cursor(selSQL); //Returns a DataSet from a TADOQuery
try
Result := selCursor.FieldByName('LASTID').AsInteger;
finally
selCursor.Close;
selCursor.Free;
end;
end;
As I said, I cannot get this to reproduce the error and it seems to happen randomly on any query I run. I have tried closing the connection, setting it to inactive ect to reproduce the error but cannot.
Has anyone got any ideas?
EDIT: It seems its the close causing issues after examining the stack trace more closely:
Stack Trace: at
System.Collections.ArrayList.get_Item(Int32 index) at
Borland.Vcl.TList.Delete(Int32 Index) at Borland.Vcl.TList.Remove(Object Item) at
Borland.Vcl.TDBBufferList.FreeHGlobal(IntPtr Ptr) at
Borland.Vcl.TCustomADODataSet.FreeRecordBuffer(IntPtr& Buffer) at
Borland.Vcl.TDataSet.SetBufListSize(Int32 Value) at
Borland.Vcl.TDataSet.CloseCursor() at
Borland.Vcl.TDataSet.SetActive(Boolean Value) at Borland.Vcl.TDataSet.Close()
EDIT2: I have put a check in the code to make sure the selCursor.Active before selCursor.Close. The stack trace suggests there is nothing to close.

What happens if the Cursor returns an empty Dataset ?
You try to Access the selCursor.FieldByName ... without a record, I think.

Very odd and I still don't know why but it seems that randomly the Cursor is already closed. It seems that adding:
if (selCursor.Active) then
selCursor.Close;
has sorted the issue ...
Thanks for the answers and time spent trying to help.

selCursor := Cursor(selSQL); //Returns a DataSet from a TADOQuery
Maybe problem is here, FieldByName is in exception handler so you have error on next line. You have to check if Cursor() is ok.

Related

Why this code do not work in second time click button

I write this code for button click event . the first time I click the button every thing work correctly but when click for the second time on button it raise an error. whats the problem?
procedure TfrmMain.Button1Click(Sender: TObject);
var
B : Boolean;
begin
DM.tblTemp.DisableControls;
B:= DM.tblTemp.Locate('FoodName', DM.tblAsli.FieldByName('FoodName').AsString,[]) ;
if B then
begin
DM.tblTemp.Edit;
DM.tblTemp.FieldByName('Number').AsInteger:= DM.tblTemp.FieldByName('Number').AsInteger + 1;
DM.tblTemp.Post;
end
else
begin
DM.tblTemp.insert;
DM.tblTemp.FieldByName('FoodName').AsString := DM.tblAsli.FieldByName('FoodName').AsString;
DM.tblTemp.FieldByName('UnitPrice').AsInteger := DM.tblAsli.FieldByName('FoodPrice').AsInteger;
DM.tblTemp.FieldByName('Number').AsInteger := 1;
DM.tblTemp.Post;
end;
TotalPrice:= TotalPrice + DM.tblTemp.FieldByName('TotalPrice').AsInteger;
DM.tblTemp.EnableControls;
end;
the Error is
Row cannot be located for updating. Some values may have been changed
since it was last read
DM is data madual
tmbTbl is ADOTable
It is a pity you haven't said which DBMS you are using (e.g. Sql Server or MS Access,
nor told us a full list of the table's column types and indexes, if any.
The most likely answer to your q is that the variable B is set to False the first time
because the call to tblTemp.Locate fails to locate the Food name in question, so the Insert branch executes and the food's data is
added to the table but the second time the Edit branch executes and the error occurs,
though you have not said where, exactly. My guess would be on the call to .Post, because
the error message is one the ADO layer, which sits between the DBMS provider and your app,
emits when it attempts to post a change to the table but cannot identify which table row to update.
As I mentioned in a comment, the fix to this is usually at add a primary key index to the table, and I gather from your latest comment that this has succeeded for you. For the record and benefit of future readers it would be helpful if you could confirm which DBMS you are using and which Ole Driver you are using in your connection string.
Fwiw, I've tested your code using a table on MS Sql Server and MS Access and do not get the
error with either database.
Btw there is an obvious q, "How is it that tblTemp.Locate succeeds, but ADO is unable to
identify the correct record to post the update?" The answer is that tblTemp.Locate works in
a different way than identifying the relevant row to post the update.

Strange behavior using TPointSeries.Clear

(Delphi 6 with TChart, Win XP)
I'm getting erratic behavior trying to clear the points from a point series, and of course, this code used to work.
Basically, this part of my program generates 5 data points and plots them. When I try to clear them using OSC_Series.Clear I get a "List index out of bounds [0]" error.
I checked to make sure there was nothing odd about the values I was plotting. All is good there. Then I tried different things to try to isolate and work around the problem.
Here's some code.
type
TksGraph_DataFrm = class(TForm)
.
.
.
private
OSC_Series: TPointSeries
public
end;
procedure TksGraph_DataFrm.cat7AnalysisInitialize(var P:TTest_Project);
begin
// Do a bunch of stuff.
// Set up the analysis data points series.
OSC_Series:=TPointSeries.Create(self);
AnalysisChart.AddSeries(OSC_Series);
with OSC_Series do
begin
Title:='';
HorizAxis:=aBothHorizAxis;
VertAxis:=aBothVertAxis;
SeriesColor:=clRed;
Pointer.Brush.Color:=clYellow;
Pointer.HorizSize:=4;
Pointer.VertSize:=4;
Pointer.Style:=psRectangle;
Pointer.Visible:=true;
LinePen.Color:=clBlack;
LinePen.Width:=1;
Linepen.Visible:=true;
ShowInLegend:=false;
XValues.Order:=LoNone;
end;
end;
procedure TksGraph_DataFrm.cat7AnalysisRefresh(var P:TTest_Project);
var X,Y:single;
begin
X:= some value
Y:= some value
// Plot the result.
OSC_Series.AddXY(X,Y);
showmessage(
'Count = '+inttostr(OSC_Series.Count)+#13+
'X = '+FloatToStr(X)+#13+
'Y = '+FloatToStr(Y)+#13+
'Plot-X = '+FloatToStr(OSC_Series.XValue[OSC_Series.Count-1])+#13+
'Plot-Y = '+FloatToStr(OSC_Series.YValue[OSC_Series.Count-1]));
end;
Here is the routine I to use to reset the series. I'm including code that does and does not work.
procedure TksGraph_DataFrm.cat7AnalysisClear(var P:TTest_Project);
var i:integer;
begin
// This should work, but it gives me the list out of bounds error
// unless the count is 0.
OSC_Series.Clear;
// This does not work, for obvious reasons. I get a "list out of
// bounds [3] for this one.
for i:=0 to OSC_Series.Count - 1 do OSC_Series.Delete[0];
// It seems this should work, but again I get the out of bounds [0]
// error.
repeat
OSC_Series.Delete(0);
until OSC_Series.Count = 0;
// This works. Don't ask me why.
showmessage('A - '+inttostr(OSC_Series.Count));
OSC_Series.Clear;
showmessage('B - '+inttostr(OSC_Series.Count));
// This also works.
sleep(2000);
OSC_Series.Clear;
// This does not work.
sleep(1000);
OSC_Series.Clear;
end;
I'm stumped, obviously.
This smells like the code is working with an object (OSC_Series) which has been destroyed and the memory then re-used for something else. When you then use the stale reference to that memory you get unexpected and unpredictable results.
Where is OSC_Series free'd ?
I would check all such places and make sure that you do not attempt to use the OSC_Series reference after it has been free'd.
Note also that since the series is owned by the form it could be that the form itself is contriving to executing code in events after it has destroyed its owned components (including this series).
OK, dumb and not dumb.
I experimented with where I put the showmessage statement. I found I could only avoid the error if that statement came after the OSC_Series.Clear statement. I kept moving that statement back until it was after the call to the AnalysisRefresh routine, which is in a button's OnClick event handler. This means that none of the code in the refresh, enable, or update routines was causing this.
Stepping back a bit, if the AnalysisRefresh routine fails the user is shown a message. After that box is closed OSC_Series.Clear is called. If I close the box by pressing SPACE or ENTER on the keyboard... no error. If I use the mouse, error.
The chart behind the mouse has an OnMouseMove event where I display the mouse position on a status bar. I also display a hint if the mouse is near a plotted point. After clicking the message box with a mouse to close it the OnMouseMove event is called and by the time it gets to where it can display the hint, the plotted point is gone, and... error.
So, it seemed like an almost random error, but it wasn't. The trigger was just somewhere else entirely. That's you my try/except block wasn't catching the error. EurekaLog was catching it, but in a different procedure far, far away. (Deltics' answer was pretty close.)
Thanks for your help and suggestions.
Dang it if some days I can push hundreds of lines of code with no problems, then something like this pops up and it costs me near two days.

cannot perform this operation on a open/closed dataset?

Hi guys i've written a function which has to check the ID given to the function as iID and then output the name of the member of it is found, otherwise it must output that it is not found
A different table is active when this function is called so it must change the table to Members (to search the ID) and then back again afterwards (I have multiple tables)
function fCheckID(iID:integer):String;
var sTable:string;
begin
sTable:=datamoduleX.tableX.TableName;
datamoduleX.tableX.TableName:='Members';
if datamoduleX.tableX.Locate('RefNo',iID,[]) then
result:=dmRooiX.tblRooiX['Name']+' '+datamoduleX.tableX['Surname']
else
result:='ID: '+inttostr(iID)+' does not exist';
datamoduleX.tableX.TableName:=sTable;
end;
but the problem is every time I call this function I get an error that says "Cannot perform this operation on an open dataset"
if I close the dataset before I run the function I get "Cannot perform this operation on a closed dataset"
I know the error occurs when I try to access the table name or change it (the function does not give the error when those 3 lines are commented out)
I have no idea how to make this work
any help would be greatly appreciated
Example :
Table1.TableName := 'TABLE1';
Table1.Open;
Table1.TableName := 'TABLE2'; <-- Cannot perform this operation on Open data set. Because Table1 is open
Table1.Locate('ID',11,[]);
simple solution
Table1.TableName := 'TABLE1';
Table1.Open;
Table1.Close; <--Close table before change table name
Table1.TableName := 'TABLE2';
Table1.Open; <-- Open new table before do Locate
Table1.Locate('ID',11,[]);

Access Violation on an attribut of dataset Delphi 7

After a lot of research, i decided to post my question here.
After the excusion of a big quantity of code, i get at the beautiful message "Access Violation ....". with the debug mode, i saw that this line "dsPRINCIPAL.DataSet.FieldByName('ID_NATURE').AsInteger" and all attributs of my dataset show this
"Violation d'accès pour 4034D1E0 accédant à FFFFFFFF".
My dsPRINCIPAL and the Dataset are active and in Edit Mode, and have 1 record(not empty).
Can you plz help me undestand why i have access violation on this line.
Here is the code:
if dsCTC_PRINCIPAL.DataSet.FieldByName('ID_NATURE_CTC').AsInteger:=Variable.Get(VCA‌​_PREFERENCES +'.ID_NATURE_CTC_MGE_SUIVI',scGlobal).asInteger; dsCTC_PRINCIPAL.DataSet.FieldByName('ID_OBJET_CTC').AsInteger:=Variable.Get(VCA_‌​PREFERENCES +'.ID_OBJET_HL_MGE',scGlobal).asInteger; dsCTC_PRINCIPAL.DataSet.FieldByName('QTEPASSE').AsInteger:=0;
Regards.
I suspect dsPRINCIPAL.DataSet or even dsPRINCIPAL.DataSet.FieldByName('ID_NATURE') returns nil, so calling AsInteger virtual method raised the access violation.
Try this:
var NatureField: TField;
if dsPRINCIPAL.DataSet=nil then
raise Exception.Create('dsPRINCIPAL.DataSet not assigned');
NatureField := dsPRINCIPAL.DataSet.FieldByName('ID_NATURE');
if NatureField=nil then
raise Exception.Create('ID_NATURE field missing');
...
NatureValue := NatureField.AsInteger;
By the way, for performance reasons, since FieldByName() can be slow, when you retrieve some data in a loop, it is a good habit to use a NatureField: TField local assessor.

TDelphiTwain component, corrupts delphi form (dfm file)

I have downloaded opensource delphi twain component (TDelphiTwain).
The interesting thing is, that when placed and saved on the form it creates bad dfm entry for itself.
object DelphiTwain: TDelphiTwain
OnSourceDisable = DelphiTwainSourceDisable
OnSourceSetupFileXfer = DelphiTwainSourceSetupFileXfer
TransferMode = ttmMemory
SourceCount = 0
Info.MajorVersion = 1
Info.MinorVersion = 0
Info.Language = tlDanish
Info.CountryCode = 1
Info.Groups = [tgControl, tgImage, tgAudio, MinorVersion]
Info.VersionInfo = 'Application name'
Info.Manufacturer = 'Application manufacturer'
Info.ProductFamily = 'App product family'
Info.ProductName = 'App product name'
LibraryLoaded = False
SourceManagerLoaded = False
Left = 520
Top = 136
end
The problem is with the line:
Info.Groups = [tgControl, tgImage, tgAudio, MinorVersion]
There are only three possible elements:
tgControl, tgImage and tgAudio
It adds MinorVersion everytime I Save the form.
When the app is run I get the error that there is invalid property for Info.Groups.
When i rmeove the bad part manually and without leaving dfm file the app starts ok.
I looked in the internet and there was one inquire regarding these strange issue, unfortunately it hasn't been resolved.
I think that there is some sort of memory corruption. In the post in teh internet, strange signs were displayed ...
Has anyone worked with that component or could give me some hint how this could be fixed?
The error seems to be in TTwainIdentity.GetGroups where result is not initialized. You can try to change the code by replacing
Include(Result, tgControl);
with
Result := [tgControl];
You have to recompile the package to make this change work inside the IDE.
I don't know the component, but I think the problem lies in the TTwainIdentity.GetGroups method. It starts like this:
begin
Include(Result, tgControl);
This means that it assumes that Result is initialized to an empty set. However, Result may contain garbage, and not necessarily an empty set. Change this method to look like this:
function TTwainIdentity.GetGroups(): TTwainGroups;
{Convert from Structure.SupportedGroups to TTwainGroups}
begin
Result := [tgControl];
if DG_IMAGE AND Structure.SupportedGroups <> 0 then
Include(Result, tgImage);
if DG_AUDIO AND Structure.SupportedGroups <> 0 then
Include(Result, tgAudio);
end;
Some result types will not throw a compiler warning about not being initialized, but that doesn't mean they are empty. Same goes, for instance, for strings.
See also: http://qc.embarcadero.com/wc/qcmain.aspx?d=894
But still, it is odd that this happens. Apparently, Delphi tries to find the name of the given item in the set and accidentally finds the name of another property. It seems to me that quite some checks in writing the dfm are missing if this happens. :)

Resources