Dynamic array access violation error - pascal

program LengthOfArray;
uses sysutils;
procedure Main();
var myArray: array of String;
begin
myArray[0] := 'hi';
myArray[1] := 'bye';
myArray[2] := 'hello';
WriteLn('array is: ', Length(myArray));
end;
begin
Main();
end.
When I run this, it gives me an error like this:
An unhandled exception occurred at $0040210C :
EAccessViolation : Access violation
$0040210C
$00401540
$004015ED
When compiling, it gave me a message Warning: Local variable "myArray" does not seem to be initialized... but I don't understand because I assigned 3 values to my array already. Isn't that initialized?

Related

Strange behavior with TThread.CreateAnonymousThread

I was unable to follow how it is working.
A very simple example first, to try explain my situation better.
This code is inside a new Form Form1 create in a new project. Where mmo1 is a Memo component.
TOb = class
Name : String;
constructor Create(Name : String);
procedure Go();
end;
procedure TOb.Go;
begin
Form1.mmo1.Lines.Add(Name);
end;
Then I have a button with this event:
procedure TForm1.btn4Click(Sender: TObject);
var
Index : Integer;
begin
mmo1.Lines.Clear;
for Index := 1 to 3 do
TThread.CreateAnonymousThread(TOb.Create('Thread ' + IntToStr(Index)).Go).Start;
end;
And my output on the memo is:
Thread 4
Thread 4
Thread 4
I really don't got it.
First question: Why the "Name" output is: Thread 4? Is a For loop from 1 to 3. At least should be 1 or 3
Second: Why it only execute the last thread "Thread 4", instead of 3 times in sequence "Thread 1", "Thread 2", "Thread 3"?
Why I'm asking this? I have an object that has already a process working fine. But now I found me in a situation that I need a List of this object to be processed. Sure work fine process one by one, but in my case they are independent one of other so I thought "hm, lets put them in threads, so it will run faster".
To avoid modifying the object to extend TThread and overriding Execute I look up on how to execute a thread with a procedure instead of an object that inherits from TThread and found the Anonymous Thread. Works really great with one object, but when I tried loop through my object list, strange behaviors happens.
This has the same effect.
for Index := 1 to 3 do
TThread.CreateAnonymousThread(
procedure
var
Ob : TOb;
begin
OB := TOb.Create('Thread ' + IntToStr(Index));
OB.Go;
end
).Start;
Sure I'm not clean the object, this was just some tests that I was running.
Any Ideas? Or in this case I will need to inherits from TThread and override the Execute methode?
The funny thing is that THIS runs just fine.
mmo1.Lines.Clear;
TThread.CreateAnonymousThread(TOb.Create('Thread ' + IntToStr(1)).Go).Start;
TThread.CreateAnonymousThread(TOb.Create('Thread ' + IntToStr(2)).Go).Start;
TThread.CreateAnonymousThread(TOb.Create('Thread ' + IntToStr(3)).Go).Start;
Output:
Thread 1
Thread 2
Thread 3
Works really great with one object, but when I tried loop through my object list, strange behaviors happens.
You are likely not taking into account how anonymous procedures bind to variables. In particular:
Note that variable capture captures variables--not values. If a variable's value changes after being captured by constructing an anonymous method, the value of the variable the anonymous method captured changes too, because they are the same variable with the same storage. Captured variables are stored on the heap, not the stack.
For example, if you do something like this:
var
Index: Integer;
begin
for Index := 0 to ObjList.Count-1 do
TThread.CreateAnonymousThread(TOb(ObjList[Index]).Go).Start;
end;
You will actually cause an EListError exception in the threads (I least when I tested it - I don't know why it happens. Verified by assigning an OnTerminate handler to the threads before calling Start(), and then having that handler check the TThread(Sender).FatalException property).
If you do this instead:
var
Index: Integer;
Ob: TOb;
begin
for Index := 0 to ObjList.Count-1 do
begin
Ob := TOb(ObjList[Index]);
TThread.CreateAnonymousThread(Ob.Go).Start;
end;
end;
The threads won't crash anymore, but they are likely to operate on the same TOb object, because CreateAnonymousThread() is taking a reference to the TOb.Go() method itself, and then your loop is modifying that reference's Self pointer on each iteration. I suspect the compiler is likely generating code similar to this:
var
Index: Integer;
Ob: TOb;
Proc: TProc; // <-- silently added
begin
for Index := 0 to ObjList.Count-1 do
begin
Ob := TOb(ObjList[Index]);
Proc := Ob.Go; // <-- silently added
TThread.CreateAnonymousThread(Proc).Start;
end;
end;
If you do this instead, it will have a similar issue:
procedure StartThread(Proc: TProc);
begin
TThread.CreateAnonymousThread(Proc).Start;
end;
...
var
Index: Integer;
Ob: TOb;
begin
for Index := 0 to ObjList.Count-1 do
begin
Ob := TOb(ObjList[Index]);
StartThread(Ob.Go);
end;
end;
Probably because the compiler generates code similar to this:
procedure StartThread(Proc: TProc);
begin
TThread.CreateAnonymousThread(Proc).Start;
end;
...
var
Index: Integer;
Ob: TOb;
Proc: TProc; // <--
begin
for Index := 0 to ObjList.Count-1 do
begin
Ob := TOb(ObjList[Index]);
Proc := Ob.Go; // <--
StartThread(Proc);
end;
end;
This will work fine, though:
procedure StartThread(Ob: TOb);
begin
TThread.CreateAnonymousThread(Ob.Go).Start;
end;
...
var
Index: Integer;
Ob: TOb;
begin
for Index := 0 to ObjList.Count-1 do
begin
Ob := TOb(ObjList[Index]);
StartThread(Ob);
// or just: StartThread(TOb(ObjList[Index]));
end;
end;
By moving the call to CreateAnonymousThread() into a separate procedure that isolates the actual reference to TOb.Go() into a local variable, you remove any chance of conflict in capturing the reference for multiple objects.
Anonymous procedures are funny that way. You have to be careful with how they capture variables.
After reading a the article that Remy Lebeau post on the comments, I found this solution.
changing the main object by add one more procedure that make the call.
Change the loop instead of creating the anonymous thread at the main loop, it is created inside the object.
TOb = class
Name : String;
constructor Create(Name : String);
procedure Process();
procedure DoWork();
end;
procedure TOb.Process;
begin
TThread.CreateAnonymousThread(DoWork).Start;
end;
procedure TOb.DoWork;
var
List : TStringList;
begin
List := TStringList.Create;
List.Add('I am ' + Name);
List.Add(DateTimeToStr(Now));
List.SaveToFile('D:\file_' + Name + '.txt');
List.Free;
end;
And the loop:
List := TObjectList<TOb>.Create();
List.Add(TOb.Create('Thread_A'));
List.Add(TOb.Create('Thread_B'));
List.Add(TOb.Create('Thread_C'));
List.Add(TOb.Create('Thread_D'));
for Obj in List do
//TThread.CreateAnonymousThread(Obj.Go).Start;
Obj.Process;
Thats resolves the problem with just a minimum change on the Main Object.
This about race condition. When you increased to max value to 100, you will see different values. Threading not guarantee when Thread starts or ends.
You can try this code block.
for I := 1 to 100 do
begin
TThread.CreateAnonymousThread(
procedure
var
Msg : string;
begin
try
Msg := 'This' + I.ToString;
MessageDlg(Msg,mtCustom,
[mbYes,mbAll,mbCancel], 0);
Except
on E: Exception do
End;
end
).Start;
end;
If you want a guarantee to write 1 to 4, you should instantiate every value before send to Thread.
for I := 1 to 100 do
begin
TThread.CreateAnonymousThread(
procedure
var
Msg : string;
begin
var instanceValue := I;
try
Msg := 'This' + instanceValue.ToString;
MessageDlg(Msg,mtCustom,
[mbYes,mbAll,mbCancel], 0);
Except
on E: Exception do
End;
end
).Start;
end;

Pascal - Whats causing this runtime error(216)?

Whenever I run SafteyDepositBox.SetNewCode I get a runtime error 216. Any idea whats causing this?
This is the error :
Runtime error 216 at $00401EFC $00401EFC $0040153D
$00401596 $00406E31
program Boxy;
{$MODE OBJFPC}
{$M+}
type
SDB = class
private
State : string;
Code : string;
public
Constructor Create();
procedure SetNewCode(newcode:string);
function Valid(s:string):boolean;
end;
constructor SDB.Create();
begin
State := 'Open-NoCode';
Code := '';
end;
procedure SDB.SetNewCode(newcode:string);
begin
Code := newcode;
writeln(Code);
end;
function SDB.Valid(s:string):boolean;
var
IsValid : boolean;
begin
If (length(s) = 4) then
IsValid := true
else
IsValid := false;
Valid := IsValid;
end;
var
SafetyDepositBox : SDB;
begin
SafetyDepositBox.Create();
SafetyDepositBox.SetNewCode('r2d2');// runtime error 216 here
end.
OMG you just made me remember Pascal!
This is how you call the object constructor:
SafetyDepositBox := SDB.Create();

How to find out Error No. in ObjFPC in a try-except statement

I am trying to find a way to detect the error no in objfpc, what I tried is shown below:
Program ErrorHandling;
{$R+}
{$MODE objfpc}
Uses
SysUtils, crt;
Var
intvar: 1 .. 100;
Begin
Try
clrscr;
writeln( 'enter intvar: ');
readln(intvar);
Except
on
e: Exception
Do
Begin
writeln('In Exception, IOResult: ',IOResult);
Case IOResult Of
201: writeln('Range intvar out of range 1-100!'); {How can we find that Error no is 201}
Else
writeln('Unkown Error!'); readln;
End
End
End;
writeln('intvar: ' , intvar);
readln;
End.
But How can we find if the 201 Range Error occurs. I tried using IOResult command but it always shows "Unknown Error" string of my case statement.
Using exceptions you can check error type by type of exception itself.
The classic way is:
try
clrscr;
writeln('enter intvar: ');
readln(intvar);
except
on e: ERangeError do // Executes when raised exception is ERangeError
begin
Writeln('Range intvar out of range 1-100!');
end;
on e: Exception do // Executes for any other exceptions
begin
Writeln(e.ClassName); // Shows exception class you can use at the "on e:" constuction
Writeln(e.Message);
end;
end;
Look at The try...except statement in the official documentation.

how to get a variable out of a procedure in the right way in PASCAL?

I'm following an internet course on the basics of programming. After making a diagram I convert it to code, right now this is PASCAL language.
I'm having a problem with procedures and can't find an answer, nor in the course, nor with some google-ing.
I want to get a variavble back form a procedure. Right now iIhave a working piece of code but I think this is not the good way of working. Here's an extract of the code:
program WELKEWAGEN;
// declare your variables here
var T, N, KM, vari, prijsDW, prijsBW, jrenGEBR, taksDW, taksBW, prijsB, verbrBW, prijsD, verbrDW : real;
procedure OPHALEN(para : string);
begin
repeat
writeln('geef de ', para , ' op');
readln(vari);
until (vari > 0);
end;
begin
//this is the main program but there is more code ofcourse
OPHALEN('prijs benzinewagen');
prijsBW := vari;
//...
end.
Now the internet course says I should program it like this:
begin
//...
prijsBW := OPHALEN('prijs benzinewagen');
//...
end.
But this is not working.
I get following errors:
WELKEWAGEN.pas(24,14) Error: Incompatible types: got "untyped" expected "Real"
WELKEWAGEN.pas(50) Fatal: There were 1 errors compiling module, stopping
pas(24,14) is this line: prijsBW := OPHALEN('prijs benzinewagen');
Procedures don't return values, so the syntax
prijsBW := OPHALEN('prijs benzinewagen');
is invalid.
If you want to return a value, you need to define a function instead:
function OPHALEN(para : string): Real;
var
Res: Real;
begin
Res := 0;
repeat
writeln('geef de ', para , ' op');
readln(Res);
until (Res > 0);
OPHALEN := Res;
end;
Note that the (bad) global variables you're using mean you don't have to return anything at all, because a procedure can access and change that global variable directly (but you have no way of knowing when the procedure is finished):
procedure OPHALEN(para : string);
begin
vari := 0;
repeat
writeln('geef de ', para , ' op');
readln(vari);
until (vari > 0);
end;
Modern Pascal dialects (such as Delphi and FreePascal) allow a cleaner syntax for the return value of functions by using an automatically declared function result variable of the proper type for you, named Result (because that's what it is - the result of the function):
function OPHALEN(para : string): Real;
begin
Result := 0;
repeat
writeln('geef de ', para , ' op');
readln(Result);
until (Result > 0);
end;
If you need to return multiple values, you can use var parameters, which allow them to be changed inside the function.
procedure OPHALEN(para: string; var RetVal: Real);
begin
RetVal := 0;
repeat
writeln('geef de ', para , ' op');
readln(RetVal);
until (RetVal > 0);
end;
Your original code (and the examples I've provided above) all fail to allow the user to cancel, BTW. There should be some way to exit the loop for the user; otherwise, your code just endlessly loops, writing para to the screen and then waiting for input. This has a tendency to annoy users.

Issue implementing an OSX CoreMidi MidiCallback function in Lazarus/FreePascal

I'am struggling implementing an OSX CoreMidi MidiCallback procedure with Lazarus / FreePascal.
In the MIDIServices unit, MIDIReadProc, the callback routine, is definded:
MIDIReadProc = procedure( (*const*) pktlist: MIDIPacketListPtr; readProcRefCon: UnivPtr; srcConnRefCon: UnivPtr );
This routine is called on a separate high-priority thread owned by CoreMidi when midi events are received.
I defined a callback procedure for handling received midi events:
Type procedure MyMidiCallback(pktList: MIDIPacketListPtr;readProcRefCon: UnivPtr; srcConnRefCon: UnivPtrMy);
procedure TMainForm.MyMidiCallback(pktList: MIDIPacketListPtr;readProcRefCon: UnivPtr; srcConnRefCon: UnivPtr);
begin
// handle midi packets
end;
The midi callback hook is defined in the following code at 'MidiInputPortCreate':
procedure TMainForm.ReceiveMidiTestClick(Sender: TObject);
var
NumOfSources, NumOfDestinations: ItemCount;
x: byte;
MIDIDestinationPointer, MidiSourcePointer: MIDIEndpointRef;
EndPointName: CFStringRef;
MidiClient: MidiClientRef;
InputPort: MidiPortRef;
MidiCallback: MidiReadProc;
begin
NumOfDestinations := MIDIGetNumberOfDestinations;
NumOfSources := MIDIGetNumberOfSources;
Memo.Lines.Add('Number of Midi Sources: ' + IntToStr(NumOfSources));
EndPointName := nil;
MidiClient := nil;
InputPort := nil;
MidiCallback := #TMainform.MyMidiCallback;
for x := 0 to NumOfDestinations -1 do // show destinations
begin
MidiDestinationPointer := MidiGetDestination(x);
MIDIObjectGetStringProperty(MidiDestinationPointer, kMIDIPropertyName, EndPointName);
Memo.Lines.Add('Destination ' + IntToStr(x) + ': ' + CFStrToAnsiStr(EndPointName));
end;
for x := 0 to NumOfSources -1 do // show sources
begin
MidiSourcePointer := MIDIGetSource(x);
MIDIObjectGetStringProperty(MidiSourcePointer, kMIDIPropertyName, EndPointName);
Memo.Lines.Add('Source ' + IntToStr(x) + ': ' + CFStrToAnsiStr(EndPointName));
end;
MidiClientCreate(CFSTRP('Midi Input Client'), nil, nil, MidiClient);
MidiInputPortCreate(MidiClient, CFSTRP('Input'), MidiCallback, nil, InputPort); // MidiCallback
MIDISourcePointer := MIDIGetSource(0); // select source(0) = midi keyboard
MidiPortConnectSource(InputPort, MIDISourcePointer, nil);
end;
Compiling generates the following error message:
mainunit.pas(480,19) Error: Incompatible types: got "<procedure variable type of procedure(MIDIPacketListPtr,Pointer,Pointer) of object;Register>" expected "<procedure variable type of procedure(MIDIPacketListPtr,Pointer,Pointer);MWPascal>"
I'am stuck here now; hope someone can help.
--------------------------------- UPDATE #1 ----------------------------------
The code above was indeed a bit strange so I rewrote things:
procedure TMainForm.ReceiveMidiTestClick(Sender: TObject);
var
MidiClient: MidiClientRef;
InputPort: MidiPortRef;
MidiCallback: MIDIReadProc;
begin
MidiCallback := MyMidiCallback;
MidiClientCreate(CFSTRP('Midi Input Client'), nil, nil, MidiClient);
MidiInputPortCreate(MidiClient, CFSTRP('Input'), MidiCallback, nil, InputPort);
MidiPortConnectSource(InputPort, MIDIGetSource(0), nil);
end;
procedure MyMidiCallback(pktList: MIDIPacketListPtr; readProcRefCon: UnivPtr; srcConnRefCon: UnivPtr);
begin
// handle midi packets
end;
Now the code compiles without errors but as soon as i hit a key on the midi keyboard, the application crashes with the following error message:
'ERROR Project ... raised exception class 'External: Sigtrap' at address FFFFD96F'
(FFFFD96F is probably the pointer to the MidiCallback routine).
Basically, the issue I have is how to let the MidiCallback pointer in MidiInputPortCreate point correctly to my MyMidiCallback procedure where I handle midi events.
BTW, sending Midi events works fine.
Let's put the error declarations in the error on separate lines:
mainunit.pas(480,19) Error: Incompatible types: got "
procedure(MIDIPacketListPtr,Pointer,Pointer) of object;Register>" expected "<
procedure variable type of procedure(MIDIPacketListPtr,Pointer,Pointer);MWPascal>"
Note two crucial differences:
1) The "of object" difference in the two procedure declarations in the error means you passed a method instead of a proper procedure.
2) Besides that, calling conventions doesn't seem to match, one is mwpascal; one is Register. Register is the default for most modes, so no calling convention modifier means register.
The "callback" part of your question is strange. You define a type as a procedure, but provide a method as implementation?

Resources