StackOverFlow error - delphi

I have a DBGrid and use it to get data into a Array. But as soon as I press the button to do this procedure I get a StackOverFlow error ? Here is the Code I use :
iRy := 0;
iCol := DBGrid.Columns.Count;
sTest := DBGrid.Columns[0].Field.AsString;
While sTest <> '' do
begin
for k := 1 to iCol do
begin
arrData[iRy+1,iCol] := DBGrid.Columns[iCol].Field.AsString;
end;
Inc(iRy);
DBGrid.DataSource.DataSet.Next;
sToets := DBGrid.Columns[0].Field.AsString;
end;
I am using Delphi 7 .

When does sTest change?
A stackoverflow is caused by running out of memory on the stack. This While loop will go forever. You need to set sTest to something other than '' in the loop, or perhaps you meant to use an if statement.

You have a couple of issues:
You never assign anything else to sTest after you initialize it (before the loop)
You're continually looping over the same record, because there's no Next, and no change to sTest. Once you start the loop, you stay there.
You're going past the end of the Columns, because you're going to Column.Count, and the last valid index is Column.Count - 1
Try this instead, to see if it's more what you're after:
iRy := 0;
sCol := DBGrid.Columns.Count - 1;
// I'm not sure why you're not putting this in the first (0 index) element
// of arrData - is that intentional?
sTest := DBGrid.Columns[0].Field.AsString;
while (sTest <> '') and (not DBGrid.DataSource.DataSet.Eof) do
begin
for k := 1 to iCol do
arrData[iRy, iCol] := DBGrid.Columns[iCol].Field.AsString;
DBGrid.DataSource.DataSet.Next;
Inc(iRy);
sTest := DBGrid.Columns[0].Field.AsString;
end;

Related

Delphi Selection Sort seems to sort backwards

procedure TfrmSorting.btnSortClick(Sender: TObject);
var
K,L,I,iNumElements : integer;
sKeep : string;
begin
iNumElements := length(arrNames);
for K := 1 to iNumElements - 1 do
begin
for L := K + 1 to iNumElements do
begin
if arrNames[K] < arrNames[L] then
begin
sKeep := arrNames[L];
arrNames[L] := arrNames[K];
arrNames[K] := sKeep;
end;
end;
end;
reditNames.Lines.Clear;
I := 1;
for K := 1 to iNumElements - 1 do
begin
reditNames.Lines.Add(arrNames[I]);
I := I + 1;
end;
end;
I'm using this sorting algorithm to sort an Array. I then diplay the contents on a richedit but instead of going from A..Z its displays Z..A. Is there a problem with the algorithm or the way im adding lines to the richedit? Thanks
K is less than L and you swap items if the K-th is less than the L-th. You have your comparison the wrong way round. Use > rather than <.
Your indexing is dubious also. Is your array really 1-based? And why do you only add 1 to N-1 to the output? Are you missing the last item In other words, I suspect there are other defects in your code. You have not shown it all so I cannot be sure.
Finally, why not use the built in sorting functionality?

Variable in for loop is not getting called

I am using DBXJson to parse a simple json file called response.jsonand show it's contents in a grid, but only the first row of the grid ever gets populated with the data and even though there is more rows/data to display. I am using a custom grid in the code below but I have tried a variation of the below code using the standard stringgrid and it exhibited the same behavior. This is the code I am using to parse the response and show it in my grid.
var
sl: TStringList;
LJsonArr: TJSONArray;
LJsonValue: TJSONValue;
LItem: TJSONValue;
col, row: Integer;
begin
col := 0;
row := 0;
sl := TStringList.Create;
sl.LoadFromFile('response.txt');
LJsonArr := TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(sl.text), 0)
as TJSONArray;
for LJsonValue in LJsonArr do
begin
NextGrid1.AddRow();
for LItem in TJSONArray(LJsonValue) do
begin
NextGrid1.Cells[col, row] := TJSONPair(LItem).JsonValue.Value;
inc(col);
end;
inc(row);
end;
sl.Free;
end;
I suspect that the problem lies in the fact that the row variable is out of place and is not getting called and that is causing only the first row to display, but I could be mistaken and I am hoping that a fresh pair of eyes can spot the problem.
The problem is that col must be re-initialised to zero every time you start a new row. So move the initialization of col into the outer loop.
row := 0;
for LJsonValue in LJsonArr do
begin
col := 0;
NextGrid1.AddRow();
for LItem in TJSONArray(LJsonValue) do
begin
NextGrid1.Cells[col,row] := TJSONPair(LItem).JsonValue.Value;
inc(col);
end;
inc(row);
end;
I don't know this JSON library but if it allows you to access array elements with random access then a traditional oindexed for loop would lead to cleaner code that the for in loop that you use. In pseudo code:
for row := 0 to arr.length do
begin
item := arr[row];
for col := 0 to item.length do
grid.Cells[col,row] := item[col];
end;
As a rule of thumb, for in loops are better if you do not need to know the item index. However, as soon as you need to know the item index then traditional indexed for loops are usually to be preferred.

Remove controls from a scrollbox

I am dynamicaly (at run time) adding controls into a TScrollBox using myScrollBox.AddObject
Now I need to remove all the controls I added to put new ones.
I tryed myScrollBox.Controls.Clear but after I call that function, any control I add are not showing up.
(Warning: I'm new to delphi and Firemonkey)
Update 1
Here is how I add my objects (this is just a test function)
procedure TMainForm.TaskDetailsAdd;
var
btn1 : TButton;
intI : Integer;
count: Integer;
begin
scbTaskVariables.BeginUpdate;
count := 0;
for intI := 0 to 100 do
begin
btn1 := TButton.Create(self);
btn1.Text := 'Salut ' + IntToStr(intI);
btn1.Parent := scbTaskVariables;
btn1.OnClick := Button1Click;
btn1.Tag := intI * 10;
btn1.Position.Y := intI * 50;
btn1.Position.X := intI * 15;
scbTaskVariables.AddObject(btn1);
count := scbTaskVariables.ControlsCount;
end;
scbTaskVariables.EndUpdate;
end;
The funny thing is that if I place a break point on count := scbTaskVariables.ControlsCount
I can see that ControlsCount goes from 0 to 1 for the first control and then it stays to 1 for the others.
Update 2
I submitted QC#125440.
The inverse of AddObject is RemoveObject. Call ScrollBox.RemoveObject(aChildObject) for each child object that you wish to remove.
The alternative is to set the Parent property of the child object. Set it to ScrollBox to add it. Set it to nil to remove it. This is interchangeable with AddObject and RemoveObject. You can do it either way.
However, when you attempt to do this, just as your said, attempts to add new controls fail if you have removed controls earlier. This would appear to be a bug. Please submit a QC report.
I tested on XE6.
Try with:
myScrollBox.Content.DeleteChildren;
I have added this as an Answer but as there are bugs in FMX it should be considered as a workaround at this stage.
I spent some time on your problem about deleting your buttons, but also to tried to find out more about the bug. David was very quick to spot this and shows his experience.
Two of my findings were that (1) the AddObect() does not appear to work with the buttons, for some reason, they are not being seen as "Objects" but as "Components". (2) Also I found that creating btn1 with the "scrollBox" as its owner helped to achieve an adequate result.
I used 1 x TScrollbox, 2 x TButton and 4 x TLabel. The buttons left with their default name and the TScrollBox with Your default name. So you can just copy and paste. btn1 is made a private variable along with it's procedures.
procedure TMainForm.TaskDetailsAdd;
var
intI : Integer;
begin
label1.Text := IntToStr(scbTaskVariables.ComponentCount);
// Initial count = 1, Probably the scroll box.
if scbTaskVariables.ComponentCount >1 then
TaskDetailsDel; // Don't create Buttons with same Name if already exists.
scbTaskVariables.BeginUpdate;
for intI := 0 to 99 do
begin
Sleep(20); //Keeps the "Pressed Button" active to prove it is working
btn1 := TButton.Create(scbTaskVariables);
btn1.Parent := scbTaskVariables;
btn1.Position.Y := intI * 50;
btn1.Position.X := intI * 15;
btn1.Tag := intI * 10;
btn1.TabOrder := 10 + intI;
btn1.Name := 'MyBtn' + IntToStr(intI);
btn1.Text := 'Salut ' + IntToStr(intI);
btn1.OnClick := Button1Click;
if btn1.IsChild(scbTaskVariables) = true then
Label2.Text := 'True'
else // All this, proves buttons not seen as children.
Label2.Text := 'False';
scbTaskVariables.AddObject(btn1);
// AddObject() taken out as button is not seen as "FmxObject"
end;
scbTaskVariables.EndUpdate;
Label3.Text := IntToStr(scbTaskVariables.ComponentCount);
// Count now all created (includes ScrollBox).
Label4.Text := IntToStr(scbTaskVariables.ControlsCount);
end;
The "TaskDetailsDel" procedure was was quite easy once I had determined that I was really dealing with "Components"
procedure TMainForm.TaskDetailsDel;
var
intI : Integer;
count: Integer;
begin
label1.Text := '';
label2.Text := '';
label3.Text := '';
label4.Text := '';
for intI := 0 to 99 do
begin
Sleep(20); //Keeps the "Pressed Button" active to prove it is working
btn1 := TButton(scbTaskVariables.FindComponent('MyBtn' + IntToStr(intI)));
btn1.Parent := Nil;
FreeAndNil(btn1);
end;
Count := scbTaskVariables.ComponentCount;
Label1.Text := IntToStr(Count);
end;
Using the FindComponent line did the trick.
Press F1 and type the links into the URL Box; I found these interesting, especially seeing how TButton is derived in the VCL and FMX.
ms-help://embarcadero.rs_xe3/libraries/Vcl.StdCtrls.TButton.html
ms-help://embarcadero.rs_xe3/libraries/FMX.Controls.TButton.html
ms-help://embarcadero.rs_xe3/libraries/FMX.Types.TStyledControl.html
ms-help://embarcadero.rs_xe3/rad/Objects,_Components,_and_Controls.html
ms-help://embarcadero.rs_xe3/libraries/FMX.Types.TFmxObject.AddObject.html
The TScrollBox has by default 1 component that is of type TScrollContent that is responsible of the display of the other components. So if we delete him then nothing will be shown ever.
I created this little function to RemoveAllComponents inside the TScrollBox (Expect the TScrollContent):
procedure RemoveAllComponentsScrollBox(ScrollBox : TScrollBox);
var i : integer; Obj : TFmxObject;
begin
for I := ScrollBox.ComponentCount-1 downto 0 do
begin
if ((ScrollBox.Components[i] is TFmxObject) and not (ScrollBox.Components[i] is TScrollContent)) then
begin
Obj:=TFmxObject(ScrollBox.Components[i]);
Obj.Parent:=nil;
FreeAndNil(Obj);
end;
end;
end;
This method can be improved by recursivity

Access violation in Delphi after successful run

I have written a program in Delphi to compute, display and save a Pascals' triangle for a user-defined number of rows. It works fine (displays the triangle, and allows me to save it), except that it comes up with an access violation at the end! Here is the message:
Access violation at address 004031DB in module 'Project1.exe'. Read of address 00000000.
I have a 2D dynamic array in the procedure but I release the memory at the end (:= nil). Why is it still giving me an access violation? Very frustrating!
I searched the archives for an answer but could not find an appropriate answer. Any help will be greatly appreciated.
Here is the code (I was a little hesitant as there is a bit of code:
procedure TForm1.btnPTClick(Sender: TObject);
var
I, J, K, N, MidCol: integer;
PT: array of array of integer;
Row: string;
begin
K := StrToInt(lblNumRows.Text);
N := StrToInt(lblNumRows.Text);//# values in row = row number
try
//initiatlize the array
SetLength(PT, K, (N*2)-1);
for I := 0 to K-1 do
for J := 0 to (N*2-1) do
PT[I,J] := 0;
MidCol := (N*2 div 2)-1;//MidCol already 0-based
for I := 0 to K-1 do
begin
if (I = 0) then
PT[I,MidCol] := 1//first row gets 1 in the middle column
else if I = 1 then
begin
PT[I,MidCol-1] := 1;
PT[I,MidCol+1] := 1; //first and last value in second = 1
end
else //if any other row
begin
//Middle column
PT[I, MidCol] := PT[I-1,MidCol-1] + PT[I-1,MidCol+1];
//right triangle
for J := MidCol+1 to (N*2-1) do
begin
if (PT[I-1, J-1]=1) then//if already at the end of prev row
begin
PT[I,J] := 1;
break;
end
else
PT[I,J] := PT[I-1,J-1] + PT[I-1,J+1];
end;
//left triangle
for J := MidCol-1 downto 0 do
begin
if (PT[I-1, J+1] = 1) then //if already at the end of prev row
begin
PT[I,J] := 1;
break;
end
else
PT[I,J] := PT[I-1,J-1] + PT[I-1,J+1];
end;
end;
end;
//now add the pyramid to the memo
Application.ProcessMessages;
for I := 0 to K-1 do
begin
Row := '';
for J := 0 to N*2-1 do
begin
if (PT[I,J] = 0) then Row := Row + ' '
else Row := Row + IntToStr(PT[I,J]);
end;
Memo.Lines.Add(Row);
end;
finally
SetLength(PT, 0, 0);
end;
end;
Read of address 00000000
This indicates that you are trying to access memory using a pointer that is nil. To know why that is so one would need code. At present only you have code, and so only you can explain.
Run the program in the debugger. Enable Debug DCUs in case the error is raised in RTL/VCL code. Ensure that the debugger is configured to break on exceptions. The run your program and trigger the error. The debugger will show you which nil object is being de-referenced. Then you have to work out why that reference is nil.
The code you have added to the answer has a buffer overrun which could certainly explain the problem. Your SetLength is incorrect and should read:
SetLength(PT, K, N*2);
Your code writes to memory out-of-bounds and so corrupts the heap. You should ask the compiler to produce runtime checks on your array bounds. Enable the compiler's range checking option. Had you done so, you would have found this error yourself.
For what it is worth you do not need that try/finally block since the compiler will automatically insert a hidden block. There's no need for two when one suffices. A dynamic array is a managed type whose memory is disposed when the variable leaves scope.
Press F7, to start the project in the debugger.
Look in the main menu for the "Find Error..." option (in Delphi 7 it was under the Search menu)
then enter the address from the exception: 004031DB.
This will show you the exact line where the exception occurred.
Read of address 00000000 generally indicates you are using a pointer that has a nil value.

Can I have an dynamic array of IDirect3DVertexBuffer9?

I am working with Delphi and DirectX. I want an dynamic array of IDirect3DVertexBuffer9. Is it possible? If yes then how?
I have written a code for it. But, it seems to be problematic. My code is shown below -
totalBuffer := 4;
SetLength(g_pVB,totalBuffer);
for cnt := 0 to totalBuffer - 1 do begin
if FAILED(g_pd3dDevice.CreateVertexBuffer(1 * SizeOf(TD3DXVector3),
0, D3DFVF_XYZ,
D3DPOOL_DEFAULT, g_pVB[cnt], nil)) then begin
Result := E_FAIL;
Exit;
end;
if FAILED(g_pVB[cnt].Lock(0, 0, Pointer(pVert[cnt]), 0)) then begin
Result := E_FAIL;
Exit;
end;
pVert[cnt] := 0;
end;
here, the problem I am facing is that, once it enter in for loop it continues and not exit the loop when cnt value is 4. And if I write static value 3 in for loop instead of totalBuffer it will exit the loop when value is 4.
You can find samples here. At Cull sample they used "array of IDirect3DVertexBuffer".

Resources