Access violation in Lazarus - pointers

I've been doing my university project and I've stumbled upon problem I cannot solve.
After declaring variable (a pointer to TButton) I cannot use it. I'll copy a part of my code to help show the problem.
So before the implementation I have
private
prevButton : ^TButton;
After that I use OnClick procedure with my buttons
procedure TForm2.MissingButtonClick(Sender : TObject);
var
b : TButton;
begin
b := Sender as TButton;
prevButton := #b;
showmessage(prevButton^.Caption);
end;
And caption is shown no problem. But then when I use my OnClick procedure and try to change labels caption I get access violation.
procedure TForm2.LabelClick(Sender : TObject);
var
l : TLabel;
begin
l := Sender as TLabel;
if prevButton = nil then
showmessage('nil');
if prevButton <> nil then begin
showmessage(prevButton^.Caption);
l.Caption := (prevButton^.Caption);
prevButton^.OnClick := #AlreadyClicked;
prevButton^.Free;
prevButton^ := nil;
prevButton := nil;
refreshLabels(words);
end;
end;
So here is the question, why can't I use my variable in this procedure if I could use it without problem second earlier in other procedure.
Cheers.

Your code is severely flawed in several places.
First, the initial declaration is incorrect. A variable of type TButton is already a pointer, so you don't have to dereference it:
private
prevButton: TButton;
You also don't have to dereference it when assigning to it or using it:
procedure TForm2.MissingButtonClick(Sender : TObject);
begin
prevButton := TButton(Sender);
showmessage(prevButton.Caption);
end;
I'm not sure what your LabelClick event is trying to accomplish. If you free prevButton, you also invalidate the original button to which you pointed it. In other words, if you assigned prevButton := Button1;, and then prevButton.Free;, you've also freed Button1, and I don't think that's your intent. You can safely assign nil to prevButton, but don't free it also:
prevButton := nil; // No longer points to existing object instance
If you want to change the OnClick of an existing button, don't jump through all of those hoops:
// Assign this to the button's OnClick in the Object Inspector
procedure TForm1.Button1FirstClick(Sender: TObject);
begin
ShowMessage('First time I was clicked');
Button1.OnClick := #Button1AfterClicked;
end;
// Create these two in the code editor, and declare it in the
// form declaration, but don't assign it to an event of the button
// in the Object Inspector
procedure TForm1.Button1AfterClick(Sender: TObject);
begin
ShowMessage('Second time clicked');
Button1.OnClick := #Button1MoreClicks;
end;
procedure TForm1.Button1MoreClicks(Sender: TObject);
begin
ShowMessage('Third and later clicks');
end;

Related

Prevent automatic text selection on focusing TCxMemo

How to prevent automatic selection of entire text on focusing TCxMemo?
Currently I fight against that by scrolling the memo to the end on every keystroke:
procedure TMyForm.ScrollMemoToTheEnd(AMemo: TCxMemo);
begin
AMemo.SelLength := 0;
AMemo.SelStart := Length(AMemo.Text);
AMemo.InnerControl.Perform(EM_SCROLLCARET, 0, 0);
end;
procedure TMyForm.BarCodeMemoKeyPress(Sender: TObject; var Key: Char);
begin
inherited;
ScrollMemoToTheEnd(BarCodeMemo);
if Key = #13 then begin
ParseBarCode(BarCodeMemo.Text);
end else
if Key >= #32 then begin
// Allow adding characters only at the end
BarCodeMemo.Text := BarCodeMemo.Text + Key;
ScrollMemoToTheEnd(BarCodeMemo);
Key := #0;
end;
end;
I tried following:
procedure TMyForm.BarCodeMemoEnter(Sender: TObject);
begin
inherited;
ScrollMemoToTheEnd(BarCodeMemo);
end;
the BarCodeMemoEnter is called, but the ScrollMemoToTheEnd call makes no effect.
I think, there should be some property in TCxMemo to control this behaviour.

Error using SQLite ATTACH - DETACH in Delphi

I experience a, for me unsolvable, problem with ATTACH and DETACH in SQLite, using Delphi (Firedac).
I have one database file connected and attach a second one with:
FDConnection1.ExecSQL('ATTACH DATABASE "' + Import_DB_filename + '" AS IMPORTDB;');
Therein, the variable 'Import_DB_filename' contains the full path and filename of the database file.
This works OK and I can access both databases within the connection through the FireDac queries, and can do my coding without problems.
However, things go wrong upon detaching:
FDConnection1.ExecSQL('DETACH DATABASE IMPORTDB;');
In debugging mode, I always get the error:
Debugger Exception Notification
E Project My_Program.EXE raised exception class $C0000005 with message 'access violation at 0x00405d7b: read of address 0x00000000'.
Apparently something goes wrong with the memory assignments, since the debugger stops in a (assembly) function SysFreeMem(P:Pointer): Integer; in GETMEM.INC.
Whatever I try, the error persists and associates a memory leak that eventually leads to a crash of the compiler (Delphi Seattle Enterprise).
Even attaching and subsequent detaching of the database without passing any code results in the same error.
(FDconnection: locking mode = lmNormal;
JournalMode = jmOff or jmWALL or jmdelete)
I do hope that you can help me out on this lasting problem.
If you run the project below, you should find that:
You can access two Sqlite databases quite happily using separate FDConnections and FDTables.
You can move data from a table in one db to a table of the same name in the other using a FireDAC FDDataMove component.
Code:
unit BatchMoveu;
interface
[...]
type
TForm3 = class(TForm)
FDConnection1: TFDConnection;
DBGrid1: TDBGrid;
DataSource1: TDataSource;
DBNavigator1: TDBNavigator;
FDGUIxWaitCursor1: TFDGUIxWaitCursor;
FDPhysSQLiteDriverLink1: TFDPhysSQLiteDriverLink;
Button1: TButton;
FDTable1: TFDTable;
FDConnection2: TFDConnection;
DataSource2: TDataSource;
DBGrid2: TDBGrid;
btnBatchMove: TButton;
FDDataMove1: TFDDataMove;
FDTable2: TFDTable;
procedure btnBatchMoveClick(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
procedure PopulateTable1;
procedure TestDataMove;
public
procedure CreateDatabase(DBName : String; FDConnection : TFDConnection;
FDTable : TFDTable);
end;
var
Form3: TForm3;
implementation
{$R *.dfm}
const
DBName1 = 'd:\delphi\code\sqlite\db1.sqlite';
DBName2 = 'd:\delphi\code\sqlite\db2.sqlite';
procedure TForm3.Button1Click(Sender: TObject);
begin
CreateDatabase(DBName1, FDConnection1, FDTable1);
CreateDatabase(DBName2, FDConnection2, FDTable2);
PopulateTable1;
FDTable2.Open;
end;
procedure TForm3.CreateDatabase(DBName : String; FDConnection : TFDConnection;
FDTable : TFDTable);
var
AField : TField;
i : Integer;
begin
if FileExists(DBName) then
DeleteFile(DBName);
AField := TLargeIntField.Create(Self);
AField.FieldName := 'ID';
AField.DataSet := FDTable;
AField.Name := AField.DataSet.Name + 'IDField';
AField := TWideStringField.Create(Self);
AField.Size := 80;
AField.FieldName := 'Name';
AField.DataSet := FDTable;
AField.Name := AField.DataSet.Name + 'NameField';
FDConnection.Params.Values['database'] := DBName;
FDConnection.Connected:= True;
FDTable.CreateTable(False, [tpTable]);
end;
procedure TForm3.PopulateTable1;
var
i : Integer;
begin
FDTable1.Open;
for i:= 1 to 1000 do begin
FDTable1.InsertRecord([i, 'Row ' + IntToStr(i)]);
end;
FDTable1.Close;
//FDConnection1.Commit;
FDTable1.Open;
end;
procedure TForm3.TestDataMove;
var
Item : TFdMappingItem;
begin
Item := FDDataMove1.Mappings.Add;
Item.SourceFieldName := 'ID';
Item.DestinationFieldName := 'ID';
Item := FDDataMove1.Mappings.Add;
Item.SourceFieldName := 'Name';
Item.DestinationFieldName := 'Name';
FDDataMove1.Source := FDTable1;
FDDataMove1.Destination := FDTable2;
FDDataMove1.Options := FDDataMove1.Options - [poOptimiseSrc];
FDDataMove1.Execute;
FDConnection2.Connected := False;
FDTable2.Open;
end;
procedure TForm3.btnBatchMoveClick(Sender: TObject);
begin
TestDataMove;
end;
procedure TForm3.FormDestroy(Sender: TObject);
begin
FDConnection1.Close;
end;
end.

SQLite insert on android works only once

I'm with a problem on Delphi, i create a simple app to test the mobile power of RadStudio, I created a simple app that put some data into some inputs and then add it to database when button is clicked. I followed this Embarcadero tutorial as starting point
The problem is that I only get one entry added, then no more entries are added or the list is not refreshed. Below some code:
Table creation:
procedure TTabbedForm.logAfterConnect(Sender: TObject);
begin
log.ExecuteDirect('CREATE TABLE IF NOT EXISTS lista (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,idCons INTEGER,nome TEXT,kms INTEGER,kmsAlarme INTEGER,quantidade INTEGER,quantidadeAlarme INTEGER,data INTEGER,dataAlarme INTEGER,alarmeMsg TEXT)');
end;
Add button code:
procedure TTabbedForm.btnGravarClick(Sender: TObject);
begin
try
SQLQueryInsert.ParamByName('idCons').AsInteger := PopupBoxTipo.ItemIndex;
SQLQueryInsert.ParamByName('nome').AsString := PopupBoxTipo.Text;
SQLQueryInsert.ParamByName('kms').AsInteger := StrToInt(kmsEdit.Text);
SQLQueryInsert.ParamByName('quantidade').AsInteger := StrToInt(qtdEdit.Text);
SQLQueryInsert.ParamByName('data').AsInteger := DateTimeToUnix(dtaEvento.Date);
SQLQueryInsert.ExecSQL();
lista.Refresh;
LinkFillControlToField1.BindList.FillList;
except
on e: Exception do
begin
ShowMessage(e.Message);
end;
end;
end;
If you need some more code snippet, please ask!
Thanks in advance for any reply!
Try this method it works for me
procedure TData.InsertItem(someObject: TSomeObjectClass);
var
qry: TFDQuery;
begin
qry := CreateQry( 'insert into SomeObject(id, description, something)'+
' values (:id, :description, :something);', false);
qry.Params.ParamByName('id').AsInteger := someObject.id;
qry.Params.ParamByName('description').asstring := someObject.description;
qry.Params.ParamByName('something').asstring := someObject.something;
qry.Prepare;
qry.execsql;
qry.Free;
end;
I've put and object in the parameter but you can also put the data you want to insert seperatly

How to implement a trie data structure in Pascal?

I would like to make a try in Pascal. I started is but I the insert method does not work correctly. Here is a part of the program:
type PVrchol = ^Vrchol;
Vrchol = record
endOfTheWord:boolean;
deti:array['a'..'z'] of PVrchol;
end;
var trie:PVrchol;
FUNCTION getChildnode(current:PVrchol;k:char):PVrchol;
Begin
if current^.deti[k]<>nil then
getChildnode:=current^.deti[k]
else
getChildNode:=nil;
End;
PROCEDURE insertWord(word:string;var trie:PVrchol);
var i:integer;
current, childNode:PVrchol;
Begin
current:=trie;
childNode:=current;
for i:=1 to Length(word) do begin
childNode:=getChildnode(current,word[i]);
if childNode=nil then begin
new(childNode);
current^.deti[word[i]]:=childNode;
end;
current:=childNode;
end;
current^.endOfTheWord:=true;
End;
BEGIN
new(trie);
//there are some methods reading the words from input, and calling the insertWord procedure.
END.
The insertWord procedure always gets a word, so the parameter wont be "".
The problem might be that records are not automatically zeroed as classes are? Try adding
fillchar(childnode,sizeof(childnode),#0);
after the new(childnode)

Pascal changing recursion to while loop with pointer

Hi I'm learning pascal and testing some functions
I made a recursive insert procedure like this.
if node exist compare two keys, and if not, make a room for new node.
procedure INSERT (KEY : integer; var NODE : NODEPTR);
begin
if NODE = Nil then
begin
New (NODE);
NODE^.KEY := KEY;
NODE^.LEFT := Nil;
NODE^.RIGHT := Nil
end
else
if KEY < NODE^.KEY then
INSERT (KEY, NODE^.LEFT)
else
INSERT (KEY, NODE^.RIGHT)
end;
and What I'm trying to do is changing recursive function to while-loop.
so I made procedure like this
if node exist do while loop until node is empty.
and after while loop is over, make a new node
procedure INSERT (KEY : integer; var NODE : NODEPTR);
begin
while NODE <> nil do
begin
if KEY < NODE^.KEY then
NODE:=NODE^.LEFT
else
NODE:=NODE^.RIGHT
end;
New (NODE);
NODE^.KEY := KEY;
NODE^.LEFT := Nil;
NODE^.RIGHT := Nil
end;
when first node is root, while loop is true and execute this code
but after this, while loop changes to false and make a new node.
if KEY < NODE^.KEY then
NODE:=NODE^.LEFT
else
NODE:=NODE^.RIGHT
Eventually there is no node connection, and this program just keep making new node.
Is there anything that i missed? or any improvising about the second code?
thanks in advance :)
The thing you missed is that you never link up the nodes (and in the current setup, you can't actually linkup the nodes). You have fields on your record for the left and right node of a node, but you don't assign them. So you end up just creating nodes without every linking them up; you have a linked list with the links missing.
At one point, you'll need something along the lines of NODE^.RIGHT := NEWNODE (or NODE^.LEFT := NEWNODE, of course).
You'll need something along the ways of (my Pascal is a bit rusty, so beware of syntax errors ;) ):
procedure INSERT(key: Integer; var NODE: NODEPTR)
begin
NODEPTR root := NODE;
if (key < root^.KEY) then begin
while (root^.LEFT <> nil) do begin
root := root^.LEFT;
end;
new(NODE);
NODE^.KEY := key;
root^.LEFT := NODE;
node^.RIGHT := root;
end else begin
while (root^.Right <> nil) do begin
root^ := root^.RIGHT;
end;
new(NODE);
NODE^.KEY := key;
root^.RIGHT := NODE;
NODE^.LEFT := root;
end;
end;
There's lots of improvements and beautifications to be made in the example above, but I think it shows the general idea.

Resources